pulseaudio integration

This commit is contained in:
Gyuri Horák 2024-02-06 16:09:18 +01:00
parent eb570e90fd
commit 85c2e2a842
Signed by: dyuri
GPG Key ID: 4993F07B3EAE8D38
7 changed files with 888 additions and 456 deletions

1
TODO.md Normal file
View File

@ -0,0 +1 @@
- add pulse/fft textures

View File

@ -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
View 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

View File

@ -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)

View File

@ -15,139 +15,124 @@
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>
static void print_usage(char** argv) { static void print_usage(char **argv) {
char* slash = strrchr(argv[0], '/'); char *slash = strrchr(argv[0], '/');
uint64_t offset; uint64_t offset;
if(slash == NULL) { if (slash == NULL) {
offset = 0; offset = 0;
} else { } else {
offset = (slash - argv[0]) + 1; offset = (slash - argv[0]) + 1;
} }
printf("%s [options] <output> <shader>\n", argv[0] + offset); printf("%s [options] <output> <shader>\n", argv[0] + offset);
printf("Options:\n"); printf("Options:\n");
printf("--help\t\t-h\tDisplays this help message\n"); printf("--help\t\t-h\tDisplays this help message\n");
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");
exit(0); 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);
} }
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' .has_arg = required_argument,
}, .flag = NULL,
{ .val = 'l'},
.name = "fork", {.name = "width",
.has_arg = no_argument, .has_arg = required_argument,
.flag = NULL, .flag = NULL,
.val = 'F' .val = 'W'},
}, {.name = "height",
{ .has_arg = required_argument,
.name = "fps", .flag = NULL,
.has_arg = required_argument, .val = 'H'},
.flag = NULL, {.name = "source",
.val = 'f' .has_arg = required_argument,
}, .flag = NULL,
{ .val = 's'},
.name = "layer", {.name = NULL, .has_arg = 0, .flag = NULL, .val = 0}};
.has_arg = required_argument, char *fps_str = NULL;
.flag = NULL, char *layer = NULL;
.val = 'l' char *width_str = NULL;
}, char *height_str = NULL;
{ char *source_str = NULL;
.name = "width", char opt;
.has_arg = required_argument, while ((opt = getopt_long(argc, argv, "hFf:l:W:H:s:", opts, NULL)) != -1) {
.flag = NULL, switch (opt) {
.val = 'W' case 'h':
}, print_usage(argv);
{ break;
.name = "height", case 'F':
.has_arg = required_argument, if (fork() > 0) {
.flag = NULL, exit(0);
.val = 'H' }
}, fclose(stdout);
{ fclose(stderr);
.name = NULL, fclose(stdin);
.has_arg = 0, break;
.flag = NULL, case 'f':
.val = 0 fps_str = optarg;
} break;
}; case 'l':
char* fps_str = NULL; layer = optarg;
char* layer = NULL; break;
char* width_str = NULL; case 'W':
char* height_str = NULL; width_str = optarg;
char opt; break;
while((opt = getopt_long(argc, argv, "hFf:l:W:H:", opts, NULL)) != -1) { case 'H':
switch(opt) { height_str = optarg;
case 'h': break;
print_usage(argv); case 's':
break; source_str = strdup(optarg);
case 'F': break;
if(fork() > 0) { }
exit(0); }
} uint16_t fps;
fclose(stdout); if (fps_str == NULL) {
fclose(stderr); fps = 0;
fclose(stdin); } else {
break; fps = strtol(fps_str, NULL, 10);
case 'f': }
fps_str = optarg;
break;
case 'l':
layer = optarg;
break;
case 'W':
width_str = optarg;
break;
case 'H':
height_str = optarg;
break;
}
}
uint16_t fps;
if(fps_str == NULL) {
fps = 0;
} else {
fps = strtol(fps_str, NULL, 10);
}
uint16_t width; uint16_t width;
if(width_str == NULL) { if (width_str == NULL) {
width = 0; width = 0;
} else { } else {
width = strtol(width_str, NULL, 10); width = strtol(width_str, NULL, 10);
} }
uint16_t height; uint16_t height;
if(height_str == NULL) { if (height_str == NULL) {
height = 0; height = 0;
} else { } else {
height = strtol(height_str, NULL, 10); height = strtol(height_str, NULL, 10);
} }
if(optind + 1 >= argc) { if (optind + 1 >= argc) {
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,
} else { height);
print_usage(argv); } else {
} print_usage(argv);
}
} }

View File

@ -17,411 +17,431 @@
#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)
static const char* monitor; static const char *monitor;
static struct node* output = NULL; static struct node *output = NULL;
static struct wl_list outputs; static struct wl_list outputs;
static struct wl_compositor* comp; 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;
int32_t width, height; int32_t width, height;
struct wl_list link; struct wl_list link;
}; };
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,
(void) data; uint32_t name, const char *interface,
if(strcmp(interface, wl_output_interface.name) == 0) { uint32_t version) {
struct node* node = malloc(sizeof(struct node)); (void)data;
node->output = wl_registry_bind(registry, name, &wl_output_interface, PROTO_VERSION(version, 4)); if (strcmp(interface, wl_output_interface.name) == 0) {
wl_list_insert(&outputs, &node->link); struct node *node = malloc(sizeof(struct node));
} else if(strcmp(interface, wl_compositor_interface.name) == 0) { node->output = wl_registry_bind(registry, name, &wl_output_interface,
comp = wl_registry_bind(registry, name, &wl_compositor_interface, PROTO_VERSION(version, 4)); PROTO_VERSION(version, 4));
} else if(strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { wl_list_insert(&outputs, &node->link);
output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, PROTO_VERSION(version, 3)); } else if (strcmp(interface, wl_compositor_interface.name) == 0) {
} else if(strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { comp = wl_registry_bind(registry, name, &wl_compositor_interface,
shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, PROTO_VERSION(version, 4)); PROTO_VERSION(version, 4));
} } 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));
} 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));
}
} }
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,
(void) xdg_output; const char *name) {
struct node* node = data; (void)xdg_output;
if(strcmp(name, monitor) == 0) { struct node *node = data;
output = node; if (strcmp(name, monitor) == 0) {
} output = node;
}
} }
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,
(void) data; uint32_t serial, uint32_t width, uint32_t height) {
(void) width; (void)data;
(void) height; (void)width;
zwlr_layer_surface_v1_ack_configure(surface, serial); (void)height;
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,
(void) output; int32_t width, int32_t height, int32_t refresh) {
(void) refresh; (void)output;
if((flags & WL_OUTPUT_MODE_CURRENT) == WL_OUTPUT_MODE_CURRENT) { (void)refresh;
struct node* node = data; if ((flags & WL_OUTPUT_MODE_CURRENT) == WL_OUTPUT_MODE_CURRENT) {
node->width = width; struct node *node = data;
node->height = height; node->width = width;
} node->height = height;
}
} }
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);
GLint frag_len[] = {strlen(frag_data[0])}; GLint frag_len[] = {strlen(frag_data[0])};
glShaderSource(frag, 1, frag_data, frag_len); glShaderSource(frag, 1, frag_data, frag_len);
glCompileShader(frag); glCompileShader(frag);
GLint status; GLint status;
glGetShaderiv(frag, GL_COMPILE_STATUS, &status); glGetShaderiv(frag, GL_COMPILE_STATUS, &status);
if(!status) { if (!status) {
char buff[255]; char buff[255];
glGetShaderInfoLog(frag, sizeof(buff), NULL, buff); glGetShaderInfoLog(frag, sizeof(buff), NULL, buff);
fprintf(stderr, "Texture Frag: %s\n", buff); fprintf(stderr, "Texture Frag: %s\n", buff);
exit(1); exit(1);
} }
glAttachShader(*prog, vert); glAttachShader(*prog, vert);
glAttachShader(*prog, frag); glAttachShader(*prog, frag);
glLinkProgram(*prog); glLinkProgram(*prog);
glBindAttribLocation(*prog, 0, "datIn"); glBindAttribLocation(*prog, 0, "datIn");
glBindAttribLocation(*prog, 1, "texIn"); glBindAttribLocation(*prog, 1, "texIn");
glGetProgramiv(*prog, GL_LINK_STATUS, &status); glGetProgramiv(*prog, GL_LINK_STATUS, &status);
if(!status) { if (!status) {
char buff[255]; char buff[255];
glGetProgramInfoLog(*prog, sizeof(buff), NULL, buff); glGetProgramInfoLog(*prog, sizeof(buff), NULL, buff);
fprintf(stderr, "Texture Shader: %s\n", buff); fprintf(stderr, "Texture Shader: %s\n", buff);
exit(1); exit(1);
} }
glGenFramebuffers(1, fbo); glGenFramebuffers(1, fbo);
glBindFramebuffer(GL_FRAMEBUFFER, *fbo); glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
glGenTextures(1, texture); glGenTextures(1, texture);
glBindTexture(GL_TEXTURE_2D, *texture); glBindTexture(GL_TEXTURE_2D, *texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
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); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
GLint resolution = glGetUniformLocation(prog, "resolution");
glUniform2f(resolution, output->width, output->height);
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,
glViewport(0, 0, width, height); GLuint final_prog, uint16_t width, uint16_t height) {
glUseProgram(main_prog); glViewport(0, 0, width, height);
glBindFramebuffer(GL_FRAMEBUFFER, fbo); glUseProgram(main_prog);
GLint time_var = glGetUniformLocation(main_prog, "time"); glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glUniform1f(time_var, (utils_get_time_millis() - start) / 1000.0f); setup_vars(main_prog);
GLint resolution = glGetUniformLocation(main_prog, "resolution"); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glUniform2f(resolution, width, height);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glViewport(0, 0, output->width, output->height); glViewport(0, 0, output->width, output->height);
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
glUseProgram(final_prog); glUseProgram(final_prog);
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
GLint tex2D = glGetUniformLocation(final_prog, "tex2D"); GLint tex2D = glGetUniformLocation(final_prog, "tex2D");
glUniform1i(tex2D, 0); glUniform1i(tex2D, 0);
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,
monitor = _monitor; char *layer_name, uint16_t width, uint16_t height) {
start = utils_get_time_millis(); monitor = _monitor;
wl_list_init(&outputs); start = utils_get_time_millis();
struct wl_display* wl = wl_display_connect(NULL); wl_list_init(&outputs);
struct wl_display *wl = wl_display_connect(NULL);
struct wl_registry *registry = wl_display_get_registry(wl);
struct wl_registry_listener reg_listener = {.global = add_interface,
.global_remove = nop};
wl_registry_add_listener(registry, &reg_listener, NULL);
wl_display_roundtrip(wl);
struct wl_registry* registry = wl_display_get_registry(wl); struct node *node;
struct wl_registry_listener reg_listener = { wl_list_for_each(node, &outputs, link) {
.global = add_interface, struct zxdg_output_v1 *xdg_output =
.global_remove = nop zxdg_output_manager_v1_get_xdg_output(output_manager, node->output);
}; struct zxdg_output_v1_listener xdg_listener = {.description = nop,
wl_registry_add_listener(registry, &reg_listener, NULL); .done = nop,
wl_display_roundtrip(wl); .logical_position = nop,
.logical_size = nop,
.name = get_name};
zxdg_output_v1_add_listener(xdg_output, &xdg_listener, node);
struct wl_output_listener out_listener = {.done = nop,
.geometry = nop,
.mode = get_res,
.scale = nop,
.name = nop,
.description = nop};
wl_output_add_listener(node->output, &out_listener, node);
}
wl_display_roundtrip(wl);
struct node* node; if (output == NULL) {
wl_list_for_each(node, &outputs, link) { fprintf(stderr,
struct zxdg_output_v1* xdg_output = zxdg_output_manager_v1_get_xdg_output(output_manager, node->output); ":/ sorry about this but we can't seem to find that output\n");
struct zxdg_output_v1_listener xdg_listener = { exit(1);
.description = nop, }
.done = nop,
.logical_position = nop,
.logical_size = nop,
.name = get_name
};
zxdg_output_v1_add_listener(xdg_output, &xdg_listener, node);
struct wl_output_listener out_listener = { // pulseaudio fft
.done = nop, fft = calloc(1, sizeof(struct pa_fft));
.geometry = nop, fft->cont = 1;
.mode = get_res, fft->samples = 2048;
.scale = nop, fft->dev = pa_src;
.name = nop, fft->rate = 44100;
.description = nop
};
wl_output_add_listener(node->output, &out_listener, node);
}
wl_display_roundtrip(wl);
if(output == NULL) { init_pulse(fft);
fprintf(stderr, ":/ sorry about this but we can't seem to find that output\n"); init_buffers(fft);
exit(1); init_fft(fft);
}
struct wl_surface *wl_surface = wl_compositor_create_surface(comp);
struct wl_region *input_region = wl_compositor_create_region(comp);
struct wl_region *render_region = wl_compositor_create_region(comp);
wl_region_add(render_region, 0, 0, output->width, output->height);
wl_surface_set_opaque_region(wl_surface, render_region);
wl_surface_set_input_region(wl_surface, input_region);
wl_display_roundtrip(wl);
struct wl_surface* wl_surface = wl_compositor_create_surface(comp); enum zwlr_layer_shell_v1_layer layer;
struct wl_region* input_region = wl_compositor_create_region(comp);
struct wl_region* render_region = wl_compositor_create_region(comp);
wl_region_add(render_region, 0, 0, output->width, output->height);
wl_surface_set_opaque_region(wl_surface, render_region);
wl_surface_set_input_region(wl_surface, input_region);
wl_display_roundtrip(wl);
enum zwlr_layer_shell_v1_layer layer; if (layer_name == NULL) {
layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND;
} else if (strcasecmp(layer_name, "top") == 0) {
layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP;
} else if (strcasecmp(layer_name, "bottom") == 0) {
layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
} else if (strcasecmp(layer_name, "background") == 0) {
layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND;
} else if (strcasecmp(layer_name, "overlay") == 0) {
layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY;
} else {
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");
zwlr_layer_surface_v1_set_exclusive_zone(surface, -1);
zwlr_layer_surface_v1_set_size(surface, output->width, output->height);
struct zwlr_layer_surface_v1_listener surface_listener = {
.closed = nop, .configure = config_surface};
zwlr_layer_surface_v1_add_listener(surface, &surface_listener, NULL);
wl_surface_commit(wl_surface);
wl_display_roundtrip(wl);
if(layer_name == NULL) { struct wl_egl_window *window =
layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; wl_egl_window_create(wl_surface, output->width, output->height);
} else if(strcasecmp(layer_name, "top") == 0) { eglBindAPI(EGL_OPENGL_ES_API);
layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP; EGLDisplay egl_display =
} else if(strcasecmp(layer_name, "bottom") == 0) { eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, wl, NULL);
layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; eglInitialize(egl_display, NULL, NULL);
} else if(strcasecmp(layer_name, "background") == 0) { const EGLint win_attrib[] = {EGL_SURFACE_TYPE,
layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; EGL_WINDOW_BIT,
} else if(strcasecmp(layer_name, "overlay") == 0) { EGL_RENDERABLE_TYPE,
layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; EGL_OPENGL_ES2_BIT,
} else { EGL_RED_SIZE,
layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; 8,
} EGL_GREEN_SIZE,
struct zwlr_layer_surface_v1* surface = zwlr_layer_shell_v1_get_layer_surface(shell, wl_surface, output->output, layer, "glpaper"); 8,
zwlr_layer_surface_v1_set_exclusive_zone(surface, -1); EGL_BLUE_SIZE,
zwlr_layer_surface_v1_set_size(surface, output->width, output->height); 8,
struct zwlr_layer_surface_v1_listener surface_listener = { EGL_NONE};
.closed = nop,
.configure = config_surface
};
zwlr_layer_surface_v1_add_listener(surface, &surface_listener, NULL);
wl_surface_commit(wl_surface);
wl_display_roundtrip(wl);
struct wl_egl_window* window = wl_egl_window_create(wl_surface, output->width, output->height); EGLConfig config;
eglBindAPI(EGL_OPENGL_ES_API); EGLint config_len;
EGLDisplay egl_display = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, wl, NULL); eglChooseConfig(egl_display, win_attrib, &config, 1, &config_len);
eglInitialize(egl_display, NULL, NULL); const EGLint ctx_attrib[] = {EGL_CONTEXT_MAJOR_VERSION, 2,
const EGLint win_attrib[] = { EGL_CONTEXT_MINOR_VERSION, 0, EGL_NONE};
EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGLContext ctx =
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, eglCreateContext(egl_display, config, EGL_NO_CONTEXT, ctx_attrib);
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_NONE
};
EGLConfig config; EGLSurface egl_surface =
EGLint config_len; eglCreatePlatformWindowSurface(egl_display, config, window, NULL);
eglChooseConfig(egl_display, win_attrib, &config, 1, &config_len); eglMakeCurrent(egl_display, egl_surface, egl_surface, ctx);
const EGLint ctx_attrib[] = { if (fps == 0) {
EGL_CONTEXT_MAJOR_VERSION, 2, eglSwapInterval(egl_display, 1);
EGL_CONTEXT_MINOR_VERSION, 0, } else {
EGL_NONE eglSwapInterval(egl_display, 0);
}; }
EGLContext ctx = eglCreateContext(egl_display, config, EGL_NO_CONTEXT, ctx_attrib);
EGLSurface egl_surface = eglCreatePlatformWindowSurface(egl_display, config, window, NULL); gladLoadGLES2Loader((GLADloadproc)eglGetProcAddress);
eglMakeCurrent(egl_display, egl_surface, egl_surface, ctx); glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
if(fps == 0) { glViewport(0, 0, output->width, output->height);
eglSwapInterval(egl_display, 1); GLfloat vbo_data[] = {
} else { // Vertex Data //Texture data
eglSwapInterval(egl_display, 0); -1.0f, 1.0f, 0.0f, 1.0f, // Top left
} -1.0f, -1.0f, 0.0f, 0.0f, // Bottom left
1.0f, -1.0f, 1.0f, 0.0f, // Bottom right
1.0f, 1.0f, 1.0f, 1.0f, // Top right
};
gladLoadGLES2Loader((GLADloadproc) eglGetProcAddress); GLuint vbo;
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glGenBuffers(1, &vbo);
glViewport(0, 0, output->width, output->height); glBindBuffer(GL_ARRAY_BUFFER, vbo);
GLfloat vbo_data[] = { glBufferData(GL_ARRAY_BUFFER, sizeof(vbo_data), vbo_data, GL_STATIC_DRAW);
//Vertex Data //Texture data
-1.0f, 1.0f, 0.0f, 1.0f, //Top left
-1.0f, -1.0f, 0.0f, 0.0f, //Bottom left
1.0f, -1.0f, 1.0f, 0.0f, //Bottom right
1.0f, 1.0f, 1.0f, 1.0f, //Top right
};
GLuint vbo; GLuint ebo_data[] = {0, 1, 2, 2, 3, 0};
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vbo_data), vbo_data, GL_STATIC_DRAW);
GLuint ebo_data[] = { GLuint ebo;
0, 1, 2, glGenBuffers(1, &ebo);
2, 3, 0 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
}; glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ebo_data), ebo_data,
GL_STATIC_DRAW);
GLuint ebo; glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
glGenBuffers(1, &ebo); glEnableVertexAttribArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat),
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ebo_data), ebo_data, GL_STATIC_DRAW); (void *)(2 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); const char *vert_data[] = {"#version 100\n"
glEnableVertexAttribArray(0); "attribute highp vec2 datIn;"
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*) (2 * sizeof(GLfloat))); "attribute highp vec2 texIn;"
glEnableVertexAttribArray(1);
const char* vert_data[] = { "varying vec2 texCoords;"
"#version 100\n"
"attribute highp vec2 datIn;"
"attribute highp vec2 texIn;"
"varying vec2 texCoords;" "void main() {"
" texCoords = texIn;"
" gl_Position = vec4(datIn, 0.0, 1.0);"
"}"};
"void main() {" GLuint shader_prog = glCreateProgram();
" texCoords = texIn;" GLuint vert = glCreateShader(GL_VERTEX_SHADER);
" gl_Position = vec4(datIn, 0.0, 1.0);" GLint vert_len[] = {strlen(vert_data[0])};
"}" glShaderSource(vert, 1, vert_data, vert_len);
}; glCompileShader(vert);
GLint status;
glGetShaderiv(vert, GL_COMPILE_STATUS, &status);
if (!status) {
char buff[255];
glGetShaderInfoLog(vert, sizeof(buff), NULL, buff);
fprintf(stderr, "Vert: %s\n", buff);
exit(1);
}
GLuint shader_prog = glCreateProgram(); if (access(frag_path, R_OK) != 0) {
GLuint vert = glCreateShader(GL_VERTEX_SHADER); fprintf(stderr, "I can't seem to find %s\n", frag_path);
GLint vert_len[] = {strlen(vert_data[0])}; exit(1);
glShaderSource(vert, 1, vert_data, vert_len); }
glCompileShader(vert);
GLint status;
glGetShaderiv(vert, GL_COMPILE_STATUS, &status);
if(!status) {
char buff[255];
glGetShaderInfoLog(vert, sizeof(buff), NULL, buff);
fprintf(stderr, "Vert: %s\n", buff);
exit(1);
}
if(access(frag_path, R_OK) != 0) { FILE *f = fopen(frag_path, "r");
fprintf(stderr, "I can't seem to find %s\n", frag_path); fseek(f, 0L, SEEK_END);
exit(1); size_t f_size = ftell(f);
} const char *frag_data[1];
frag_data[0] = calloc(1, f_size + 1);
fseek(f, 0L, SEEK_SET);
fread((void *)frag_data[0], 1, f_size, f);
fclose(f);
FILE* f = fopen(frag_path, "r"); GLuint frag = glCreateShader(GL_FRAGMENT_SHADER);
fseek(f, 0L, SEEK_END); GLint frag_len[] = {f_size};
size_t f_size = ftell(f); glShaderSource(frag, 1, frag_data, frag_len);
const char* frag_data[1]; glCompileShader(frag);
frag_data[0] = calloc(1, f_size + 1); glGetShaderiv(frag, GL_COMPILE_STATUS, &status);
fseek(f, 0L, SEEK_SET); if (!status) {
fread((void*) frag_data[0], 1, f_size, f); char buff[255];
fclose(f); glGetShaderInfoLog(frag, sizeof(buff), NULL, buff);
fprintf(stderr, "Frag: %s\n", buff);
exit(1);
}
GLuint frag = glCreateShader(GL_FRAGMENT_SHADER); glAttachShader(shader_prog, vert);
GLint frag_len[] = {f_size}; glAttachShader(shader_prog, frag);
glShaderSource(frag, 1, frag_data, frag_len); glLinkProgram(shader_prog);
glCompileShader(frag); glBindAttribLocation(shader_prog, 0, "datIn");
glGetShaderiv(frag, GL_COMPILE_STATUS, &status); glBindAttribLocation(shader_prog, 1, "texIn");
if(!status) {
char buff[255];
glGetShaderInfoLog(frag, sizeof(buff), NULL, buff);
fprintf(stderr, "Frag: %s\n", buff);
exit(1);
}
glAttachShader(shader_prog, vert); glGetProgramiv(shader_prog, GL_LINK_STATUS, &status);
glAttachShader(shader_prog, frag); if (!status) {
glLinkProgram(shader_prog); char buff[255];
glBindAttribLocation(shader_prog, 0, "datIn"); glGetProgramInfoLog(shader_prog, sizeof(buff), NULL, buff);
glBindAttribLocation(shader_prog, 1, "texIn"); fprintf(stderr, "Shader: %s\n", buff);
exit(1);
}
glGetProgramiv(shader_prog, GL_LINK_STATUS, &status); bool use_fbo = width > 0 || height > 0;
if(!status) { GLuint fbo = 0;
char buff[255]; GLuint final_prog = 0;
glGetProgramInfoLog(shader_prog, sizeof(buff), NULL, buff); GLuint render_tex = 0;
fprintf(stderr, "Shader: %s\n", buff); if (use_fbo) {
exit(1); if (width == 0) {
} width = output->width;
}
if (height == 0) {
height = output->height;
}
setup_fbo(&fbo, &final_prog, &render_tex, vert, width, height);
}
glDeleteShader(vert);
glDeleteShader(frag);
glUseProgram(shader_prog);
bool use_fbo = width > 0 || height > 0; time_t frame_start;
GLuint fbo = 0;
GLuint final_prog = 0;
GLuint render_tex = 0;
if(use_fbo) {
if(width == 0) {
width = output->width;
}
if(height == 0) {
height = output->height;
}
setup_fbo(&fbo, &final_prog, &render_tex, vert, width, height);
}
glDeleteShader(vert); while (true) {
glDeleteShader(frag); frame_start = utils_get_time_millis();
glUseProgram(shader_prog); if (wl_display_flush(wl) == -1) {
exit(0);
time_t frame_start; }
if (use_fbo) {
while(true) { draw_fbo(fbo, render_tex, shader_prog, final_prog, width, height);
frame_start = utils_get_time_millis(); } else {
if(wl_display_flush(wl) == -1) { draw(shader_prog);
exit(0); }
} eglSwapBuffers(egl_display, egl_surface);
if(use_fbo) { if (fps != 0) {
draw_fbo(fbo, render_tex, shader_prog, final_prog, width, height); int64_t sleep = (1000 / fps) - (utils_get_time_millis() - frame_start);
} else { utils_sleep_millis(sleep >= 0 ? sleep : 0);
draw(shader_prog); }
} }
eglSwapBuffers(egl_display, egl_surface);
if(fps != 0) {
int64_t sleep = (1000 / fps) - (utils_get_time_millis() - frame_start);
utils_sleep_millis(sleep >= 0 ? sleep : 0);
}
}
} }

349
src/pulsefft.c Normal file
View 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);
}