pulseaudio integration
This commit is contained in:
parent
eb570e90fd
commit
85c2e2a842
@ -20,6 +20,7 @@
|
||||
|
||||
#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
|
||||
|
||||
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')
|
||||
wl_egl = dependency('wayland-egl')
|
||||
egl = dependency('egl')
|
||||
pulse = dependency('libpulse-simple')
|
||||
math = cc.find_library('m', required : false)
|
||||
fftw = dependency('fftw3')
|
||||
|
||||
executable(meson.project_name(),
|
||||
'src/main.c',
|
||||
'src/paper.c',
|
||||
'src/pulsefft.c',
|
||||
'src/utils.c',
|
||||
'glad/glad.c',
|
||||
'proto/wlr-layer-shell-unstable-v1-protocol.c',
|
||||
'proto/xdg-output-unstable-v1-protocol.c',
|
||||
'proto/xdg-shell-protocol.c',
|
||||
include_directories : inc,
|
||||
dependencies : [wayland, wl_egl, egl], install : true)
|
||||
dependencies : [math, wayland, wl_egl, egl, pulse, fftw], install : true)
|
||||
|
||||
233
src/main.c
233
src/main.c
@ -15,139 +15,124 @@
|
||||
along with GLPaper. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <getopt.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <paper.h>
|
||||
|
||||
static void print_usage(char** argv) {
|
||||
char* slash = strrchr(argv[0], '/');
|
||||
uint64_t offset;
|
||||
if(slash == NULL) {
|
||||
offset = 0;
|
||||
} else {
|
||||
offset = (slash - argv[0]) + 1;
|
||||
}
|
||||
printf("%s [options] <output> <shader>\n", argv[0] + offset);
|
||||
printf("Options:\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("--fps\t\t-f\tSets the FPS to render at\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("--height\t-H\tThe height to render at, this does not affect display resolution\n");
|
||||
exit(0);
|
||||
static void print_usage(char **argv) {
|
||||
char *slash = strrchr(argv[0], '/');
|
||||
uint64_t offset;
|
||||
if (slash == NULL) {
|
||||
offset = 0;
|
||||
} else {
|
||||
offset = (slash - argv[0]) + 1;
|
||||
}
|
||||
printf("%s [options] <output> <shader>\n", argv[0] + offset);
|
||||
printf("Options:\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("--fps\t\t-f\tSets the FPS to render at\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("--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) {
|
||||
if(argc > 2) {
|
||||
struct option opts[] = {
|
||||
{
|
||||
.name = "help",
|
||||
.has_arg = no_argument,
|
||||
.flag = NULL,
|
||||
.val = 'h'
|
||||
},
|
||||
{
|
||||
.name = "fork",
|
||||
.has_arg = no_argument,
|
||||
.flag = NULL,
|
||||
.val = 'F'
|
||||
},
|
||||
{
|
||||
.name = "fps",
|
||||
.has_arg = required_argument,
|
||||
.flag = NULL,
|
||||
.val = 'f'
|
||||
},
|
||||
{
|
||||
.name = "layer",
|
||||
.has_arg = required_argument,
|
||||
.flag = NULL,
|
||||
.val = 'l'
|
||||
},
|
||||
{
|
||||
.name = "width",
|
||||
.has_arg = required_argument,
|
||||
.flag = NULL,
|
||||
.val = 'W'
|
||||
},
|
||||
{
|
||||
.name = "height",
|
||||
.has_arg = required_argument,
|
||||
.flag = NULL,
|
||||
.val = 'H'
|
||||
},
|
||||
{
|
||||
.name = NULL,
|
||||
.has_arg = 0,
|
||||
.flag = NULL,
|
||||
.val = 0
|
||||
}
|
||||
};
|
||||
char* fps_str = NULL;
|
||||
char* layer = NULL;
|
||||
char* width_str = NULL;
|
||||
char* height_str = NULL;
|
||||
char opt;
|
||||
while((opt = getopt_long(argc, argv, "hFf:l:W:H:", opts, NULL)) != -1) {
|
||||
switch(opt) {
|
||||
case 'h':
|
||||
print_usage(argv);
|
||||
break;
|
||||
case 'F':
|
||||
if(fork() > 0) {
|
||||
exit(0);
|
||||
}
|
||||
fclose(stdout);
|
||||
fclose(stderr);
|
||||
fclose(stdin);
|
||||
break;
|
||||
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);
|
||||
}
|
||||
int main(int argc, char **argv) {
|
||||
if (argc > 2) {
|
||||
struct option opts[] = {
|
||||
{.name = "help", .has_arg = no_argument, .flag = NULL, .val = 'h'},
|
||||
{.name = "fork", .has_arg = no_argument, .flag = NULL, .val = 'F'},
|
||||
{.name = "fps", .has_arg = required_argument, .flag = NULL, .val = 'f'},
|
||||
{.name = "layer",
|
||||
.has_arg = required_argument,
|
||||
.flag = NULL,
|
||||
.val = 'l'},
|
||||
{.name = "width",
|
||||
.has_arg = required_argument,
|
||||
.flag = NULL,
|
||||
.val = 'W'},
|
||||
{.name = "height",
|
||||
.has_arg = required_argument,
|
||||
.flag = NULL,
|
||||
.val = 'H'},
|
||||
{.name = "source",
|
||||
.has_arg = required_argument,
|
||||
.flag = NULL,
|
||||
.val = 's'},
|
||||
{.name = NULL, .has_arg = 0, .flag = NULL, .val = 0}};
|
||||
char *fps_str = NULL;
|
||||
char *layer = NULL;
|
||||
char *width_str = NULL;
|
||||
char *height_str = NULL;
|
||||
char *source_str = NULL;
|
||||
char opt;
|
||||
while ((opt = getopt_long(argc, argv, "hFf:l:W:H:s:", opts, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
print_usage(argv);
|
||||
break;
|
||||
case 'F':
|
||||
if (fork() > 0) {
|
||||
exit(0);
|
||||
}
|
||||
fclose(stdout);
|
||||
fclose(stderr);
|
||||
fclose(stdin);
|
||||
break;
|
||||
case 'f':
|
||||
fps_str = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
layer = optarg;
|
||||
break;
|
||||
case 'W':
|
||||
width_str = optarg;
|
||||
break;
|
||||
case 'H':
|
||||
height_str = optarg;
|
||||
break;
|
||||
case 's':
|
||||
source_str = strdup(optarg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint16_t fps;
|
||||
if (fps_str == NULL) {
|
||||
fps = 0;
|
||||
} else {
|
||||
fps = strtol(fps_str, NULL, 10);
|
||||
}
|
||||
|
||||
uint16_t width;
|
||||
if(width_str == NULL) {
|
||||
width = 0;
|
||||
} else {
|
||||
width = strtol(width_str, NULL, 10);
|
||||
}
|
||||
uint16_t width;
|
||||
if (width_str == NULL) {
|
||||
width = 0;
|
||||
} else {
|
||||
width = strtol(width_str, NULL, 10);
|
||||
}
|
||||
|
||||
uint16_t height;
|
||||
if(height_str == NULL) {
|
||||
height = 0;
|
||||
} else {
|
||||
height = strtol(height_str, NULL, 10);
|
||||
}
|
||||
uint16_t height;
|
||||
if (height_str == NULL) {
|
||||
height = 0;
|
||||
} else {
|
||||
height = strtol(height_str, NULL, 10);
|
||||
}
|
||||
|
||||
if(optind + 1 >= argc) {
|
||||
print_usage(argv);
|
||||
}
|
||||
if (optind + 1 >= argc) {
|
||||
print_usage(argv);
|
||||
}
|
||||
|
||||
paper_init(argv[optind], argv[optind + 1], fps, layer, width, height);
|
||||
} else {
|
||||
print_usage(argv);
|
||||
}
|
||||
paper_init(argv[optind], argv[optind + 1], source_str, fps, layer, width,
|
||||
height);
|
||||
} else {
|
||||
print_usage(argv);
|
||||
}
|
||||
}
|
||||
|
||||
680
src/paper.c
680
src/paper.c
@ -17,411 +17,431 @@
|
||||
|
||||
#include <paper.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <utils.h>
|
||||
#include <pulsefft.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <utils.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <glad/glad_egl.h>
|
||||
|
||||
#include <wayland-egl.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 <xdg-output-unstable-v1-client-protocol.h>
|
||||
|
||||
#define PROTO_VERSION(v1, v2) (v1 < v2 ? v1 : v2)
|
||||
|
||||
static const char* monitor;
|
||||
static struct node* output = NULL;
|
||||
static const char *monitor;
|
||||
static struct node *output = NULL;
|
||||
static struct wl_list outputs;
|
||||
static struct wl_compositor* comp;
|
||||
static struct zwlr_layer_shell_v1* shell;
|
||||
static struct zxdg_output_manager_v1* output_manager;
|
||||
static struct wl_compositor *comp;
|
||||
static struct zwlr_layer_shell_v1 *shell;
|
||||
static struct zxdg_output_manager_v1 *output_manager;
|
||||
static time_t start;
|
||||
static struct pa_fft *fft;
|
||||
|
||||
struct node {
|
||||
struct wl_output* output;
|
||||
int32_t width, height;
|
||||
struct wl_list link;
|
||||
struct wl_output *output;
|
||||
int32_t width, height;
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
static void nop() {}
|
||||
|
||||
static void add_interface(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
|
||||
(void) data;
|
||||
if(strcmp(interface, wl_output_interface.name) == 0) {
|
||||
struct node* node = malloc(sizeof(struct node));
|
||||
node->output = wl_registry_bind(registry, name, &wl_output_interface, PROTO_VERSION(version, 4));
|
||||
wl_list_insert(&outputs, &node->link);
|
||||
} else if(strcmp(interface, wl_compositor_interface.name) == 0) {
|
||||
comp = wl_registry_bind(registry, name, &wl_compositor_interface, 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 add_interface(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *interface,
|
||||
uint32_t version) {
|
||||
(void)data;
|
||||
if (strcmp(interface, wl_output_interface.name) == 0) {
|
||||
struct node *node = malloc(sizeof(struct node));
|
||||
node->output = wl_registry_bind(registry, name, &wl_output_interface,
|
||||
PROTO_VERSION(version, 4));
|
||||
wl_list_insert(&outputs, &node->link);
|
||||
} else if (strcmp(interface, wl_compositor_interface.name) == 0) {
|
||||
comp = wl_registry_bind(registry, name, &wl_compositor_interface,
|
||||
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) {
|
||||
(void) xdg_output;
|
||||
struct node* node = data;
|
||||
if(strcmp(name, monitor) == 0) {
|
||||
output = node;
|
||||
}
|
||||
static void get_name(void *data, struct zxdg_output_v1 *xdg_output,
|
||||
const char *name) {
|
||||
(void)xdg_output;
|
||||
struct node *node = data;
|
||||
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) {
|
||||
(void) data;
|
||||
(void) width;
|
||||
(void) height;
|
||||
zwlr_layer_surface_v1_ack_configure(surface, serial);
|
||||
static void config_surface(void *data, struct zwlr_layer_surface_v1 *surface,
|
||||
uint32_t serial, uint32_t width, uint32_t height) {
|
||||
(void)data;
|
||||
(void)width;
|
||||
(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) {
|
||||
(void) output;
|
||||
(void) refresh;
|
||||
if((flags & WL_OUTPUT_MODE_CURRENT) == WL_OUTPUT_MODE_CURRENT) {
|
||||
struct node* node = data;
|
||||
node->width = width;
|
||||
node->height = height;
|
||||
}
|
||||
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)refresh;
|
||||
if ((flags & WL_OUTPUT_MODE_CURRENT) == WL_OUTPUT_MODE_CURRENT) {
|
||||
struct node *node = data;
|
||||
node->width = width;
|
||||
node->height = height;
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_fbo(GLuint* fbo, GLuint* prog, GLuint* texture, GLuint vert, uint16_t width, uint16_t height) {
|
||||
const char* frag_data[] = {
|
||||
"#version 100\n"
|
||||
"uniform sampler2D tex2D;"
|
||||
static void setup_fbo(GLuint *fbo, GLuint *prog, GLuint *texture, GLuint vert,
|
||||
uint16_t width, uint16_t height) {
|
||||
const char *frag_data[] = {"#version 100\n"
|
||||
"uniform sampler2D tex2D;"
|
||||
|
||||
"varying highp vec2 texCoords;"
|
||||
"varying highp vec2 texCoords;"
|
||||
|
||||
"void main() {"
|
||||
" gl_FragColor = texture2D(tex2D, texCoords);"
|
||||
"}"
|
||||
};
|
||||
"void main() {"
|
||||
" gl_FragColor = texture2D(tex2D, texCoords);"
|
||||
"}"};
|
||||
|
||||
*prog = glCreateProgram();
|
||||
GLuint frag = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
GLint frag_len[] = {strlen(frag_data[0])};
|
||||
glShaderSource(frag, 1, frag_data, frag_len);
|
||||
glCompileShader(frag);
|
||||
*prog = glCreateProgram();
|
||||
GLuint frag = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
GLint frag_len[] = {strlen(frag_data[0])};
|
||||
glShaderSource(frag, 1, frag_data, frag_len);
|
||||
glCompileShader(frag);
|
||||
|
||||
GLint status;
|
||||
glGetShaderiv(frag, GL_COMPILE_STATUS, &status);
|
||||
if(!status) {
|
||||
char buff[255];
|
||||
glGetShaderInfoLog(frag, sizeof(buff), NULL, buff);
|
||||
fprintf(stderr, "Texture Frag: %s\n", buff);
|
||||
exit(1);
|
||||
}
|
||||
GLint status;
|
||||
glGetShaderiv(frag, GL_COMPILE_STATUS, &status);
|
||||
if (!status) {
|
||||
char buff[255];
|
||||
glGetShaderInfoLog(frag, sizeof(buff), NULL, buff);
|
||||
fprintf(stderr, "Texture Frag: %s\n", buff);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
glAttachShader(*prog, vert);
|
||||
glAttachShader(*prog, frag);
|
||||
glLinkProgram(*prog);
|
||||
glAttachShader(*prog, vert);
|
||||
glAttachShader(*prog, frag);
|
||||
glLinkProgram(*prog);
|
||||
|
||||
glBindAttribLocation(*prog, 0, "datIn");
|
||||
glBindAttribLocation(*prog, 1, "texIn");
|
||||
glBindAttribLocation(*prog, 0, "datIn");
|
||||
glBindAttribLocation(*prog, 1, "texIn");
|
||||
|
||||
glGetProgramiv(*prog, GL_LINK_STATUS, &status);
|
||||
if(!status) {
|
||||
char buff[255];
|
||||
glGetProgramInfoLog(*prog, sizeof(buff), NULL, buff);
|
||||
fprintf(stderr, "Texture Shader: %s\n", buff);
|
||||
exit(1);
|
||||
}
|
||||
glGetProgramiv(*prog, GL_LINK_STATUS, &status);
|
||||
if (!status) {
|
||||
char buff[255];
|
||||
glGetProgramInfoLog(*prog, sizeof(buff), NULL, buff);
|
||||
fprintf(stderr, "Texture Shader: %s\n", buff);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
glGenFramebuffers(1, fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
|
||||
glGenFramebuffers(1, fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
|
||||
|
||||
glGenTextures(1, 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_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);
|
||||
glGenTextures(1, 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_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);
|
||||
|
||||
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) {
|
||||
glUseProgram(prog);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
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);
|
||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
||||
glUseProgram(prog);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
setup_vars(prog);
|
||||
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) {
|
||||
glViewport(0, 0, width, height);
|
||||
glUseProgram(main_prog);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
GLint time_var = glGetUniformLocation(main_prog, "time");
|
||||
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);
|
||||
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);
|
||||
glUseProgram(main_prog);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
setup_vars(main_prog);
|
||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
||||
|
||||
glViewport(0, 0, output->width, output->height);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glUseProgram(final_prog);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
GLint tex2D = glGetUniformLocation(final_prog, "tex2D");
|
||||
glUniform1i(tex2D, 0);
|
||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
||||
glViewport(0, 0, output->width, output->height);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glUseProgram(final_prog);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
GLint tex2D = glGetUniformLocation(final_prog, "tex2D");
|
||||
glUniform1i(tex2D, 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) {
|
||||
monitor = _monitor;
|
||||
start = utils_get_time_millis();
|
||||
wl_list_init(&outputs);
|
||||
struct wl_display* wl = wl_display_connect(NULL);
|
||||
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;
|
||||
start = utils_get_time_millis();
|
||||
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, ®_listener, NULL);
|
||||
wl_display_roundtrip(wl);
|
||||
|
||||
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, ®_listener, NULL);
|
||||
wl_display_roundtrip(wl);
|
||||
struct node *node;
|
||||
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_listener xdg_listener = {.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 = {.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;
|
||||
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_listener xdg_listener = {
|
||||
.description = nop,
|
||||
.done = nop,
|
||||
.logical_position = nop,
|
||||
.logical_size = nop,
|
||||
.name = get_name
|
||||
};
|
||||
zxdg_output_v1_add_listener(xdg_output, &xdg_listener, node);
|
||||
if (output == NULL) {
|
||||
fprintf(stderr,
|
||||
":/ sorry about this but we can't seem to find that output\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
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);
|
||||
// pulseaudio fft
|
||||
fft = calloc(1, sizeof(struct pa_fft));
|
||||
fft->cont = 1;
|
||||
fft->samples = 2048;
|
||||
fft->dev = pa_src;
|
||||
fft->rate = 44100;
|
||||
|
||||
if(output == NULL) {
|
||||
fprintf(stderr, ":/ sorry about this but we can't seem to find that output\n");
|
||||
exit(1);
|
||||
}
|
||||
init_pulse(fft);
|
||||
init_buffers(fft);
|
||||
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);
|
||||
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;
|
||||
|
||||
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) {
|
||||
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);
|
||||
struct wl_egl_window *window =
|
||||
wl_egl_window_create(wl_surface, output->width, output->height);
|
||||
eglBindAPI(EGL_OPENGL_ES_API);
|
||||
EGLDisplay egl_display =
|
||||
eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, wl, NULL);
|
||||
eglInitialize(egl_display, NULL, NULL);
|
||||
const EGLint win_attrib[] = {EGL_SURFACE_TYPE,
|
||||
EGL_WINDOW_BIT,
|
||||
EGL_RENDERABLE_TYPE,
|
||||
EGL_OPENGL_ES2_BIT,
|
||||
EGL_RED_SIZE,
|
||||
8,
|
||||
EGL_GREEN_SIZE,
|
||||
8,
|
||||
EGL_BLUE_SIZE,
|
||||
8,
|
||||
EGL_NONE};
|
||||
|
||||
struct wl_egl_window* window = wl_egl_window_create(wl_surface, output->width, output->height);
|
||||
eglBindAPI(EGL_OPENGL_ES_API);
|
||||
EGLDisplay egl_display = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, wl, NULL);
|
||||
eglInitialize(egl_display, NULL, NULL);
|
||||
const EGLint win_attrib[] = {
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_RED_SIZE, 8,
|
||||
EGL_GREEN_SIZE, 8,
|
||||
EGL_BLUE_SIZE, 8,
|
||||
EGL_NONE
|
||||
};
|
||||
EGLConfig config;
|
||||
EGLint config_len;
|
||||
eglChooseConfig(egl_display, win_attrib, &config, 1, &config_len);
|
||||
const EGLint ctx_attrib[] = {EGL_CONTEXT_MAJOR_VERSION, 2,
|
||||
EGL_CONTEXT_MINOR_VERSION, 0, EGL_NONE};
|
||||
EGLContext ctx =
|
||||
eglCreateContext(egl_display, config, EGL_NO_CONTEXT, ctx_attrib);
|
||||
|
||||
EGLConfig config;
|
||||
EGLint config_len;
|
||||
eglChooseConfig(egl_display, win_attrib, &config, 1, &config_len);
|
||||
const EGLint ctx_attrib[] = {
|
||||
EGL_CONTEXT_MAJOR_VERSION, 2,
|
||||
EGL_CONTEXT_MINOR_VERSION, 0,
|
||||
EGL_NONE
|
||||
};
|
||||
EGLContext ctx = eglCreateContext(egl_display, config, EGL_NO_CONTEXT, ctx_attrib);
|
||||
EGLSurface egl_surface =
|
||||
eglCreatePlatformWindowSurface(egl_display, config, window, NULL);
|
||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, ctx);
|
||||
if (fps == 0) {
|
||||
eglSwapInterval(egl_display, 1);
|
||||
} else {
|
||||
eglSwapInterval(egl_display, 0);
|
||||
}
|
||||
|
||||
EGLSurface egl_surface = eglCreatePlatformWindowSurface(egl_display, config, window, NULL);
|
||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, ctx);
|
||||
if(fps == 0) {
|
||||
eglSwapInterval(egl_display, 1);
|
||||
} else {
|
||||
eglSwapInterval(egl_display, 0);
|
||||
}
|
||||
gladLoadGLES2Loader((GLADloadproc)eglGetProcAddress);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glViewport(0, 0, output->width, output->height);
|
||||
GLfloat vbo_data[] = {
|
||||
// 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
|
||||
};
|
||||
|
||||
gladLoadGLES2Loader((GLADloadproc) eglGetProcAddress);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glViewport(0, 0, output->width, output->height);
|
||||
GLfloat vbo_data[] = {
|
||||
//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;
|
||||
glGenBuffers(1, &vbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vbo_data), vbo_data, GL_STATIC_DRAW);
|
||||
|
||||
GLuint vbo;
|
||||
glGenBuffers(1, &vbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vbo_data), vbo_data, GL_STATIC_DRAW);
|
||||
GLuint ebo_data[] = {0, 1, 2, 2, 3, 0};
|
||||
|
||||
GLuint ebo_data[] = {
|
||||
0, 1, 2,
|
||||
2, 3, 0
|
||||
};
|
||||
GLuint ebo;
|
||||
glGenBuffers(1, &ebo);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ebo_data), ebo_data,
|
||||
GL_STATIC_DRAW);
|
||||
|
||||
GLuint ebo;
|
||||
glGenBuffers(1, &ebo);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ebo_data), ebo_data, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat),
|
||||
(void *)(2 * sizeof(GLfloat)));
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*) (2 * sizeof(GLfloat)));
|
||||
glEnableVertexAttribArray(1);
|
||||
const char *vert_data[] = {"#version 100\n"
|
||||
"attribute highp vec2 datIn;"
|
||||
"attribute highp vec2 texIn;"
|
||||
|
||||
const char* vert_data[] = {
|
||||
"#version 100\n"
|
||||
"attribute highp vec2 datIn;"
|
||||
"attribute highp vec2 texIn;"
|
||||
"varying vec2 texCoords;"
|
||||
|
||||
"varying vec2 texCoords;"
|
||||
"void main() {"
|
||||
" texCoords = texIn;"
|
||||
" gl_Position = vec4(datIn, 0.0, 1.0);"
|
||||
"}"};
|
||||
|
||||
"void main() {"
|
||||
" texCoords = texIn;"
|
||||
" gl_Position = vec4(datIn, 0.0, 1.0);"
|
||||
"}"
|
||||
};
|
||||
GLuint shader_prog = glCreateProgram();
|
||||
GLuint vert = glCreateShader(GL_VERTEX_SHADER);
|
||||
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();
|
||||
GLuint vert = glCreateShader(GL_VERTEX_SHADER);
|
||||
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);
|
||||
}
|
||||
if (access(frag_path, R_OK) != 0) {
|
||||
fprintf(stderr, "I can't seem to find %s\n", frag_path);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(access(frag_path, R_OK) != 0) {
|
||||
fprintf(stderr, "I can't seem to find %s\n", frag_path);
|
||||
exit(1);
|
||||
}
|
||||
FILE *f = fopen(frag_path, "r");
|
||||
fseek(f, 0L, SEEK_END);
|
||||
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");
|
||||
fseek(f, 0L, SEEK_END);
|
||||
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);
|
||||
GLuint frag = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
GLint frag_len[] = {f_size};
|
||||
glShaderSource(frag, 1, frag_data, frag_len);
|
||||
glCompileShader(frag);
|
||||
glGetShaderiv(frag, GL_COMPILE_STATUS, &status);
|
||||
if (!status) {
|
||||
char buff[255];
|
||||
glGetShaderInfoLog(frag, sizeof(buff), NULL, buff);
|
||||
fprintf(stderr, "Frag: %s\n", buff);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
GLuint frag = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
GLint frag_len[] = {f_size};
|
||||
glShaderSource(frag, 1, frag_data, frag_len);
|
||||
glCompileShader(frag);
|
||||
glGetShaderiv(frag, GL_COMPILE_STATUS, &status);
|
||||
if(!status) {
|
||||
char buff[255];
|
||||
glGetShaderInfoLog(frag, sizeof(buff), NULL, buff);
|
||||
fprintf(stderr, "Frag: %s\n", buff);
|
||||
exit(1);
|
||||
}
|
||||
glAttachShader(shader_prog, vert);
|
||||
glAttachShader(shader_prog, frag);
|
||||
glLinkProgram(shader_prog);
|
||||
glBindAttribLocation(shader_prog, 0, "datIn");
|
||||
glBindAttribLocation(shader_prog, 1, "texIn");
|
||||
|
||||
glAttachShader(shader_prog, vert);
|
||||
glAttachShader(shader_prog, frag);
|
||||
glLinkProgram(shader_prog);
|
||||
glBindAttribLocation(shader_prog, 0, "datIn");
|
||||
glBindAttribLocation(shader_prog, 1, "texIn");
|
||||
glGetProgramiv(shader_prog, GL_LINK_STATUS, &status);
|
||||
if (!status) {
|
||||
char buff[255];
|
||||
glGetProgramInfoLog(shader_prog, sizeof(buff), NULL, buff);
|
||||
fprintf(stderr, "Shader: %s\n", buff);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
glGetProgramiv(shader_prog, GL_LINK_STATUS, &status);
|
||||
if(!status) {
|
||||
char buff[255];
|
||||
glGetProgramInfoLog(shader_prog, sizeof(buff), NULL, buff);
|
||||
fprintf(stderr, "Shader: %s\n", buff);
|
||||
exit(1);
|
||||
}
|
||||
bool use_fbo = width > 0 || height > 0;
|
||||
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);
|
||||
glDeleteShader(frag);
|
||||
glUseProgram(shader_prog);
|
||||
|
||||
bool use_fbo = width > 0 || height > 0;
|
||||
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);
|
||||
}
|
||||
time_t frame_start;
|
||||
|
||||
glDeleteShader(vert);
|
||||
glDeleteShader(frag);
|
||||
glUseProgram(shader_prog);
|
||||
|
||||
time_t frame_start;
|
||||
|
||||
while(true) {
|
||||
frame_start = utils_get_time_millis();
|
||||
if(wl_display_flush(wl) == -1) {
|
||||
exit(0);
|
||||
}
|
||||
if(use_fbo) {
|
||||
draw_fbo(fbo, render_tex, shader_prog, final_prog, width, height);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
while (true) {
|
||||
frame_start = utils_get_time_millis();
|
||||
if (wl_display_flush(wl) == -1) {
|
||||
exit(0);
|
||||
}
|
||||
if (use_fbo) {
|
||||
draw_fbo(fbo, render_tex, shader_prog, final_prog, width, height);
|
||||
} else {
|
||||
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
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