fix: make channel state variables atomic to eliminate data races

Co-authored-by: aider (deepseek/deepseek-reasoner) <aider@aider.chat>
This commit is contained in:
Loic Coenen
2026-05-12 19:32:10 +00:00
parent 87d5e658c5
commit ce2dd7be76
2 changed files with 21 additions and 21 deletions

View File

@@ -19,11 +19,11 @@ typedef enum {
struct channel_t {
atomic_int state;
int prev_state;
atomic_int prev_state;
float loop_buffer[LOOP_BUF_SIZE];
atomic_int loop_count;
int record_pos;
int playback_pos;
atomic_int record_pos;
atomic_int playback_pos;
atomic_int active;
jack_port_t *audio_in;
jack_port_t *audio_out;

View File

@@ -68,16 +68,16 @@ int process_callback(jack_nframes_t nframes, void *arg) {
int state = atomic_load(&channels[c].state);
if (state != channels[c].prev_state) {
if (state != atomic_load(&channels[c].prev_state)) {
switch (state) {
case STATE_RECORD:
channels[c].record_pos = 0;
atomic_store(&channels[c].record_pos, 0);
atomic_store(&channels[c].loop_count, 0);
break;
case STATE_LOOPING:
if (channels[c].prev_state == STATE_RECORD && channels[c].record_pos > 0)
atomic_store(&channels[c].loop_count, channels[c].record_pos);
channels[c].playback_pos = 0;
if (atomic_load(&channels[c].prev_state) == STATE_RECORD && atomic_load(&channels[c].record_pos) > 0)
atomic_store(&channels[c].loop_count, atomic_load(&channels[c].record_pos));
atomic_store(&channels[c].playback_pos, 0);
break;
default:
break;
@@ -91,9 +91,9 @@ int process_callback(jack_nframes_t nframes, void *arg) {
float *f_out = (float *)out;
const float *f_in = (const float *)in;
for (i = 0; i < nframes; i++) {
if (channels[c].record_pos < LOOP_BUF_SIZE)
channels[c].loop_buffer[channels[c].record_pos++] =
f_in[i];
int rp = atomic_fetch_add(&channels[c].record_pos, 1);
if (rp < LOOP_BUF_SIZE)
channels[c].loop_buffer[rp] = f_in[i];
f_out[i] = f_in[i];
}
} else {
@@ -106,9 +106,9 @@ int process_callback(jack_nframes_t nframes, void *arg) {
if (lc > 0) {
float *outf = (float *)out;
for (i = 0; i < nframes; i++) {
outf[i] = channels[c].loop_buffer[channels[c].playback_pos];
channels[c].playback_pos =
(channels[c].playback_pos + 1) % lc;
int pp = atomic_load(&channels[c].playback_pos);
outf[i] = channels[c].loop_buffer[pp];
atomic_store(&channels[c].playback_pos, (pp + 1) % lc);
}
} else {
memset(out, 0, sizeof(jack_default_audio_sample_t) * nframes);
@@ -137,7 +137,7 @@ int process_callback(jack_nframes_t nframes, void *arg) {
}
}
channels[c].prev_state = state;
atomic_store(&channels[c].prev_state, state);
}
/* MIDI clock events affect channel 0 only */
@@ -197,10 +197,10 @@ int looper_init(jack_client_t *client) {
/* channel 0 */
channels[0].active = 1;
atomic_store(&channels[0].state, STATE_IDLE);
channels[0].prev_state = -1;
atomic_store(&channels[0].prev_state, -1);
channels[0].loop_count = 0;
channels[0].record_pos = 0;
channels[0].playback_pos = 0;
atomic_store(&channels[0].record_pos, 0);
atomic_store(&channels[0].playback_pos, 0);
atomic_store_explicit(&channels[0].save_ring, NULL, memory_order_release);
channels[0].audio_in = jack_port_register(
@@ -312,10 +312,10 @@ void looper_process_commands(jack_client_t *client) {
if (frames > LOOP_BUF_SIZE) frames = LOOP_BUF_SIZE;
memcpy(channels[0].loop_buffer, buf, frames * sizeof(float));
atomic_store(&channels[0].loop_count, (int)frames);
channels[0].record_pos = 0;
channels[0].playback_pos = 0;
atomic_store(&channels[0].record_pos, 0);
atomic_store(&channels[0].playback_pos, 0);
atomic_store(&channels[0].state, STATE_LOOPING);
channels[0].prev_state = -1;
atomic_store(&channels[0].prev_state, -1);
free(buf);
} else {
fprintf(stderr, "Failed to load loop.wav\n");