1-multichannel #1

Merged
boomjacky merged 31 commits from 1-multichannel into multichannel 2026-05-09 15:47:09 -04:00
2 changed files with 230 additions and 228 deletions
Showing only changes of commit 1db9735e1b - Show all commits

View File

@@ -1,13 +1,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jack/jack.h>
#include <jack/midiport.h>
#include <stdatomic.h>
#include <math.h>
#include "looper.h" #include "looper.h"
#include "channel.h" #include "channel.h"
#include "midi.h" #include "midi.h"
#include <jack/jack.h>
#include <jack/midiport.h>
#include <math.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Global state (shared across files) */ /* Global state (shared across files) */
struct channel_t channels[MAX_CHANNELS]; struct channel_t channels[MAX_CHANNELS];
@@ -26,8 +26,7 @@ static int pending_unregister_idx = -1;
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* process callback * process callback
* ---------------------------------------------------------------- */ * ---------------------------------------------------------------- */
int process_callback(jack_nframes_t nframes, void *arg) int process_callback(jack_nframes_t nframes, void *arg) {
{
(void)arg; (void)arg;
if (midi_control_port) { if (midi_control_port) {
@@ -39,7 +38,8 @@ int process_callback(jack_nframes_t nframes, void *arg)
/* process each active channel */ /* process each active channel */
for (int c = 0; c < MAX_CHANNELS; c++) { for (int c = 0; c < MAX_CHANNELS; c++) {
if (!atomic_load(&channels[c].active)) continue; if (!atomic_load(&channels[c].active))
continue;
/* Guard against NULL ports (e.g. if port registration failed) */ /* Guard against NULL ports (e.g. if port registration failed) */
if (!channels[c].audio_in || !channels[c].audio_out) { if (!channels[c].audio_in || !channels[c].audio_out) {
@@ -47,11 +47,14 @@ int process_callback(jack_nframes_t nframes, void *arg)
continue; continue;
} }
jack_default_audio_sample_t *in = (jack_default_audio_sample_t *) jack_default_audio_sample_t *in =
jack_port_get_buffer(channels[c].audio_in, nframes); (jack_default_audio_sample_t *)jack_port_get_buffer(
jack_default_audio_sample_t *out = (jack_default_audio_sample_t *) channels[c].audio_in, nframes);
jack_port_get_buffer(channels[c].audio_out, nframes); jack_default_audio_sample_t *out =
if (!out) continue; (jack_default_audio_sample_t *)jack_port_get_buffer(
channels[c].audio_out, nframes);
if (!out)
continue;
int state = atomic_load(&channels[c].state); int state = atomic_load(&channels[c].state);
@@ -77,7 +80,8 @@ int process_callback(jack_nframes_t nframes, void *arg)
if (in) { if (in) {
for (i = 0; i < nframes; i++) { for (i = 0; i < nframes; i++) {
if (channels[c].record_pos < LOOP_BUF_SIZE) if (channels[c].record_pos < LOOP_BUF_SIZE)
channels[c].loop_buffer[channels[c].record_pos++] = ((const float *)in)[i]; channels[c].loop_buffer[channels[c].record_pos++] =
((const float *)in)[i];
((float *)out)[i] = ((const float *)in)[i]; ((float *)out)[i] = ((const float *)in)[i];
} }
} else { } else {
@@ -90,7 +94,8 @@ int process_callback(jack_nframes_t nframes, void *arg)
float *outf = (float *)out; float *outf = (float *)out;
for (i = 0; i < nframes; i++) { for (i = 0; i < nframes; i++) {
outf[i] = channels[c].loop_buffer[channels[c].playback_pos]; outf[i] = channels[c].loop_buffer[channels[c].playback_pos];
channels[c].playback_pos = (channels[c].playback_pos + 1) % channels[c].loop_count; channels[c].playback_pos =
(channels[c].playback_pos + 1) % channels[c].loop_count;
} }
} else { } else {
memset(out, 0, sizeof(jack_default_audio_sample_t) * nframes); memset(out, 0, sizeof(jack_default_audio_sample_t) * nframes);
@@ -120,13 +125,15 @@ int process_callback(jack_nframes_t nframes, void *arg)
jack_nframes_t n_clock_events = jack_midi_get_event_count(midi_clock_buf); jack_nframes_t n_clock_events = jack_midi_get_event_count(midi_clock_buf);
jack_midi_event_t cev; jack_midi_event_t cev;
for (jack_nframes_t j = 0; j < n_clock_events; j++) { for (jack_nframes_t j = 0; j < n_clock_events; j++) {
if (jack_midi_event_get(&cev, midi_clock_buf, j) != 0) continue; if (jack_midi_event_get(&cev, midi_clock_buf, j) != 0)
continue;
if (cev.size >= 1) { if (cev.size >= 1) {
unsigned char msg = cev.buffer[0]; unsigned char msg = cev.buffer[0];
switch (msg) { switch (msg) {
case 0xFA: { case 0xFA: {
int s = atomic_load(&channels[0].state); int s = atomic_load(&channels[0].state);
if (s == STATE_IDLE) atomic_store(&channels[0].state, STATE_RECORD); if (s == STATE_IDLE)
atomic_store(&channels[0].state, STATE_RECORD);
break; break;
} }
case 0xFC: case 0xFC:
@@ -134,7 +141,8 @@ int process_callback(jack_nframes_t nframes, void *arg)
break; break;
case 0xFB: { case 0xFB: {
int s = atomic_load(&channels[0].state); int s = atomic_load(&channels[0].state);
if (s == STATE_PAUSED) atomic_store(&channels[0].state, STATE_LOOPING); if (s == STATE_PAUSED)
atomic_store(&channels[0].state, STATE_LOOPING);
break; break;
} }
default: default:
@@ -151,8 +159,7 @@ int process_callback(jack_nframes_t nframes, void *arg)
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* shutdown callback * shutdown callback
* ---------------------------------------------------------------- */ * ---------------------------------------------------------------- */
void jack_shutdown_cb(void *arg) void jack_shutdown_cb(void *arg) {
{
(void)arg; (void)arg;
fprintf(stderr, "JACK shutdown\n"); fprintf(stderr, "JACK shutdown\n");
exit(0); exit(0);
@@ -161,8 +168,7 @@ void jack_shutdown_cb(void *arg)
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* looper initialisation * looper initialisation
* ---------------------------------------------------------------- */ * ---------------------------------------------------------------- */
int looper_init(jack_client_t *client) int looper_init(jack_client_t *client) {
{
/* channel 0 */ /* channel 0 */
channels[0].active = 1; channels[0].active = 1;
atomic_store(&channels[0].state, STATE_IDLE); atomic_store(&channels[0].state, STATE_IDLE);
@@ -171,23 +177,19 @@ int looper_init(jack_client_t *client)
channels[0].record_pos = 0; channels[0].record_pos = 0;
channels[0].playback_pos = 0; channels[0].playback_pos = 0;
channels[0].audio_in = jack_port_register(client, "input", channels[0].audio_in = jack_port_register(
JACK_DEFAULT_AUDIO_TYPE, client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
JackPortIsInput, 0); channels[0].audio_out = jack_port_register(
channels[0].audio_out = jack_port_register(client, "output", client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput, 0);
if (!channels[0].audio_in || !channels[0].audio_out) { if (!channels[0].audio_in || !channels[0].audio_out) {
fprintf(stderr, "Could not create audio ports for channel 0\n"); fprintf(stderr, "Could not create audio ports for channel 0\n");
return -1; return -1;
} }
channel_count = 1; channel_count = 1;
midi_control_port = jack_port_register(client, "control", midi_control_port = jack_port_register(
JACK_DEFAULT_MIDI_TYPE, client, "control", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
JackPortIsInput, 0); midi_clock_port = jack_port_register(client, "clock", JACK_DEFAULT_MIDI_TYPE,
midi_clock_port = jack_port_register(client, "clock",
JACK_DEFAULT_MIDI_TYPE,
JackPortIsInput, 0); JackPortIsInput, 0);
if (!midi_control_port || !midi_clock_port) { if (!midi_control_port || !midi_clock_port) {
fprintf(stderr, "Could not create MIDI ports\n"); fprintf(stderr, "Could not create MIDI ports\n");
@@ -200,8 +202,7 @@ int looper_init(jack_client_t *client)
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* mainloop command processing * mainloop command processing
* ---------------------------------------------------------------- */ * ---------------------------------------------------------------- */
void looper_process_commands(jack_client_t *client) void looper_process_commands(jack_client_t *client) {
{
/* Unregister any ports that were marked for deferred removal. /* Unregister any ports that were marked for deferred removal.
By now the realtime thread has had at least one full cycle By now the realtime thread has had at least one full cycle
to see the `active = 0` store. */ to see the `active = 0` store. */
@@ -217,7 +218,8 @@ void looper_process_commands(jack_client_t *client)
if (atomic_exchange(&cmd_add, 0)) { if (atomic_exchange(&cmd_add, 0)) {
int idx; int idx;
for (idx = 0; idx < MAX_CHANNELS; idx++) for (idx = 0; idx < MAX_CHANNELS; idx++)
if (!channels[idx].active) break; if (!channels[idx].active)
break;
if (idx < MAX_CHANNELS) { if (idx < MAX_CHANNELS) {
channel_add(client, idx); channel_add(client, idx);
} }
@@ -226,7 +228,8 @@ void looper_process_commands(jack_client_t *client)
if (atomic_exchange(&cmd_remove, 0)) { if (atomic_exchange(&cmd_remove, 0)) {
int remove_idx = -1; int remove_idx = -1;
for (int idx = 1; idx < MAX_CHANNELS; idx++) for (int idx = 1; idx < MAX_CHANNELS; idx++)
if (channels[idx].active) remove_idx = idx; if (channels[idx].active)
remove_idx = idx;
if (remove_idx != -1) { if (remove_idx != -1) {
/* Mark inactive now; ports will be unregistered next round */ /* Mark inactive now; ports will be unregistered next round */
channel_remove(client, remove_idx); channel_remove(client, remove_idx);

View File

@@ -1,11 +1,10 @@
#include "looper.h"
#include <jack/jack.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <jack/jack.h>
#include "looper.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[]) {
{
(void)argc; (void)argc;
(void)argv; (void)argv;
const char *client_name = "looper"; const char *client_name = "looper";