From a47fd9ee3f1b7a44e4375ed43cfddc05dc34820f Mon Sep 17 00:00:00 2001 From: OldShatterham Date: Wed, 22 Apr 2020 01:53:05 +0200 Subject: [PATCH 1/3] Add preliminary JACK support Add preliminary support for the JACK Audio Connection Kit. Code needs to be heavily cleaned up and checked, this is just to establish a first working configuration. --- glava/jack_input.c | 201 +++++++++++++++++++++++++++++++++++++++++++++ glava/jack_input.h | 7 ++ meson.build | 3 +- 3 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 glava/jack_input.c create mode 100644 glava/jack_input.h diff --git a/glava/jack_input.c b/glava/jack_input.c new file mode 100644 index 0000000..5901876 --- /dev/null +++ b/glava/jack_input.c @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fifo.h" + + +/* glava backend */ +struct audio_data* audio; +int buffer_steps; +int buffer_offset; + + +/* Resampling buffer */ +//When enough JACK samples to fill another glava buffer frame are summed, +//the current sample_sum_* is appended to frames_*[frame_index] +double sample_sum_1, sample_sum_2; +float sample_counter; + +float sample_sz_ratio = 93.75f; + +int frame_index; +int LIMIT; + + +/* JACK backend */ +const int glava_sample_rate = 22050; +const int JACK_sample_rate = 96000; +jack_port_t *input_port1, *input_port2; +jack_client_t* client; + +void updateBuffer(float frames_l[], float frames_r[]) { + float* bl = (float*) audio->audio_out_l; + float* br = (float*) audio->audio_out_r; + + pthread_mutex_lock(&audio->mutex); + + //Shift buffers: + memmove(bl, &bl[buffer_steps], buffer_offset * sizeof(float)); + memmove(br, &br[buffer_steps], buffer_offset * sizeof(float)); + + //Append samples from frames_l and frames_r: + float blockSum = 0.0f; + for (int i = 0; i < buffer_steps; i++) { + blockSum += frames_l[i]; + } + + for (int frame = 0; frame < buffer_steps; frame++) { + bl[buffer_offset + frame] = frames_l[frame]; + br[buffer_offset + frame] = frames_r[frame]; + } + + float checkSum = 0.0f; + for (int i = 0; i < buffer_steps; i++) { + checkSum += bl[buffer_offset + i]; + } + //fprintf(stdout, "[DEBUG] Copied data from frames_l to glava buffers; block sum: %f, check sum: %f.\n", blockSum, checkSum); + + + frame_index = 0; + + audio->modified = true; + pthread_mutex_unlock(&audio->mutex); +} + +int process (jack_nframes_t nframes, void *arg) { + double *frames_l = (double*)arg; + //TODO: double *frames_r = frames_l; + + float* samples1 = (jack_default_audio_sample_t*)jack_port_get_buffer (input_port1, nframes); + float* samples2 = (jack_default_audio_sample_t*)jack_port_get_buffer (input_port2, nframes); + + //Try to resample from JACK sample rate to glava sample rate + //TODO: find a proper resampling algorithm + + /*for (int frame = 0; frame < nframes; frame++) { + sample_sum_1 += *samples1; + sample_sum_2 += *samples2; + sample_counter++; + + if (sample_counter >= sample_sz_ratio) { + if (frame_index >= LIMIT) { + frame_index = LIMIT - 1; + fprintf(stderr, "Dear god, frame_index was %d while limit is %d!\n", frame_index, LIMIT); + } + frames_l[frame_index] = sample_sum_1 / sample_counter; + //TODO: frames_r[frame_index] = sample_sum_2 / sample_counter; + + sample_sum_1 = 0.0; + sample_sum_2 = 0.0; + + if(++frame_index >= buffer_steps) { + //TODO: Swap frames_l for frames_r + updateBuffer(frames_l, frames_l); + } + + sample_counter -= sample_sz_ratio; + } + }*/ + + updateBuffer(samples1, samples2); + + return 0; +} + +void jack_shutdown (void *arg) { + //DO SOMETHING HERE? + printf ("jack_shutdown() called.\n"); +} + +static void init(struct audio_data* audio) { + printf ("[DEBUG] init() called.\n"); + if (!audio->source) { + audio->source = strdup("/tmp/mpd.fifo"); + } + + const char *client_name = "glava"; + const char *server_name = NULL; + jack_options_t options = JackNullOption; + jack_status_t status; + client = jack_client_open (client_name, options, &status, server_name); + + if (client == NULL) { + fprintf (stderr, "jack_client_open() failed, status = 0x%2.0x\n", status); + if (status & JackServerFailed) { + printf ("Unable to connect to JACK server!\n"); + } + exit (1); + } + if (status & JackServerStarted) { + fprintf (stdout, "JACK server started\n"); + } + if (status & JackNameNotUnique) { + client_name = jack_get_client_name(client); + fprintf (stderr, "Unique name '%s' assigned!\n", client_name); + } + + jack_on_shutdown (client, jack_shutdown, 0); + + input_port1 = jack_port_register (client, "input1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); + input_port2 = jack_port_register (client, "input2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); + + if ((input_port1 == NULL) || (input_port2 == NULL)) { + fprintf (stderr, "No more JACK ports available!\n"); + exit (1); + } + printf ("[DEBUG] init() finished.\n"); +} + +static void* entry(void* data) { + printf ("[DEBUG] entry() called.\n"); + audio = (struct audio_data *) data; + + size_t buffer_sz = audio->audio_buf_sz; + size_t glava_sample_sz = audio->sample_sz; + sample_sz_ratio = ((float)JACK_sample_rate) / glava_sample_rate; + + //How many samples the buffer will shift each update: + //TODO: buffer_steps = glava_sample_sz / 4; + buffer_steps = 64; + buffer_offset = buffer_sz - buffer_steps; + + fprintf(stdout, "Buffer size: '%d', Sample size: '%d'\n", buffer_sz, glava_sample_sz); + + double frames_l[buffer_steps]; + //TODO: Clean all of this up... + double *frames_r = frames_l; + LIMIT = buffer_steps; + + jack_set_process_callback (client, process, frames_l); + printf ("[DEBUG] trying to activate client...\n"); + if (jack_activate (client)) { + fprintf (stderr, "Cannot activate client!\n"); + exit (1); + } + + while (true) { + sleep(1); + + if (audio->terminate == 1) { + break; + } + } + + jack_client_close (client); + printf ("[DEBUG] entry() finished.\n"); + return 0; +} + +AUDIO_ATTACH(jack); diff --git a/glava/jack_input.h b/glava/jack_input.h new file mode 100644 index 0000000..6448fa4 --- /dev/null +++ b/glava/jack_input.h @@ -0,0 +1,7 @@ + +#ifndef JACK_INPUT_H +#define JACK_INPUT_H + +#include "fifo.h" + +#endif diff --git a/meson.build b/meson.build index c3ba8cd..895ffa5 100644 --- a/meson.build +++ b/meson.build @@ -25,7 +25,8 @@ glava_dependencies = [ cc.find_library('dl'), cc.find_library('m'), cc.find_library('X11'), - cc.find_library('Xext') + cc.find_library('Xext'), + cc.find_library('jack') ] if cc.get_id() == 'clang' From e904d08e3265d818ed436650aa8ce2e69ea5c326 Mon Sep 17 00:00:00 2001 From: OldShatterham Date: Wed, 22 Apr 2020 19:31:24 +0200 Subject: [PATCH 2/3] Refactor code significantly Remove unneeded include directives, remove unneeded variables and associated code. Remove debug output to stdout. Remove superfluous comments. Clean up remaining code to match style guide. Add working files to .gitignore --- .gitignore | 4 +- glava/jack_input.c | 178 ++++++++++++--------------------------------- 2 files changed, 49 insertions(+), 133 deletions(-) diff --git a/.gitignore b/.gitignore index d163863..fd62742 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -build/ \ No newline at end of file +build/ +buildScript.sh +shaders/glava/rc_test.glsl \ No newline at end of file diff --git a/glava/jack_input.c b/glava/jack_input.c index 5901876..200ed86 100644 --- a/glava/jack_input.c +++ b/glava/jack_input.c @@ -1,185 +1,100 @@ -#include #include -#include #include -#include -#include -#include -#include -#include #include -#include -#include #include #include "fifo.h" -/* glava backend */ -struct audio_data* audio; -int buffer_steps; -int buffer_offset; - - -/* Resampling buffer */ -//When enough JACK samples to fill another glava buffer frame are summed, -//the current sample_sum_* is appended to frames_*[frame_index] -double sample_sum_1, sample_sum_2; -float sample_counter; - -float sample_sz_ratio = 93.75f; - -int frame_index; -int LIMIT; - +/* GLava backend and configuration */ +struct audio_data *audio; +size_t buffer_size; /* JACK backend */ -const int glava_sample_rate = 22050; -const int JACK_sample_rate = 96000; +jack_client_t *client; jack_port_t *input_port1, *input_port2; -jack_client_t* client; -void updateBuffer(float frames_l[], float frames_r[]) { + + +/* + * Callback function for JACK. + * Appends new samples to GLava buffer and marks it as modified. + */ +int process (jack_nframes_t nframes, void *arg) { + float* samples1 = (jack_default_audio_sample_t*)jack_port_get_buffer + (input_port1, nframes); + float* samples2 = (jack_default_audio_sample_t*)jack_port_get_buffer + (input_port2, nframes); + + pthread_mutex_lock(&audio->mutex); float* bl = (float*) audio->audio_out_l; float* br = (float*) audio->audio_out_r; - pthread_mutex_lock(&audio->mutex); - - //Shift buffers: + size_t buffer_steps = (size_t) nframes; + size_t buffer_offset = buffer_size - buffer_steps; memmove(bl, &bl[buffer_steps], buffer_offset * sizeof(float)); memmove(br, &br[buffer_steps], buffer_offset * sizeof(float)); - //Append samples from frames_l and frames_r: - float blockSum = 0.0f; - for (int i = 0; i < buffer_steps; i++) { - blockSum += frames_l[i]; - } - for (int frame = 0; frame < buffer_steps; frame++) { - bl[buffer_offset + frame] = frames_l[frame]; - br[buffer_offset + frame] = frames_r[frame]; + bl[buffer_offset + frame] = samples1[frame]; + br[buffer_offset + frame] = samples2[frame]; } - float checkSum = 0.0f; - for (int i = 0; i < buffer_steps; i++) { - checkSum += bl[buffer_offset + i]; - } - //fprintf(stdout, "[DEBUG] Copied data from frames_l to glava buffers; block sum: %f, check sum: %f.\n", blockSum, checkSum); - - - frame_index = 0; - audio->modified = true; pthread_mutex_unlock(&audio->mutex); -} - -int process (jack_nframes_t nframes, void *arg) { - double *frames_l = (double*)arg; - //TODO: double *frames_r = frames_l; - - float* samples1 = (jack_default_audio_sample_t*)jack_port_get_buffer (input_port1, nframes); - float* samples2 = (jack_default_audio_sample_t*)jack_port_get_buffer (input_port2, nframes); - - //Try to resample from JACK sample rate to glava sample rate - //TODO: find a proper resampling algorithm - - /*for (int frame = 0; frame < nframes; frame++) { - sample_sum_1 += *samples1; - sample_sum_2 += *samples2; - sample_counter++; - - if (sample_counter >= sample_sz_ratio) { - if (frame_index >= LIMIT) { - frame_index = LIMIT - 1; - fprintf(stderr, "Dear god, frame_index was %d while limit is %d!\n", frame_index, LIMIT); - } - frames_l[frame_index] = sample_sum_1 / sample_counter; - //TODO: frames_r[frame_index] = sample_sum_2 / sample_counter; - - sample_sum_1 = 0.0; - sample_sum_2 = 0.0; - - if(++frame_index >= buffer_steps) { - //TODO: Swap frames_l for frames_r - updateBuffer(frames_l, frames_l); - } - - sample_counter -= sample_sz_ratio; - } - }*/ - - updateBuffer(samples1, samples2); return 0; } void jack_shutdown (void *arg) { - //DO SOMETHING HERE? - printf ("jack_shutdown() called.\n"); + //Do more cleanup here if needed } static void init(struct audio_data* audio) { - printf ("[DEBUG] init() called.\n"); - if (!audio->source) { - audio->source = strdup("/tmp/mpd.fifo"); - } + if (!audio->source) + audio->source = strdup ("JACK"); - const char *client_name = "glava"; + const char *client_name = "GLava"; const char *server_name = NULL; jack_options_t options = JackNullOption; jack_status_t status; client = jack_client_open (client_name, options, &status, server_name); if (client == NULL) { - fprintf (stderr, "jack_client_open() failed, status = 0x%2.0x\n", status); - if (status & JackServerFailed) { - printf ("Unable to connect to JACK server!\n"); - } - exit (1); - } - if (status & JackServerStarted) { - fprintf (stdout, "JACK server started\n"); - } - if (status & JackNameNotUnique) { - client_name = jack_get_client_name(client); - fprintf (stderr, "Unique name '%s' assigned!\n", client_name); - } + fprintf (stderr, "jack_client_open() failed, status = 0x%2.0x\n", + status); + if (status & JackServerFailed) { + printf ("Unable to connect to JACK server!\n"); + } + exit (1); + } + if (status & JackServerStarted) { + fprintf (stdout, "JACK server started\n"); + } + if (status & JackNameNotUnique) { + client_name = jack_get_client_name (client); + fprintf (stderr, "Unique name '%s' assigned!\n", client_name); + } jack_on_shutdown (client, jack_shutdown, 0); - input_port1 = jack_port_register (client, "input1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); - input_port2 = jack_port_register (client, "input2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); + input_port1 = jack_port_register (client, "input1", JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, 0); + input_port2 = jack_port_register (client, "input2", JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, 0); if ((input_port1 == NULL) || (input_port2 == NULL)) { fprintf (stderr, "No more JACK ports available!\n"); exit (1); } - printf ("[DEBUG] init() finished.\n"); } static void* entry(void* data) { - printf ("[DEBUG] entry() called.\n"); audio = (struct audio_data *) data; + buffer_size = audio->audio_buf_sz; - size_t buffer_sz = audio->audio_buf_sz; - size_t glava_sample_sz = audio->sample_sz; - sample_sz_ratio = ((float)JACK_sample_rate) / glava_sample_rate; - - //How many samples the buffer will shift each update: - //TODO: buffer_steps = glava_sample_sz / 4; - buffer_steps = 64; - buffer_offset = buffer_sz - buffer_steps; - - fprintf(stdout, "Buffer size: '%d', Sample size: '%d'\n", buffer_sz, glava_sample_sz); - - double frames_l[buffer_steps]; - //TODO: Clean all of this up... - double *frames_r = frames_l; - LIMIT = buffer_steps; - - jack_set_process_callback (client, process, frames_l); - printf ("[DEBUG] trying to activate client...\n"); + jack_set_process_callback (client, process, NULL); if (jack_activate (client)) { fprintf (stderr, "Cannot activate client!\n"); exit (1); @@ -194,8 +109,7 @@ static void* entry(void* data) { } jack_client_close (client); - printf ("[DEBUG] entry() finished.\n"); return 0; } -AUDIO_ATTACH(jack); +AUDIO_ATTACH (jack); From 32de8029bb2026647c12bbcef64b495ef9fc462a Mon Sep 17 00:00:00 2001 From: OldShatterham Date: Wed, 22 Apr 2020 19:36:11 +0200 Subject: [PATCH 3/3] Update README Add information about this specific for of GLava and update building requirements. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 1870f39..8433801 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ +***Note:*** *This fork of GLava introduces support for the JACK Audio Connector Kit.* + **GLava** is a general-purpose, highly configurable OpenGL audio spectrum visualizer for X11. Displayed to the left is the `radial` shader module, or for a more extensive demonstration [see this demo](https://streamable.com/dgpj8). Development is active, and reporting issues is encouranged. @@ -35,6 +37,7 @@ Note that versions since `2.0` use Meson for the build system, although the `Mak - Meson - OBS (disable with `-Ddisable-obs=true`) +- JACK **Optional requirements:**