pulseaudio integration
This commit is contained in:
parent
eb570e90fd
commit
85c2e2a842
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void paper_init(char* monitor, char* frag_path, uint16_t fps, char* layer_name, uint16_t width, uint16_t height);
|
void paper_init(char *monitor, char *frag_path, char *pa_src, uint16_t fps,
|
||||||
|
char *layer_name, uint16_t width, uint16_t height);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
72
inc/pulsefft.h
Normal file
72
inc/pulsefft.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#ifndef _PULSEFFT_H
|
||||||
|
#define _PULSEFFT_H
|
||||||
|
|
||||||
|
#include <complex.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <tgmath.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <pulse/error.h>
|
||||||
|
#include <pulse/pulseaudio.h>
|
||||||
|
#include <pulse/simple.h>
|
||||||
|
|
||||||
|
#include <fftw3.h>
|
||||||
|
|
||||||
|
typedef struct pa_fft {
|
||||||
|
pthread_t thread;
|
||||||
|
int cont;
|
||||||
|
|
||||||
|
// Pulseaudio
|
||||||
|
pa_simple *s;
|
||||||
|
char *dev;
|
||||||
|
unsigned int rate;
|
||||||
|
int error;
|
||||||
|
pa_sample_spec ss;
|
||||||
|
pa_channel_map map;
|
||||||
|
|
||||||
|
// Buffers
|
||||||
|
unsigned int samples;
|
||||||
|
|
||||||
|
double *pa_buff_l;
|
||||||
|
double *pa_buff_r;
|
||||||
|
size_t pa_buff_size;
|
||||||
|
unsigned int pa_samples;
|
||||||
|
|
||||||
|
double *fft_buff;
|
||||||
|
double *fft_buff_l;
|
||||||
|
double *fft_buff_r;
|
||||||
|
size_t fft_buff_size;
|
||||||
|
unsigned int fft_samples;
|
||||||
|
|
||||||
|
// Output buffers
|
||||||
|
float *pa_output;
|
||||||
|
size_t pa_output_size;
|
||||||
|
|
||||||
|
float *fft_output;
|
||||||
|
size_t fft_output_size;
|
||||||
|
|
||||||
|
// FFTW
|
||||||
|
fftw_complex *output_l;
|
||||||
|
fftw_complex *output_r;
|
||||||
|
size_t output_size;
|
||||||
|
unsigned int output_samples;
|
||||||
|
fftw_plan plan_l;
|
||||||
|
fftw_plan plan_r;
|
||||||
|
} pa_fft;
|
||||||
|
|
||||||
|
void separate_freq_bands(double *out, fftw_complex *in, int n, int *lcf,
|
||||||
|
int *hcf, float *k, double sensitivity,
|
||||||
|
int in_samples);
|
||||||
|
void *pa_fft_thread(void *arg);
|
||||||
|
void get_pulse_default_sink(pa_fft *pa_fft);
|
||||||
|
void init_pulse(pa_fft *pa_fft);
|
||||||
|
void init_buffers(pa_fft *pa_fft);
|
||||||
|
void init_fft(pa_fft *pa_fft);
|
||||||
|
void deinit_fft(pa_fft *pa_fft);
|
||||||
|
|
||||||
|
#endif // _PULSEFFT_H
|
||||||
@ -5,14 +5,18 @@ inc = include_directories('inc')
|
|||||||
wayland = dependency('wayland-client')
|
wayland = dependency('wayland-client')
|
||||||
wl_egl = dependency('wayland-egl')
|
wl_egl = dependency('wayland-egl')
|
||||||
egl = dependency('egl')
|
egl = dependency('egl')
|
||||||
|
pulse = dependency('libpulse-simple')
|
||||||
|
math = cc.find_library('m', required : false)
|
||||||
|
fftw = dependency('fftw3')
|
||||||
|
|
||||||
executable(meson.project_name(),
|
executable(meson.project_name(),
|
||||||
'src/main.c',
|
'src/main.c',
|
||||||
'src/paper.c',
|
'src/paper.c',
|
||||||
|
'src/pulsefft.c',
|
||||||
'src/utils.c',
|
'src/utils.c',
|
||||||
'glad/glad.c',
|
'glad/glad.c',
|
||||||
'proto/wlr-layer-shell-unstable-v1-protocol.c',
|
'proto/wlr-layer-shell-unstable-v1-protocol.c',
|
||||||
'proto/xdg-output-unstable-v1-protocol.c',
|
'proto/xdg-output-unstable-v1-protocol.c',
|
||||||
'proto/xdg-shell-protocol.c',
|
'proto/xdg-shell-protocol.c',
|
||||||
include_directories : inc,
|
include_directories : inc,
|
||||||
dependencies : [wayland, wl_egl, egl], install : true)
|
dependencies : [math, wayland, wl_egl, egl, pulse, fftw], install : true)
|
||||||
|
|||||||
71
src/main.c
71
src/main.c
@ -15,12 +15,12 @@
|
|||||||
along with GLPaper. If not, see <http://www.gnu.org/licenses/>.
|
along with GLPaper. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <string.h>
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <paper.h>
|
#include <paper.h>
|
||||||
|
|
||||||
@ -38,63 +38,44 @@ static void print_usage(char** argv) {
|
|||||||
printf("--fork\t\t-F\tForks glpaper so you can close the terminal\n");
|
printf("--fork\t\t-F\tForks glpaper so you can close the terminal\n");
|
||||||
printf("--fps\t\t-f\tSets the FPS to render at\n");
|
printf("--fps\t\t-f\tSets the FPS to render at\n");
|
||||||
printf("--layer\t\t-l\tSpecifies layer to run on\n");
|
printf("--layer\t\t-l\tSpecifies layer to run on\n");
|
||||||
printf("--width\t\t-W\tThe width to render at, this does not affect display resolution\n");
|
printf("--width\t\t-W\tThe width to render at, this does not affect display "
|
||||||
printf("--height\t-H\tThe height to render at, this does not affect display resolution\n");
|
"resolution\n");
|
||||||
|
printf("--height\t-H\tThe height to render at, this does not affect display "
|
||||||
|
"resolution\n");
|
||||||
|
printf("--source\t-s\tPulseaudio source\n");
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
if (argc > 2) {
|
if (argc > 2) {
|
||||||
struct option opts[] = {
|
struct option opts[] = {
|
||||||
{
|
{.name = "help", .has_arg = no_argument, .flag = NULL, .val = 'h'},
|
||||||
.name = "help",
|
{.name = "fork", .has_arg = no_argument, .flag = NULL, .val = 'F'},
|
||||||
.has_arg = no_argument,
|
{.name = "fps", .has_arg = required_argument, .flag = NULL, .val = 'f'},
|
||||||
.flag = NULL,
|
{.name = "layer",
|
||||||
.val = 'h'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "fork",
|
|
||||||
.has_arg = no_argument,
|
|
||||||
.flag = NULL,
|
|
||||||
.val = 'F'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "fps",
|
|
||||||
.has_arg = required_argument,
|
.has_arg = required_argument,
|
||||||
.flag = NULL,
|
.flag = NULL,
|
||||||
.val = 'f'
|
.val = 'l'},
|
||||||
},
|
{.name = "width",
|
||||||
{
|
|
||||||
.name = "layer",
|
|
||||||
.has_arg = required_argument,
|
.has_arg = required_argument,
|
||||||
.flag = NULL,
|
.flag = NULL,
|
||||||
.val = 'l'
|
.val = 'W'},
|
||||||
},
|
{.name = "height",
|
||||||
{
|
|
||||||
.name = "width",
|
|
||||||
.has_arg = required_argument,
|
.has_arg = required_argument,
|
||||||
.flag = NULL,
|
.flag = NULL,
|
||||||
.val = 'W'
|
.val = 'H'},
|
||||||
},
|
{.name = "source",
|
||||||
{
|
|
||||||
.name = "height",
|
|
||||||
.has_arg = required_argument,
|
.has_arg = required_argument,
|
||||||
.flag = NULL,
|
.flag = NULL,
|
||||||
.val = 'H'
|
.val = 's'},
|
||||||
},
|
{.name = NULL, .has_arg = 0, .flag = NULL, .val = 0}};
|
||||||
{
|
|
||||||
.name = NULL,
|
|
||||||
.has_arg = 0,
|
|
||||||
.flag = NULL,
|
|
||||||
.val = 0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
char *fps_str = NULL;
|
char *fps_str = NULL;
|
||||||
char *layer = NULL;
|
char *layer = NULL;
|
||||||
char *width_str = NULL;
|
char *width_str = NULL;
|
||||||
char *height_str = NULL;
|
char *height_str = NULL;
|
||||||
|
char *source_str = NULL;
|
||||||
char opt;
|
char opt;
|
||||||
while((opt = getopt_long(argc, argv, "hFf:l:W:H:", opts, NULL)) != -1) {
|
while ((opt = getopt_long(argc, argv, "hFf:l:W:H:s:", opts, NULL)) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'h':
|
case 'h':
|
||||||
print_usage(argv);
|
print_usage(argv);
|
||||||
@ -119,6 +100,9 @@ int main(int argc, char** argv) {
|
|||||||
case 'H':
|
case 'H':
|
||||||
height_str = optarg;
|
height_str = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
source_str = strdup(optarg);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint16_t fps;
|
uint16_t fps;
|
||||||
@ -146,7 +130,8 @@ int main(int argc, char** argv) {
|
|||||||
print_usage(argv);
|
print_usage(argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
paper_init(argv[optind], argv[optind + 1], fps, layer, width, height);
|
paper_init(argv[optind], argv[optind + 1], source_str, fps, layer, width,
|
||||||
|
height);
|
||||||
} else {
|
} else {
|
||||||
print_usage(argv);
|
print_usage(argv);
|
||||||
}
|
}
|
||||||
|
|||||||
178
src/paper.c
178
src/paper.c
@ -17,21 +17,22 @@
|
|||||||
|
|
||||||
#include <paper.h>
|
#include <paper.h>
|
||||||
|
|
||||||
#include <time.h>
|
#include <pulsefft.h>
|
||||||
#include <utils.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <utils.h>
|
||||||
|
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
#include <glad/glad_egl.h>
|
#include <glad/glad_egl.h>
|
||||||
|
|
||||||
#include <wayland-egl.h>
|
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <xdg-output-unstable-v1-client-protocol.h>
|
#include <wayland-egl.h>
|
||||||
#include <wlr-layer-shell-unstable-v1-client-protocol.h>
|
#include <wlr-layer-shell-unstable-v1-client-protocol.h>
|
||||||
|
#include <xdg-output-unstable-v1-client-protocol.h>
|
||||||
|
|
||||||
#define PROTO_VERSION(v1, v2) (v1 < v2 ? v1 : v2)
|
#define PROTO_VERSION(v1, v2) (v1 < v2 ? v1 : v2)
|
||||||
|
|
||||||
@ -42,6 +43,7 @@ static struct wl_compositor* comp;
|
|||||||
static struct zwlr_layer_shell_v1 *shell;
|
static struct zwlr_layer_shell_v1 *shell;
|
||||||
static struct zxdg_output_manager_v1 *output_manager;
|
static struct zxdg_output_manager_v1 *output_manager;
|
||||||
static time_t start;
|
static time_t start;
|
||||||
|
static struct pa_fft *fft;
|
||||||
|
|
||||||
struct node {
|
struct node {
|
||||||
struct wl_output *output;
|
struct wl_output *output;
|
||||||
@ -51,22 +53,30 @@ struct node {
|
|||||||
|
|
||||||
static void nop() {}
|
static void nop() {}
|
||||||
|
|
||||||
static void add_interface(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
|
static void add_interface(void *data, struct wl_registry *registry,
|
||||||
|
uint32_t name, const char *interface,
|
||||||
|
uint32_t version) {
|
||||||
(void)data;
|
(void)data;
|
||||||
if (strcmp(interface, wl_output_interface.name) == 0) {
|
if (strcmp(interface, wl_output_interface.name) == 0) {
|
||||||
struct node *node = malloc(sizeof(struct node));
|
struct node *node = malloc(sizeof(struct node));
|
||||||
node->output = wl_registry_bind(registry, name, &wl_output_interface, PROTO_VERSION(version, 4));
|
node->output = wl_registry_bind(registry, name, &wl_output_interface,
|
||||||
|
PROTO_VERSION(version, 4));
|
||||||
wl_list_insert(&outputs, &node->link);
|
wl_list_insert(&outputs, &node->link);
|
||||||
} else if (strcmp(interface, wl_compositor_interface.name) == 0) {
|
} else if (strcmp(interface, wl_compositor_interface.name) == 0) {
|
||||||
comp = wl_registry_bind(registry, name, &wl_compositor_interface, PROTO_VERSION(version, 4));
|
comp = wl_registry_bind(registry, name, &wl_compositor_interface,
|
||||||
|
PROTO_VERSION(version, 4));
|
||||||
} else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
|
} else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
|
||||||
output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, PROTO_VERSION(version, 3));
|
output_manager =
|
||||||
|
wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface,
|
||||||
|
PROTO_VERSION(version, 3));
|
||||||
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
||||||
shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, PROTO_VERSION(version, 4));
|
shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface,
|
||||||
|
PROTO_VERSION(version, 4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_name(void* data, struct zxdg_output_v1* xdg_output, const char* name) {
|
static void get_name(void *data, struct zxdg_output_v1 *xdg_output,
|
||||||
|
const char *name) {
|
||||||
(void)xdg_output;
|
(void)xdg_output;
|
||||||
struct node *node = data;
|
struct node *node = data;
|
||||||
if (strcmp(name, monitor) == 0) {
|
if (strcmp(name, monitor) == 0) {
|
||||||
@ -74,14 +84,16 @@ static void get_name(void* data, struct zxdg_output_v1* xdg_output, const char*
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void config_surface(void* data, struct zwlr_layer_surface_v1* surface, uint32_t serial, uint32_t width, uint32_t height) {
|
static void config_surface(void *data, struct zwlr_layer_surface_v1 *surface,
|
||||||
|
uint32_t serial, uint32_t width, uint32_t height) {
|
||||||
(void)data;
|
(void)data;
|
||||||
(void)width;
|
(void)width;
|
||||||
(void)height;
|
(void)height;
|
||||||
zwlr_layer_surface_v1_ack_configure(surface, serial);
|
zwlr_layer_surface_v1_ack_configure(surface, serial);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_res(void* data, struct wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
|
static void get_res(void *data, struct wl_output *output, uint32_t flags,
|
||||||
|
int32_t width, int32_t height, int32_t refresh) {
|
||||||
(void)output;
|
(void)output;
|
||||||
(void)refresh;
|
(void)refresh;
|
||||||
if ((flags & WL_OUTPUT_MODE_CURRENT) == WL_OUTPUT_MODE_CURRENT) {
|
if ((flags & WL_OUTPUT_MODE_CURRENT) == WL_OUTPUT_MODE_CURRENT) {
|
||||||
@ -91,17 +103,16 @@ static void get_res(void* data, struct wl_output* output, uint32_t flags, int32_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setup_fbo(GLuint* fbo, GLuint* prog, GLuint* texture, GLuint vert, uint16_t width, uint16_t height) {
|
static void setup_fbo(GLuint *fbo, GLuint *prog, GLuint *texture, GLuint vert,
|
||||||
const char* frag_data[] = {
|
uint16_t width, uint16_t height) {
|
||||||
"#version 100\n"
|
const char *frag_data[] = {"#version 100\n"
|
||||||
"uniform sampler2D tex2D;"
|
"uniform sampler2D tex2D;"
|
||||||
|
|
||||||
"varying highp vec2 texCoords;"
|
"varying highp vec2 texCoords;"
|
||||||
|
|
||||||
"void main() {"
|
"void main() {"
|
||||||
" gl_FragColor = texture2D(tex2D, texCoords);"
|
" gl_FragColor = texture2D(tex2D, texCoords);"
|
||||||
"}"
|
"}"};
|
||||||
};
|
|
||||||
|
|
||||||
*prog = glCreateProgram();
|
*prog = glCreateProgram();
|
||||||
GLuint frag = glCreateShader(GL_FRAGMENT_SHADER);
|
GLuint frag = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
@ -142,31 +153,36 @@ static void setup_fbo(GLuint* fbo, GLuint* prog, GLuint* texture, GLuint vert, u
|
|||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
|
||||||
|
GL_UNSIGNED_BYTE, NULL);
|
||||||
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *texture, 0);
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||||
|
*texture, 0);
|
||||||
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setup_vars(GLuint prog) {
|
||||||
|
glUseProgram(prog);
|
||||||
|
GLint time_var = glGetUniformLocation(prog, "time");
|
||||||
|
glUniform1f(time_var, (utils_get_time_millis() - start) / 1000.0f);
|
||||||
|
GLint resolution = glGetUniformLocation(prog, "resolution");
|
||||||
|
glUniform2f(resolution, output->width, output->height);
|
||||||
|
}
|
||||||
|
|
||||||
static void draw(GLuint prog) {
|
static void draw(GLuint prog) {
|
||||||
glUseProgram(prog);
|
glUseProgram(prog);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
GLint time_var = glGetUniformLocation(prog, "time");
|
setup_vars(prog);
|
||||||
glUniform1f(time_var, (utils_get_time_millis() - start) / 1000.0f);
|
|
||||||
GLint resolution = glGetUniformLocation(prog, "resolution");
|
|
||||||
glUniform2f(resolution, output->width, output->height);
|
|
||||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw_fbo(GLuint fbo, GLuint texture, GLuint main_prog, GLuint final_prog, uint16_t width, uint16_t height) {
|
static void draw_fbo(GLuint fbo, GLuint texture, GLuint main_prog,
|
||||||
|
GLuint final_prog, uint16_t width, uint16_t height) {
|
||||||
glViewport(0, 0, width, height);
|
glViewport(0, 0, width, height);
|
||||||
glUseProgram(main_prog);
|
glUseProgram(main_prog);
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
GLint time_var = glGetUniformLocation(main_prog, "time");
|
setup_vars(main_prog);
|
||||||
glUniform1f(time_var, (utils_get_time_millis() - start) / 1000.0f);
|
|
||||||
GLint resolution = glGetUniformLocation(main_prog, "resolution");
|
|
||||||
glUniform2f(resolution, width, height);
|
|
||||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
||||||
|
|
||||||
glViewport(0, 0, output->width, output->height);
|
glViewport(0, 0, output->width, output->height);
|
||||||
@ -178,51 +194,56 @@ static void draw_fbo(GLuint fbo, GLuint texture, GLuint main_prog, GLuint final_
|
|||||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void paper_init(char* _monitor, char* frag_path, uint16_t fps, char* layer_name, uint16_t width, uint16_t height) {
|
void paper_init(char *_monitor, char *frag_path, char *pa_src, uint16_t fps,
|
||||||
|
char *layer_name, uint16_t width, uint16_t height) {
|
||||||
monitor = _monitor;
|
monitor = _monitor;
|
||||||
start = utils_get_time_millis();
|
start = utils_get_time_millis();
|
||||||
wl_list_init(&outputs);
|
wl_list_init(&outputs);
|
||||||
struct wl_display *wl = wl_display_connect(NULL);
|
struct wl_display *wl = wl_display_connect(NULL);
|
||||||
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(wl);
|
struct wl_registry *registry = wl_display_get_registry(wl);
|
||||||
struct wl_registry_listener reg_listener = {
|
struct wl_registry_listener reg_listener = {.global = add_interface,
|
||||||
.global = add_interface,
|
.global_remove = nop};
|
||||||
.global_remove = nop
|
|
||||||
};
|
|
||||||
wl_registry_add_listener(registry, ®_listener, NULL);
|
wl_registry_add_listener(registry, ®_listener, NULL);
|
||||||
wl_display_roundtrip(wl);
|
wl_display_roundtrip(wl);
|
||||||
|
|
||||||
|
|
||||||
struct node *node;
|
struct node *node;
|
||||||
wl_list_for_each(node, &outputs, link) {
|
wl_list_for_each(node, &outputs, link) {
|
||||||
struct zxdg_output_v1* xdg_output = zxdg_output_manager_v1_get_xdg_output(output_manager, node->output);
|
struct zxdg_output_v1 *xdg_output =
|
||||||
struct zxdg_output_v1_listener xdg_listener = {
|
zxdg_output_manager_v1_get_xdg_output(output_manager, node->output);
|
||||||
.description = nop,
|
struct zxdg_output_v1_listener xdg_listener = {.description = nop,
|
||||||
.done = nop,
|
.done = nop,
|
||||||
.logical_position = nop,
|
.logical_position = nop,
|
||||||
.logical_size = nop,
|
.logical_size = nop,
|
||||||
.name = get_name
|
.name = get_name};
|
||||||
};
|
|
||||||
zxdg_output_v1_add_listener(xdg_output, &xdg_listener, node);
|
zxdg_output_v1_add_listener(xdg_output, &xdg_listener, node);
|
||||||
|
|
||||||
struct wl_output_listener out_listener = {
|
struct wl_output_listener out_listener = {.done = nop,
|
||||||
.done = nop,
|
|
||||||
.geometry = nop,
|
.geometry = nop,
|
||||||
.mode = get_res,
|
.mode = get_res,
|
||||||
.scale = nop,
|
.scale = nop,
|
||||||
.name = nop,
|
.name = nop,
|
||||||
.description = nop
|
.description = nop};
|
||||||
};
|
|
||||||
wl_output_add_listener(node->output, &out_listener, node);
|
wl_output_add_listener(node->output, &out_listener, node);
|
||||||
}
|
}
|
||||||
wl_display_roundtrip(wl);
|
wl_display_roundtrip(wl);
|
||||||
|
|
||||||
if (output == NULL) {
|
if (output == NULL) {
|
||||||
fprintf(stderr, ":/ sorry about this but we can't seem to find that output\n");
|
fprintf(stderr,
|
||||||
|
":/ sorry about this but we can't seem to find that output\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pulseaudio fft
|
||||||
|
fft = calloc(1, sizeof(struct pa_fft));
|
||||||
|
fft->cont = 1;
|
||||||
|
fft->samples = 2048;
|
||||||
|
fft->dev = pa_src;
|
||||||
|
fft->rate = 44100;
|
||||||
|
|
||||||
|
init_pulse(fft);
|
||||||
|
init_buffers(fft);
|
||||||
|
init_fft(fft);
|
||||||
|
|
||||||
struct wl_surface *wl_surface = wl_compositor_create_surface(comp);
|
struct wl_surface *wl_surface = wl_compositor_create_surface(comp);
|
||||||
struct wl_region *input_region = wl_compositor_create_region(comp);
|
struct wl_region *input_region = wl_compositor_create_region(comp);
|
||||||
@ -247,41 +268,44 @@ void paper_init(char* _monitor, char* frag_path, uint16_t fps, char* layer_name,
|
|||||||
} else {
|
} else {
|
||||||
layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND;
|
layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND;
|
||||||
}
|
}
|
||||||
struct zwlr_layer_surface_v1* surface = zwlr_layer_shell_v1_get_layer_surface(shell, wl_surface, output->output, layer, "glpaper");
|
struct zwlr_layer_surface_v1 *surface = zwlr_layer_shell_v1_get_layer_surface(
|
||||||
|
shell, wl_surface, output->output, layer, "glpaper");
|
||||||
zwlr_layer_surface_v1_set_exclusive_zone(surface, -1);
|
zwlr_layer_surface_v1_set_exclusive_zone(surface, -1);
|
||||||
zwlr_layer_surface_v1_set_size(surface, output->width, output->height);
|
zwlr_layer_surface_v1_set_size(surface, output->width, output->height);
|
||||||
struct zwlr_layer_surface_v1_listener surface_listener = {
|
struct zwlr_layer_surface_v1_listener surface_listener = {
|
||||||
.closed = nop,
|
.closed = nop, .configure = config_surface};
|
||||||
.configure = config_surface
|
|
||||||
};
|
|
||||||
zwlr_layer_surface_v1_add_listener(surface, &surface_listener, NULL);
|
zwlr_layer_surface_v1_add_listener(surface, &surface_listener, NULL);
|
||||||
wl_surface_commit(wl_surface);
|
wl_surface_commit(wl_surface);
|
||||||
wl_display_roundtrip(wl);
|
wl_display_roundtrip(wl);
|
||||||
|
|
||||||
struct wl_egl_window* window = wl_egl_window_create(wl_surface, output->width, output->height);
|
struct wl_egl_window *window =
|
||||||
|
wl_egl_window_create(wl_surface, output->width, output->height);
|
||||||
eglBindAPI(EGL_OPENGL_ES_API);
|
eglBindAPI(EGL_OPENGL_ES_API);
|
||||||
EGLDisplay egl_display = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, wl, NULL);
|
EGLDisplay egl_display =
|
||||||
|
eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, wl, NULL);
|
||||||
eglInitialize(egl_display, NULL, NULL);
|
eglInitialize(egl_display, NULL, NULL);
|
||||||
const EGLint win_attrib[] = {
|
const EGLint win_attrib[] = {EGL_SURFACE_TYPE,
|
||||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
EGL_WINDOW_BIT,
|
||||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
EGL_RENDERABLE_TYPE,
|
||||||
EGL_RED_SIZE, 8,
|
EGL_OPENGL_ES2_BIT,
|
||||||
EGL_GREEN_SIZE, 8,
|
EGL_RED_SIZE,
|
||||||
EGL_BLUE_SIZE, 8,
|
8,
|
||||||
EGL_NONE
|
EGL_GREEN_SIZE,
|
||||||
};
|
8,
|
||||||
|
EGL_BLUE_SIZE,
|
||||||
|
8,
|
||||||
|
EGL_NONE};
|
||||||
|
|
||||||
EGLConfig config;
|
EGLConfig config;
|
||||||
EGLint config_len;
|
EGLint config_len;
|
||||||
eglChooseConfig(egl_display, win_attrib, &config, 1, &config_len);
|
eglChooseConfig(egl_display, win_attrib, &config, 1, &config_len);
|
||||||
const EGLint ctx_attrib[] = {
|
const EGLint ctx_attrib[] = {EGL_CONTEXT_MAJOR_VERSION, 2,
|
||||||
EGL_CONTEXT_MAJOR_VERSION, 2,
|
EGL_CONTEXT_MINOR_VERSION, 0, EGL_NONE};
|
||||||
EGL_CONTEXT_MINOR_VERSION, 0,
|
EGLContext ctx =
|
||||||
EGL_NONE
|
eglCreateContext(egl_display, config, EGL_NO_CONTEXT, ctx_attrib);
|
||||||
};
|
|
||||||
EGLContext ctx = eglCreateContext(egl_display, config, EGL_NO_CONTEXT, ctx_attrib);
|
|
||||||
|
|
||||||
EGLSurface egl_surface = eglCreatePlatformWindowSurface(egl_display, config, window, NULL);
|
EGLSurface egl_surface =
|
||||||
|
eglCreatePlatformWindowSurface(egl_display, config, window, NULL);
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, ctx);
|
eglMakeCurrent(egl_display, egl_surface, egl_surface, ctx);
|
||||||
if (fps == 0) {
|
if (fps == 0) {
|
||||||
eglSwapInterval(egl_display, 1);
|
eglSwapInterval(egl_display, 1);
|
||||||
@ -305,23 +329,21 @@ void paper_init(char* _monitor, char* frag_path, uint16_t fps, char* layer_name,
|
|||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vbo_data), vbo_data, GL_STATIC_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vbo_data), vbo_data, GL_STATIC_DRAW);
|
||||||
|
|
||||||
GLuint ebo_data[] = {
|
GLuint ebo_data[] = {0, 1, 2, 2, 3, 0};
|
||||||
0, 1, 2,
|
|
||||||
2, 3, 0
|
|
||||||
};
|
|
||||||
|
|
||||||
GLuint ebo;
|
GLuint ebo;
|
||||||
glGenBuffers(1, &ebo);
|
glGenBuffers(1, &ebo);
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ebo_data), ebo_data, GL_STATIC_DRAW);
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ebo_data), ebo_data,
|
||||||
|
GL_STATIC_DRAW);
|
||||||
|
|
||||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
|
||||||
glEnableVertexAttribArray(0);
|
glEnableVertexAttribArray(0);
|
||||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*) (2 * sizeof(GLfloat)));
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat),
|
||||||
|
(void *)(2 * sizeof(GLfloat)));
|
||||||
glEnableVertexAttribArray(1);
|
glEnableVertexAttribArray(1);
|
||||||
|
|
||||||
const char* vert_data[] = {
|
const char *vert_data[] = {"#version 100\n"
|
||||||
"#version 100\n"
|
|
||||||
"attribute highp vec2 datIn;"
|
"attribute highp vec2 datIn;"
|
||||||
"attribute highp vec2 texIn;"
|
"attribute highp vec2 texIn;"
|
||||||
|
|
||||||
@ -330,8 +352,7 @@ void paper_init(char* _monitor, char* frag_path, uint16_t fps, char* layer_name,
|
|||||||
"void main() {"
|
"void main() {"
|
||||||
" texCoords = texIn;"
|
" texCoords = texIn;"
|
||||||
" gl_Position = vec4(datIn, 0.0, 1.0);"
|
" gl_Position = vec4(datIn, 0.0, 1.0);"
|
||||||
"}"
|
"}"};
|
||||||
};
|
|
||||||
|
|
||||||
GLuint shader_prog = glCreateProgram();
|
GLuint shader_prog = glCreateProgram();
|
||||||
GLuint vert = glCreateShader(GL_VERTEX_SHADER);
|
GLuint vert = glCreateShader(GL_VERTEX_SHADER);
|
||||||
@ -387,7 +408,6 @@ void paper_init(char* _monitor, char* frag_path, uint16_t fps, char* layer_name,
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool use_fbo = width > 0 || height > 0;
|
bool use_fbo = width > 0 || height > 0;
|
||||||
GLuint fbo = 0;
|
GLuint fbo = 0;
|
||||||
GLuint final_prog = 0;
|
GLuint final_prog = 0;
|
||||||
|
|||||||
349
src/pulsefft.c
Normal file
349
src/pulsefft.c
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
#include "pulsefft.h"
|
||||||
|
|
||||||
|
// TODO: Add this vars to config
|
||||||
|
const int smoothing_prec = 100;
|
||||||
|
float smooth[] = {1.0, 1.0, 1.0, 1.0, 1.0};
|
||||||
|
float gravity = 0.0006;
|
||||||
|
int highf = 18000, lowf = 20;
|
||||||
|
float logScale = 1.0;
|
||||||
|
double sensitivity = 1.0;
|
||||||
|
|
||||||
|
void separate_freq_bands(double *out, fftw_complex *in, int n, int *lcf,
|
||||||
|
int *hcf, float *k, double sensitivity,
|
||||||
|
int in_samples) {
|
||||||
|
double peak[n];
|
||||||
|
double y[in_samples];
|
||||||
|
double temp;
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
peak[i] = 0;
|
||||||
|
|
||||||
|
for (int j = lcf[i]; j <= hcf[i]; j++) {
|
||||||
|
y[j] = sqrt(creal(in[j]) * creal(in[j]) + cimag(in[j]) * cimag(in[j]));
|
||||||
|
peak[i] += y[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
peak[i] = peak[i] / (hcf[i] - lcf[i] + 1);
|
||||||
|
temp = peak[i] * sensitivity * k[i] / 1000000;
|
||||||
|
out[i] = temp / 100.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t audio_out_l[65536];
|
||||||
|
int16_t audio_out_r[65536];
|
||||||
|
|
||||||
|
void *pa_fft_thread(void *arg) {
|
||||||
|
pa_fft *t = (struct pa_fft *)arg;
|
||||||
|
|
||||||
|
float smoothing[smoothing_prec];
|
||||||
|
double freqconst = log(highf - lowf) / log(pow(smoothing_prec, logScale));
|
||||||
|
|
||||||
|
int fall[smoothing_prec];
|
||||||
|
float fpeak[smoothing_prec], flast[smoothing_prec], fmem[smoothing_prec];
|
||||||
|
|
||||||
|
float fc[smoothing_prec], x;
|
||||||
|
int lcf[smoothing_prec], hcf[smoothing_prec];
|
||||||
|
|
||||||
|
// Calc freq range for each bar
|
||||||
|
for (int i = 0; i < smoothing_prec; i++) {
|
||||||
|
fc[i] = pow(powf(i, (logScale - 1.0) * ((double)i + 1.0) /
|
||||||
|
((double)smoothing_prec) +
|
||||||
|
1.0),
|
||||||
|
freqconst) +
|
||||||
|
lowf;
|
||||||
|
x = fc[i] / (t->rate / 2);
|
||||||
|
lcf[i] = x * (t->samples / 2);
|
||||||
|
if (i != 0)
|
||||||
|
hcf[i - 1] = lcf[i] - 1 > lcf[i - 1] ? lcf[i] - 1 : lcf[i - 1];
|
||||||
|
}
|
||||||
|
hcf[smoothing_prec - 1] = highf * t->samples / t->rate;
|
||||||
|
|
||||||
|
// Calc smoothing
|
||||||
|
for (int i = 0; i < smoothing_prec; i++) {
|
||||||
|
smoothing[i] = pow(fc[i], 0.64); // TODO: Add smoothing factor to config
|
||||||
|
smoothing[i] *=
|
||||||
|
smooth[(int)(i / smoothing_prec * sizeof(smooth) / sizeof(*smooth))];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int n = 0;
|
||||||
|
int16_t buf[t->samples];
|
||||||
|
|
||||||
|
// Clear arrays
|
||||||
|
for (int i = 0; i < smoothing_prec; i++)
|
||||||
|
buf[i] = fall[i] = fpeak[i] = flast[i] = fmem[i] = 0;
|
||||||
|
|
||||||
|
while (t->cont) {
|
||||||
|
// Read from pulseaudio buffer
|
||||||
|
if (pa_simple_read(t->s, buf, sizeof(buf) / 2, &t->error) < 0) {
|
||||||
|
printf("ERROR: pa_simple_read() failed: %s\n", pa_strerror(t->error));
|
||||||
|
t->cont = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spliting channels
|
||||||
|
for (unsigned int i = 0; i < t->samples / 2; i += 2) {
|
||||||
|
if (true) { // TODO Add mono to config
|
||||||
|
audio_out_l[n] = (buf[i] + buf[i + 1]) / 2;
|
||||||
|
} else {
|
||||||
|
audio_out_l[n] = buf[i];
|
||||||
|
audio_out_r[n] = buf[i + 1];
|
||||||
|
}
|
||||||
|
n++;
|
||||||
|
if (n == t->samples - 1)
|
||||||
|
n = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copying buffer
|
||||||
|
for (unsigned int i = 0; i < t->samples + 2; i++) {
|
||||||
|
if (i < t->samples) {
|
||||||
|
t->pa_buff_l[i] = audio_out_l[i];
|
||||||
|
t->pa_buff_r[i] = audio_out_r[i];
|
||||||
|
t->pa_output[i] = (audio_out_l[i] + audio_out_r[i]) / 2;
|
||||||
|
} else {
|
||||||
|
t->pa_buff_l[i] = 0;
|
||||||
|
t->pa_buff_r[i] = 0;
|
||||||
|
t->pa_output[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) { // TODO mono
|
||||||
|
// Run fftw
|
||||||
|
fftw_execute(t->plan_l);
|
||||||
|
|
||||||
|
// Separate fftw output
|
||||||
|
separate_freq_bands(t->fft_buff, t->output_l, smoothing_prec, lcf, hcf,
|
||||||
|
smoothing, sensitivity, t->output_samples);
|
||||||
|
} else {
|
||||||
|
fftw_execute(t->plan_l);
|
||||||
|
fftw_execute(t->plan_r);
|
||||||
|
|
||||||
|
separate_freq_bands(t->fft_buff_l, t->output_l, smoothing_prec / 2, lcf,
|
||||||
|
hcf, smoothing, sensitivity, t->output_samples);
|
||||||
|
separate_freq_bands(t->fft_buff_r, t->output_r, smoothing_prec / 2, lcf,
|
||||||
|
hcf, smoothing, sensitivity, t->output_samples);
|
||||||
|
|
||||||
|
// mirror stereo
|
||||||
|
for (int i = 0; i < smoothing_prec; i++) {
|
||||||
|
if (i < smoothing_prec / 2) {
|
||||||
|
t->fft_buff[i] = t->fft_buff_l[smoothing_prec / 2 - i - 1];
|
||||||
|
} else {
|
||||||
|
t->fft_buff[i] = t->fft_buff_r[i - smoothing_prec / 2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Processing */
|
||||||
|
// Waves
|
||||||
|
for (int i = 0; i < smoothing_prec; i++) {
|
||||||
|
t->fft_buff[i] *= 0.8;
|
||||||
|
for (int j = i - 1; j >= 0; j--) {
|
||||||
|
if (t->fft_buff[i] - (i - j) * (i - j) / 1000.0 > t->fft_buff[j])
|
||||||
|
t->fft_buff[j] = t->fft_buff[i] - (i - j) * (i - j) / 1000.0;
|
||||||
|
}
|
||||||
|
for (int j = i + 1; j < smoothing_prec; j++)
|
||||||
|
if (t->fft_buff[i] - (i - j) * (i - j) / 1000.0 > t->fft_buff[j])
|
||||||
|
t->fft_buff[j] = t->fft_buff[i] - (i - j) * (i - j) / 1000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gravity
|
||||||
|
for (int i = 0; i < smoothing_prec; i++) {
|
||||||
|
if (t->fft_buff[i] < flast[i]) {
|
||||||
|
t->fft_buff[i] = fpeak[i] - (gravity * fall[i] * fall[i]);
|
||||||
|
fall[i]++;
|
||||||
|
} else {
|
||||||
|
fpeak[i] = t->fft_buff[i];
|
||||||
|
fall[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
flast[i] = t->fft_buff[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Integral
|
||||||
|
for (int i = 0; i < smoothing_prec; i++) {
|
||||||
|
t->fft_buff[i] = (int)(t->fft_buff[i] * 100);
|
||||||
|
t->fft_buff[i] += fmem[i] * 0.9; // TODO: Add integral to config
|
||||||
|
fmem[i] = t->fft_buff[i];
|
||||||
|
|
||||||
|
int diff = 100 - t->fft_buff[i];
|
||||||
|
if (diff < 0)
|
||||||
|
diff = 0;
|
||||||
|
double div = 1 / (diff + 1);
|
||||||
|
fmem[i] *= 1 - div / 20;
|
||||||
|
t->fft_buff[i] /= 100.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto sensitivity
|
||||||
|
for (int i = 0; i < smoothing_prec; i++) {
|
||||||
|
if (t->fft_buff[i] > 0.95) {
|
||||||
|
sensitivity *= 0.985;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i == smoothing_prec - 1 && sensitivity < 1.0)
|
||||||
|
sensitivity *= 1.002;
|
||||||
|
}
|
||||||
|
if (sensitivity < 0.0001)
|
||||||
|
sensitivity = 0.0001;
|
||||||
|
|
||||||
|
for (int i = 0; i < smoothing_prec; i++)
|
||||||
|
t->fft_output[i] = t->fft_buff[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pa_mainloop *m_pulseaudio_mainloop;
|
||||||
|
|
||||||
|
static void cb(__attribute__((unused)) pa_context *pulseaudio_context,
|
||||||
|
const pa_server_info *i, void *userdata) {
|
||||||
|
|
||||||
|
// Obtain default sink name
|
||||||
|
pa_fft *pa_fft = (struct pa_fft *)userdata;
|
||||||
|
pa_fft->dev = malloc(sizeof(char) * 1024);
|
||||||
|
|
||||||
|
strcpy(pa_fft->dev, i->default_sink_name);
|
||||||
|
|
||||||
|
// Append `.monitor` suffix
|
||||||
|
pa_fft->dev = strcat(pa_fft->dev, ".monitor");
|
||||||
|
|
||||||
|
// Quiting mainloop
|
||||||
|
pa_context_disconnect(pulseaudio_context);
|
||||||
|
pa_context_unref(pulseaudio_context);
|
||||||
|
pa_mainloop_quit(m_pulseaudio_mainloop, 0);
|
||||||
|
pa_mainloop_free(m_pulseaudio_mainloop);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pulseaudio_context_state_callback(pa_context *pulseaudio_context,
|
||||||
|
void *userdata) {
|
||||||
|
// Ensure loop is ready
|
||||||
|
switch (pa_context_get_state(pulseaudio_context)) {
|
||||||
|
case PA_CONTEXT_UNCONNECTED:
|
||||||
|
break;
|
||||||
|
case PA_CONTEXT_CONNECTING:
|
||||||
|
break;
|
||||||
|
case PA_CONTEXT_AUTHORIZING:
|
||||||
|
break;
|
||||||
|
case PA_CONTEXT_SETTING_NAME:
|
||||||
|
break;
|
||||||
|
case PA_CONTEXT_READY: // Extract default sink name
|
||||||
|
pa_operation_unref(
|
||||||
|
pa_context_get_server_info(pulseaudio_context, cb, userdata));
|
||||||
|
break;
|
||||||
|
case PA_CONTEXT_FAILED:
|
||||||
|
printf("failed to connect to pulseaudio server\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
break;
|
||||||
|
case PA_CONTEXT_TERMINATED:
|
||||||
|
pa_mainloop_quit(m_pulseaudio_mainloop, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_pulse_default_sink(pa_fft *pa_fft) {
|
||||||
|
pa_mainloop_api *mainloop_api;
|
||||||
|
pa_context *pulseaudio_context;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
// Create a mainloop API and connection to the default server
|
||||||
|
m_pulseaudio_mainloop = pa_mainloop_new();
|
||||||
|
|
||||||
|
mainloop_api = pa_mainloop_get_api(m_pulseaudio_mainloop);
|
||||||
|
pulseaudio_context = pa_context_new(mainloop_api, "xglbg device list");
|
||||||
|
|
||||||
|
// Connect to the PA server
|
||||||
|
pa_context_connect(pulseaudio_context, NULL, PA_CONTEXT_NOFLAGS, NULL);
|
||||||
|
|
||||||
|
// Define a callback so the server will tell us its state
|
||||||
|
pa_context_set_state_callback(
|
||||||
|
pulseaudio_context, pulseaudio_context_state_callback, (void *)pa_fft);
|
||||||
|
|
||||||
|
// Start mainloop to get default sink
|
||||||
|
|
||||||
|
// Start with one non blocking iteration in case pulseaudio is not able to run
|
||||||
|
if (!(ret = pa_mainloop_iterate(m_pulseaudio_mainloop, 0, &ret))) {
|
||||||
|
printf(
|
||||||
|
"Could not open pulseaudio mainloop to find default device name: %d\n"
|
||||||
|
"Check if pulseaudio is running\n",
|
||||||
|
ret);
|
||||||
|
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_mainloop_run(m_pulseaudio_mainloop, &ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_pulse(pa_fft *pa_fft) {
|
||||||
|
if (!pa_fft->dev)
|
||||||
|
get_pulse_default_sink(pa_fft);
|
||||||
|
|
||||||
|
pa_fft->ss.format = PA_SAMPLE_S16LE;
|
||||||
|
pa_fft->ss.rate = pa_fft->rate;
|
||||||
|
pa_fft->ss.channels = 2;
|
||||||
|
|
||||||
|
const pa_buffer_attr pb = {.maxlength = (uint32_t)-1,
|
||||||
|
.fragsize = pa_fft->samples};
|
||||||
|
|
||||||
|
if (!(pa_fft->s = pa_simple_new(NULL, "xglbg", PA_STREAM_RECORD, pa_fft->dev,
|
||||||
|
"xglbg audio source", &pa_fft->ss, NULL, &pb,
|
||||||
|
&pa_fft->error))) {
|
||||||
|
printf("ERROR: pa_simple_new() failed: %s\n", pa_strerror(pa_fft->error));
|
||||||
|
pa_fft->cont = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_buffers(pa_fft *pa_fft) {
|
||||||
|
if (!pa_fft)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Pulse buffer
|
||||||
|
pa_fft->pa_samples = pa_fft->samples + 2;
|
||||||
|
pa_fft->pa_buff_size = sizeof(double) * pa_fft->pa_samples;
|
||||||
|
pa_fft->pa_buff_l = malloc(pa_fft->pa_buff_size);
|
||||||
|
pa_fft->pa_buff_r = malloc(pa_fft->pa_buff_size);
|
||||||
|
|
||||||
|
// FFT buffer
|
||||||
|
pa_fft->fft_samples = pa_fft->samples + 2;
|
||||||
|
pa_fft->fft_buff_size = sizeof(double) * pa_fft->fft_samples;
|
||||||
|
pa_fft->fft_buff = malloc(pa_fft->fft_buff_size);
|
||||||
|
pa_fft->fft_buff_l = malloc(pa_fft->fft_buff_size);
|
||||||
|
pa_fft->fft_buff_r = malloc(pa_fft->fft_buff_size);
|
||||||
|
|
||||||
|
// FFTW buffer
|
||||||
|
pa_fft->output_samples = (pa_fft->samples / 2) + 1;
|
||||||
|
pa_fft->output_size = sizeof(fftw_complex) * pa_fft->output_samples;
|
||||||
|
pa_fft->output_l = fftw_malloc(pa_fft->output_size);
|
||||||
|
pa_fft->output_r = fftw_malloc(pa_fft->output_size);
|
||||||
|
|
||||||
|
// Pulse output buffer
|
||||||
|
pa_fft->pa_output_size = sizeof(float) * pa_fft->pa_samples;
|
||||||
|
pa_fft->pa_output = malloc(pa_fft->pa_output_size);
|
||||||
|
|
||||||
|
// FFT output buffer
|
||||||
|
pa_fft->fft_output_size = sizeof(float) * pa_fft->fft_samples;
|
||||||
|
pa_fft->fft_output = malloc(pa_fft->fft_output_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_fft(pa_fft *pa_fft) {
|
||||||
|
if (!pa_fft)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pa_fft->plan_l = fftw_plan_dft_r2c_1d(pa_fft->pa_samples, pa_fft->pa_buff_l,
|
||||||
|
pa_fft->output_l, FFTW_MEASURE);
|
||||||
|
pa_fft->plan_r = fftw_plan_dft_r2c_1d(pa_fft->pa_samples, pa_fft->pa_buff_r,
|
||||||
|
pa_fft->output_r, FFTW_MEASURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deinit_fft(pa_fft *pa_fft) {
|
||||||
|
if (!pa_fft)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fftw_destroy_plan(pa_fft->plan_l);
|
||||||
|
fftw_destroy_plan(pa_fft->plan_r);
|
||||||
|
pa_simple_free(pa_fft->s);
|
||||||
|
free(pa_fft->pa_buff_l);
|
||||||
|
free(pa_fft->pa_buff_r);
|
||||||
|
|
||||||
|
pthread_join(pa_fft->thread, NULL);
|
||||||
|
|
||||||
|
free(pa_fft);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user