2-midi-looping #3
@@ -6,35 +6,37 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
void channel_add(jack_client_t *client, int idx) {
|
void channel_add(jack_client_t *client, int idx) {
|
||||||
|
struct channel_t *cur = get_channels_array();
|
||||||
|
|
||||||
char in_name[64], out_name[64];
|
char in_name[64], out_name[64];
|
||||||
snprintf(in_name, sizeof(in_name), "channel%d_input", next_channel_id);
|
snprintf(in_name, sizeof(in_name), "channel%d_input", next_channel_id);
|
||||||
snprintf(out_name, sizeof(out_name), "channel%d_output", next_channel_id);
|
snprintf(out_name, sizeof(out_name), "channel%d_output", next_channel_id);
|
||||||
|
|
||||||
channels[idx].audio_in = jack_port_register(
|
cur[idx].audio_in = jack_port_register(
|
||||||
client, in_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
|
client, in_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
|
||||||
channels[idx].audio_out = jack_port_register(
|
cur[idx].audio_out = jack_port_register(
|
||||||
client, out_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
|
client, out_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
|
||||||
if (!channels[idx].audio_in || !channels[idx].audio_out) {
|
if (!cur[idx].audio_in || !cur[idx].audio_out) {
|
||||||
fprintf(stderr, "Failed to register ports for channel %d\n",
|
fprintf(stderr, "Failed to register ports for channel %d\n",
|
||||||
next_channel_id);
|
next_channel_id);
|
||||||
/* Do NOT mark channel active – process loop will skip it */
|
atomic_store(&cur[idx].active, 0);
|
||||||
atomic_store(&channels[idx].active, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_store(&channels[idx].active, 1);
|
atomic_store(&cur[idx].active, 1);
|
||||||
atomic_store(&channels[idx].state, STATE_IDLE);
|
atomic_store(&cur[idx].state, STATE_IDLE);
|
||||||
channels[idx].prev_state = -1;
|
cur[idx].prev_state = -1;
|
||||||
channels[idx].loop_count = 0;
|
cur[idx].loop_count = 0;
|
||||||
channels[idx].record_pos = 0;
|
cur[idx].record_pos = 0;
|
||||||
channels[idx].playback_pos = 0;
|
cur[idx].playback_pos = 0;
|
||||||
|
|
||||||
next_channel_id++;
|
next_channel_id++;
|
||||||
channel_count++;
|
atomic_fetch_add(&channel_count, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void channel_remove(jack_client_t *client, int idx) {
|
void channel_remove(jack_client_t *client, int idx) {
|
||||||
(void)client;
|
(void)client;
|
||||||
atomic_store(&channels[idx].active, 0);
|
struct channel_t *cur = get_channels_array();
|
||||||
channel_count--;
|
atomic_store(&cur[idx].active, 0);
|
||||||
|
atomic_fetch_sub(&channel_count, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
|
|
||||||
#define LOOP_BUF_SIZE (5 * 48000)
|
#define LOOP_BUF_SIZE (5 * 48000)
|
||||||
#define MAX_CHANNELS 16
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
STATE_IDLE,
|
STATE_IDLE,
|
||||||
@@ -28,10 +27,16 @@ struct channel_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Globals declared in looper.c */
|
/* Globals declared in looper.c */
|
||||||
extern struct channel_t channels[MAX_CHANNELS];
|
extern _Atomic struct channel_t *channels;
|
||||||
|
extern atomic_int channel_capacity;
|
||||||
extern atomic_int channel_count;
|
extern atomic_int channel_count;
|
||||||
extern int next_channel_id;
|
extern int next_channel_id;
|
||||||
|
|
||||||
|
/* Safe accessor for the real‑time thread (returns a snapshot of the current pointer) */
|
||||||
|
static inline struct channel_t *get_channels_array(void) {
|
||||||
|
return atomic_load(&channels);
|
||||||
|
}
|
||||||
|
|
||||||
void channel_add(jack_client_t *client, int idx);
|
void channel_add(jack_client_t *client, int idx);
|
||||||
void channel_remove(jack_client_t *client, int idx);
|
void channel_remove(jack_client_t *client, int idx);
|
||||||
|
|
||||||
|
|||||||
172
src/looper.c
172
src/looper.c
@@ -13,7 +13,8 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/* Global state (shared across files) */
|
/* Global state (shared across files) */
|
||||||
struct channel_t channels[MAX_CHANNELS];
|
_Atomic struct channel_t *channels = NULL;
|
||||||
|
atomic_int channel_capacity = 0;
|
||||||
atomic_int channel_count = 0;
|
atomic_int channel_count = 0;
|
||||||
int next_channel_id = 1;
|
int next_channel_id = 1;
|
||||||
spsc_queue_t cmd_queue_main_midi;
|
spsc_queue_t cmd_queue_main_midi;
|
||||||
@@ -29,13 +30,38 @@ spsc_queue_t cmd_queue;
|
|||||||
static int pending_unregister_idx = -1;
|
static int pending_unregister_idx = -1;
|
||||||
static int pending_unregister_cycle = 0;
|
static int pending_unregister_cycle = 0;
|
||||||
|
|
||||||
|
/* Helper: grow the channel array so that index idx is valid */
|
||||||
|
static int ensure_capacity(jack_client_t *client, int idx) {
|
||||||
|
(void)client;
|
||||||
|
int cur_cap = atomic_load(&channel_capacity);
|
||||||
|
if (idx < cur_cap)
|
||||||
|
return 0;
|
||||||
|
int new_cap = cur_cap == 0 ? 8 : cur_cap;
|
||||||
|
while (new_cap <= idx)
|
||||||
|
new_cap *= 2;
|
||||||
|
struct channel_t *new_arr = calloc(new_cap, sizeof(struct channel_t));
|
||||||
|
if (!new_arr)
|
||||||
|
return -1;
|
||||||
|
/* copy existing channels */
|
||||||
|
if (cur_cap > 0)
|
||||||
|
memcpy(new_arr, atomic_load(&channels), cur_cap * sizeof(struct channel_t));
|
||||||
|
/* atomically publish new array */
|
||||||
|
struct channel_t *old = atomic_exchange(&channels, new_arr);
|
||||||
|
atomic_store(&channel_capacity, new_cap);
|
||||||
|
free(old);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void apply_command(command_t cmd) {
|
static void apply_command(command_t cmd) {
|
||||||
|
struct channel_t *cur = get_channels_array();
|
||||||
|
int cap = atomic_load(&channel_capacity);
|
||||||
|
|
||||||
switch (cmd.type) {
|
switch (cmd.type) {
|
||||||
case CMD_CYCLE:
|
case CMD_CYCLE:
|
||||||
if (cmd.channel >= 0 && cmd.channel < MAX_CHANNELS) {
|
if (cmd.channel >= 0 && cmd.channel < cap) {
|
||||||
int cur = atomic_load(&channels[cmd.channel].state);
|
int cst = atomic_load(&cur[cmd.channel].state);
|
||||||
int next;
|
int next;
|
||||||
switch (cur) {
|
switch (cst) {
|
||||||
case STATE_IDLE:
|
case STATE_IDLE:
|
||||||
next = STATE_RECORD;
|
next = STATE_RECORD;
|
||||||
break;
|
break;
|
||||||
@@ -52,15 +78,15 @@ static void apply_command(command_t cmd) {
|
|||||||
next = STATE_IDLE;
|
next = STATE_IDLE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
atomic_store(&channels[cmd.channel].state, next);
|
atomic_store(&cur[cmd.channel].state, next);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CMD_STOP:
|
case CMD_STOP:
|
||||||
if (cmd.channel >= 0 && cmd.channel < MAX_CHANNELS)
|
if (cmd.channel >= 0 && cmd.channel < cap)
|
||||||
atomic_store(&channels[cmd.channel].state, STATE_IDLE);
|
atomic_store(&cur[cmd.channel].state, STATE_IDLE);
|
||||||
else {
|
else {
|
||||||
for (int i = 0; i < MAX_CHANNELS; i++)
|
for (int i = 0; i < cap; i++)
|
||||||
atomic_store(&channels[i].state, STATE_IDLE);
|
atomic_store(&cur[i].state, STATE_IDLE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CMD_BIND_CHANNEL:
|
case CMD_BIND_CHANNEL:
|
||||||
@@ -94,37 +120,39 @@ 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++) {
|
struct channel_t *active_channels = get_channels_array();
|
||||||
if (!atomic_load(&channels[c].active))
|
int cap = atomic_load(&channel_capacity);
|
||||||
|
for (int c = 0; c < cap; c++) {
|
||||||
|
if (!atomic_load(&active_channels[c].active))
|
||||||
continue;
|
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 (!active_channels[c].audio_in || !active_channels[c].audio_out) {
|
||||||
fprintf(stderr, "WARN: channel %d has NULL audio port(s), skipping\n", c);
|
fprintf(stderr, "WARN: channel %d has NULL audio port(s), skipping\n", c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const jack_default_audio_sample_t *in =
|
const jack_default_audio_sample_t *in =
|
||||||
(const jack_default_audio_sample_t *)jack_port_get_buffer(
|
(const jack_default_audio_sample_t *)jack_port_get_buffer(
|
||||||
channels[c].audio_in, nframes);
|
active_channels[c].audio_in, nframes);
|
||||||
jack_default_audio_sample_t *out =
|
jack_default_audio_sample_t *out =
|
||||||
(jack_default_audio_sample_t *)jack_port_get_buffer(
|
(jack_default_audio_sample_t *)jack_port_get_buffer(
|
||||||
channels[c].audio_out, nframes);
|
active_channels[c].audio_out, nframes);
|
||||||
if (!out)
|
if (!out)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int state = atomic_load(&channels[c].state);
|
int state = atomic_load(&active_channels[c].state);
|
||||||
|
|
||||||
if (state != channels[c].prev_state) {
|
if (state != active_channels[c].prev_state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case STATE_RECORD:
|
case STATE_RECORD:
|
||||||
channels[c].record_pos = 0;
|
active_channels[c].record_pos = 0;
|
||||||
channels[c].loop_count = 0;
|
active_channels[c].loop_count = 0;
|
||||||
break;
|
break;
|
||||||
case STATE_LOOPING:
|
case STATE_LOOPING:
|
||||||
if (channels[c].record_pos > 0)
|
if (active_channels[c].record_pos > 0)
|
||||||
channels[c].loop_count = channels[c].record_pos;
|
active_channels[c].loop_count = active_channels[c].record_pos;
|
||||||
channels[c].playback_pos = 0;
|
active_channels[c].playback_pos = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -138,8 +166,8 @@ int process_callback(jack_nframes_t nframes, void *arg) {
|
|||||||
float *f_out = (float *)out;
|
float *f_out = (float *)out;
|
||||||
const float *f_in = (const float *)in;
|
const float *f_in = (const float *)in;
|
||||||
for (i = 0; i < nframes; i++) {
|
for (i = 0; i < nframes; i++) {
|
||||||
if (channels[c].record_pos < LOOP_BUF_SIZE)
|
if (active_channels[c].record_pos < LOOP_BUF_SIZE)
|
||||||
channels[c].loop_buffer[channels[c].record_pos++] = f_in[i];
|
active_channels[c].loop_buffer[active_channels[c].record_pos++] = f_in[i];
|
||||||
f_out[i] = f_in[i];
|
f_out[i] = f_in[i];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -148,12 +176,12 @@ int process_callback(jack_nframes_t nframes, void *arg) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_LOOPING:
|
case STATE_LOOPING:
|
||||||
if (channels[c].loop_count > 0) {
|
if (active_channels[c].loop_count > 0) {
|
||||||
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] = active_channels[c].loop_buffer[active_channels[c].playback_pos];
|
||||||
channels[c].playback_pos =
|
active_channels[c].playback_pos =
|
||||||
(channels[c].playback_pos + 1) % channels[c].loop_count;
|
(active_channels[c].playback_pos + 1) % active_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);
|
||||||
@@ -173,7 +201,7 @@ int process_callback(jack_nframes_t nframes, void *arg) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
channels[c].prev_state = state;
|
active_channels[c].prev_state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MIDI clock events – affect channel 0 only */
|
/* MIDI clock events – affect channel 0 only */
|
||||||
@@ -189,18 +217,22 @@ int process_callback(jack_nframes_t nframes, void *arg) {
|
|||||||
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);
|
struct channel_t *cur = atomic_load(&channels);
|
||||||
|
int s = atomic_load(&cur[0].state);
|
||||||
if (s == STATE_IDLE)
|
if (s == STATE_IDLE)
|
||||||
atomic_store(&channels[0].state, STATE_RECORD);
|
atomic_store(&cur[0].state, STATE_RECORD);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0xFC:
|
case 0xFC: {
|
||||||
atomic_store(&channels[0].state, STATE_IDLE);
|
struct channel_t *cur = atomic_load(&channels);
|
||||||
|
atomic_store(&cur[0].state, STATE_IDLE);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case 0xFB: {
|
case 0xFB: {
|
||||||
int s = atomic_load(&channels[0].state);
|
struct channel_t *cur = atomic_load(&channels);
|
||||||
|
int s = atomic_load(&cur[0].state);
|
||||||
if (s == STATE_PAUSED)
|
if (s == STATE_PAUSED)
|
||||||
atomic_store(&channels[0].state, STATE_LOOPING);
|
atomic_store(&cur[0].state, STATE_LOOPING);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -231,23 +263,30 @@ int looper_init(jack_client_t *client) {
|
|||||||
queue_init(&cmd_queue);
|
queue_init(&cmd_queue);
|
||||||
queue_init(&cmd_queue_main_midi);
|
queue_init(&cmd_queue_main_midi);
|
||||||
queue_init(&cmd_queue_main_fifo);
|
queue_init(&cmd_queue_main_fifo);
|
||||||
/* channel 0 */
|
|
||||||
channels[0].active = 1;
|
|
||||||
atomic_store(&channels[0].state, STATE_IDLE);
|
|
||||||
channels[0].prev_state = -1;
|
|
||||||
channels[0].loop_count = 0;
|
|
||||||
channels[0].record_pos = 0;
|
|
||||||
channels[0].playback_pos = 0;
|
|
||||||
|
|
||||||
channels[0].audio_in = jack_port_register(
|
/* allocate initial array for at least one channel */
|
||||||
|
if (ensure_capacity(client, 0) != 0) {
|
||||||
|
fprintf(stderr, "Cannot allocate channel array\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
struct channel_t *init = atomic_load(&channels);
|
||||||
|
/* channel 0 */
|
||||||
|
init[0].active = 1;
|
||||||
|
atomic_store(&init[0].state, STATE_IDLE);
|
||||||
|
init[0].prev_state = -1;
|
||||||
|
init[0].loop_count = 0;
|
||||||
|
init[0].record_pos = 0;
|
||||||
|
init[0].playback_pos = 0;
|
||||||
|
|
||||||
|
init[0].audio_in = jack_port_register(
|
||||||
client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
|
client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
|
||||||
channels[0].audio_out = jack_port_register(
|
init[0].audio_out = jack_port_register(
|
||||||
client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
|
client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
|
||||||
if (!channels[0].audio_in || !channels[0].audio_out) {
|
if (!init[0].audio_in || !init[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;
|
atomic_store(&channel_count, 1);
|
||||||
|
|
||||||
midi_control_port = jack_port_register(
|
midi_control_port = jack_port_register(
|
||||||
client, "control", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
|
client, "control", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
|
||||||
@@ -270,18 +309,25 @@ void looper_process_commands(jack_client_t *client) {
|
|||||||
while (queue_pop(&cmd_queue_main_midi, &cmd)) {
|
while (queue_pop(&cmd_queue_main_midi, &cmd)) {
|
||||||
switch (cmd.type) {
|
switch (cmd.type) {
|
||||||
case CMD_ADD_CHANNEL: {
|
case CMD_ADD_CHANNEL: {
|
||||||
|
int cap = atomic_load(&channel_capacity);
|
||||||
|
struct channel_t *cur = get_channels_array();
|
||||||
int idx;
|
int idx;
|
||||||
for (idx = 0; idx < MAX_CHANNELS; idx++)
|
for (idx = 0; idx < cap; idx++)
|
||||||
if (!channels[idx].active)
|
if (!atomic_load(&cur[idx].active))
|
||||||
break;
|
break;
|
||||||
if (idx < MAX_CHANNELS)
|
if (idx == cap) {
|
||||||
|
if (ensure_capacity(client, idx) != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
channel_add(client, idx);
|
channel_add(client, idx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CMD_REMOVE_CHANNEL: {
|
case CMD_REMOVE_CHANNEL: {
|
||||||
|
int cap = atomic_load(&channel_capacity);
|
||||||
|
struct channel_t *cur = get_channels_array();
|
||||||
int remove_idx = -1;
|
int remove_idx = -1;
|
||||||
for (int idx = 1; idx < MAX_CHANNELS; idx++)
|
for (int idx = 1; idx < cap; idx++)
|
||||||
if (channels[idx].active)
|
if (atomic_load(&cur[idx].active))
|
||||||
remove_idx = idx;
|
remove_idx = idx;
|
||||||
if (remove_idx != -1) {
|
if (remove_idx != -1) {
|
||||||
channel_remove(client, remove_idx);
|
channel_remove(client, remove_idx);
|
||||||
@@ -297,18 +343,25 @@ void looper_process_commands(jack_client_t *client) {
|
|||||||
while (queue_pop(&cmd_queue_main_fifo, &cmd)) {
|
while (queue_pop(&cmd_queue_main_fifo, &cmd)) {
|
||||||
switch (cmd.type) {
|
switch (cmd.type) {
|
||||||
case CMD_ADD_CHANNEL: {
|
case CMD_ADD_CHANNEL: {
|
||||||
|
int cap = atomic_load(&channel_capacity);
|
||||||
|
struct channel_t *cur = get_channels_array();
|
||||||
int idx;
|
int idx;
|
||||||
for (idx = 0; idx < MAX_CHANNELS; idx++)
|
for (idx = 0; idx < cap; idx++)
|
||||||
if (!channels[idx].active)
|
if (!atomic_load(&cur[idx].active))
|
||||||
break;
|
break;
|
||||||
if (idx < MAX_CHANNELS)
|
if (idx == cap) {
|
||||||
|
if (ensure_capacity(client, idx) != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
channel_add(client, idx);
|
channel_add(client, idx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CMD_REMOVE_CHANNEL: {
|
case CMD_REMOVE_CHANNEL: {
|
||||||
|
int cap = atomic_load(&channel_capacity);
|
||||||
|
struct channel_t *cur = get_channels_array();
|
||||||
int remove_idx = -1;
|
int remove_idx = -1;
|
||||||
for (int idx = 1; idx < MAX_CHANNELS; idx++)
|
for (int idx = 1; idx < cap; idx++)
|
||||||
if (channels[idx].active)
|
if (atomic_load(&cur[idx].active))
|
||||||
remove_idx = idx;
|
remove_idx = idx;
|
||||||
if (remove_idx != -1) {
|
if (remove_idx != -1) {
|
||||||
channel_remove(client, remove_idx);
|
channel_remove(client, remove_idx);
|
||||||
@@ -327,10 +380,11 @@ void looper_process_commands(jack_client_t *client) {
|
|||||||
int current_cycle = atomic_load(&global_rt_cycles);
|
int current_cycle = atomic_load(&global_rt_cycles);
|
||||||
if (current_cycle - pending_unregister_cycle >= 1) {
|
if (current_cycle - pending_unregister_cycle >= 1) {
|
||||||
int idx = pending_unregister_idx;
|
int idx = pending_unregister_idx;
|
||||||
if (channels[idx].audio_in)
|
struct channel_t *cur = atomic_load(&channels);
|
||||||
jack_port_unregister(client, channels[idx].audio_in);
|
if (cur[idx].audio_in)
|
||||||
if (channels[idx].audio_out)
|
jack_port_unregister(client, cur[idx].audio_in);
|
||||||
jack_port_unregister(client, channels[idx].audio_out);
|
if (cur[idx].audio_out)
|
||||||
|
jack_port_unregister(client, cur[idx].audio_out);
|
||||||
pending_unregister_idx = -1;
|
pending_unregister_idx = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ void midi_handle_events(void *port_buffer, jack_nframes_t nframes) {
|
|||||||
int ck = atomic_load(&control_key_active);
|
int ck = atomic_load(&control_key_active);
|
||||||
if (ck) {
|
if (ck) {
|
||||||
atomic_store(&control_key_active, 0);
|
atomic_store(&control_key_active, 0);
|
||||||
if (note < 16) {
|
if (note < 16 && note < atomic_load(&channel_capacity)) {
|
||||||
command_t cmd = {
|
command_t cmd = {
|
||||||
.type = CMD_BIND_CHANNEL, .channel = -1, .data = note};
|
.type = CMD_BIND_CHANNEL, .channel = -1, .data = note};
|
||||||
queue_push(&cmd_queue, cmd);
|
queue_push(&cmd_queue, cmd);
|
||||||
@@ -53,7 +53,7 @@ void midi_handle_events(void *port_buffer, jack_nframes_t nframes) {
|
|||||||
} break;
|
} break;
|
||||||
case 62: {
|
case 62: {
|
||||||
int bch = atomic_load(&bind_channel);
|
int bch = atomic_load(&bind_channel);
|
||||||
if (bch >= 0 && bch < MAX_CHANNELS) {
|
if (bch >= 0 && bch < atomic_load(&channel_capacity)) {
|
||||||
command_t cmd = {.type = CMD_CYCLE, .channel = bch, .data = 0};
|
command_t cmd = {.type = CMD_CYCLE, .channel = bch, .data = 0};
|
||||||
queue_push(&cmd_queue, cmd);
|
queue_push(&cmd_queue, cmd);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user