fix: use atomic operations for thread-safe clip state access

Co-authored-by: aider (deepseek/deepseek-coder) <aider@aider.chat>
This commit is contained in:
Loic Coenen
2026-05-02 11:09:17 +00:00
parent 42ecd94d83
commit a8223baf43
3 changed files with 53 additions and 51 deletions

View File

@@ -134,19 +134,19 @@ static int process_callback(jack_nframes_t nframes, void *arg) {
int clip_idx = CLIP_INDEX(s, ch);
Clip *clip = &engine->clips[clip_idx];
if (clip->state == CLIP_RECORDING) {
if ((ClipState)atomic_load(&clip->state) == CLIP_RECORDING) {
if (clip->write_position < MAX_BUFFER_SIZE) {
clip->buffer[clip->write_position++] = audio_in[ch][i];
} else {
// Buffer full, stop recording
clip->state = CLIP_LOOPING;
clip->buffer_size = clip->write_position;
atomic_store(&clip->state, CLIP_LOOPING);
atomic_store(&clip->buffer_size, clip->write_position);
clip->read_position = 0;
}
}
// Play looping clips to this channel's output
if (clip->state == CLIP_LOOPING && clip->buffer_size > 0) {
if ((ClipState)atomic_load(&clip->state) == CLIP_LOOPING && atomic_load(&clip->buffer_size) > 0) {
audio_out[ch][i] += clip->buffer[clip->read_position];
clip->read_position = (clip->read_position + 1) % clip->buffer_size;
}
@@ -469,32 +469,32 @@ void engine_process_commands(Engine *engine) {
action.previous_read_position = clip->read_position;
engine_push_undo_action(engine, &action);
ClipState prev_state = clip->state;
ClipState prev_state = (ClipState)atomic_load(&clip->state);
switch (clip->state) {
switch (prev_state) {
case CLIP_EMPTY:
clip->state = CLIP_RECORDING;
atomic_store(&clip->state, CLIP_RECORDING);
clip->write_position = 0;
clip->buffer_size = 0;
atomic_store(&clip->buffer_size, 0);
clip->read_position = 0;
break;
case CLIP_RECORDING:
clip->state = CLIP_LOOPING;
clip->buffer_size = clip->write_position;
atomic_store(&clip->state, CLIP_LOOPING);
atomic_store(&clip->buffer_size, clip->write_position);
clip->read_position = 0;
break;
case CLIP_LOOPING:
clip->state = CLIP_STOPPED;
atomic_store(&clip->state, CLIP_STOPPED);
clip->read_position = 0;
break;
case CLIP_STOPPED:
clip->state = CLIP_LOOPING;
atomic_store(&clip->state, CLIP_LOOPING);
clip->read_position = 0;
break;
}
// Auto-save when recording finishes (RECORDING -> LOOPING)
if (prev_state == CLIP_RECORDING && clip->state == CLIP_LOOPING) {
if (prev_state == CLIP_RECORDING && (ClipState)atomic_load(&clip->state) == CLIP_LOOPING) {
save_load_queue_push(&engine->save_load_queue, REQ_SAVE_CLIP, cmd.index, "");
}
break;
@@ -514,32 +514,32 @@ void engine_process_commands(Engine *engine) {
int clip_idx = CLIP_INDEX(cmd.index, ch);
Clip *clip = &engine->clips[clip_idx];
ClipState prev_state = clip->state;
ClipState prev_state = (ClipState)atomic_load(&clip->state);
switch (clip->state) {
switch (prev_state) {
case CLIP_EMPTY:
clip->state = CLIP_RECORDING;
atomic_store(&clip->state, CLIP_RECORDING);
clip->write_position = 0;
clip->buffer_size = 0;
atomic_store(&clip->buffer_size, 0);
clip->read_position = 0;
break;
case CLIP_RECORDING:
clip->state = CLIP_LOOPING;
clip->buffer_size = clip->write_position;
atomic_store(&clip->state, CLIP_LOOPING);
atomic_store(&clip->buffer_size, clip->write_position);
clip->read_position = 0;
break;
case CLIP_LOOPING:
clip->state = CLIP_STOPPED;
atomic_store(&clip->state, CLIP_STOPPED);
clip->read_position = 0;
break;
case CLIP_STOPPED:
clip->state = CLIP_LOOPING;
atomic_store(&clip->state, CLIP_LOOPING);
clip->read_position = 0;
break;
}
// Auto-save when recording finishes
if (prev_state == CLIP_RECORDING && clip->state == CLIP_LOOPING) {
if (prev_state == CLIP_RECORDING && (ClipState)atomic_load(&clip->state) == CLIP_LOOPING) {
save_load_queue_push(&engine->save_load_queue, REQ_SAVE_CLIP, clip_idx, "");
}
}
@@ -562,8 +562,8 @@ void engine_process_commands(Engine *engine) {
action.previous_read_position = clip->read_position;
engine_push_undo_action(engine, &action);
clip->state = CLIP_EMPTY;
clip->buffer_size = 0;
atomic_store(&clip->state, CLIP_EMPTY);
atomic_store(&clip->buffer_size, 0);
clip->write_position = 0;
clip->read_position = 0;
memset(clip->buffer, 0, MAX_BUFFER_SIZE * sizeof(float));
@@ -732,8 +732,8 @@ void engine_undo(Engine *engine) {
if (clip_idx < 0 || clip_idx >= MAX_CLIPS) break;
Clip *clip = &engine->clips[clip_idx];
if (!clip->buffer) break; // ADD THIS
clip->state = action->previous_state;
clip->buffer_size = action->previous_buffer_size;
atomic_store(&clip->state, action->previous_state);
atomic_store(&clip->buffer_size, action->previous_buffer_size);
clip->write_position = action->previous_write_position;
clip->read_position = action->previous_read_position;
break;
@@ -747,8 +747,8 @@ void engine_undo(Engine *engine) {
if (clip_idx < 0 || clip_idx >= MAX_CLIPS) continue;
Clip *clip = &engine->clips[clip_idx];
if (!clip->buffer) continue;
clip->state = CLIP_EMPTY;
clip->buffer_size = 0;
atomic_store(&clip->state, CLIP_EMPTY);
atomic_store(&clip->buffer_size, 0);
clip->write_position = 0;
clip->read_position = 0;
}
@@ -760,8 +760,8 @@ void engine_undo(Engine *engine) {
if (clip_idx < 0 || clip_idx >= MAX_CLIPS) break;
Clip *clip = &engine->clips[clip_idx];
if (!clip->buffer) break;
clip->state = action->previous_state;
clip->buffer_size = action->previous_buffer_size;
atomic_store(&clip->state, action->previous_state);
atomic_store(&clip->buffer_size, action->previous_buffer_size);
clip->write_position = action->previous_write_position;
clip->read_position = action->previous_read_position;
break;
@@ -829,24 +829,24 @@ void engine_redo(Engine *engine) {
Clip *clip = &engine->clips[clip_idx];
if (!clip->buffer) break; // ADD THIS
// Re-apply the trigger by directly manipulating state
switch (clip->state) {
switch ((ClipState)atomic_load(&clip->state)) {
case CLIP_EMPTY:
clip->state = CLIP_RECORDING;
atomic_store(&clip->state, CLIP_RECORDING);
clip->write_position = 0;
clip->buffer_size = 0;
atomic_store(&clip->buffer_size, 0);
clip->read_position = 0;
break;
case CLIP_RECORDING:
clip->state = CLIP_LOOPING;
clip->buffer_size = clip->write_position;
atomic_store(&clip->state, CLIP_LOOPING);
atomic_store(&clip->buffer_size, clip->write_position);
clip->read_position = 0;
break;
case CLIP_LOOPING:
clip->state = CLIP_STOPPED;
atomic_store(&clip->state, CLIP_STOPPED);
clip->read_position = 0;
break;
case CLIP_STOPPED:
clip->state = CLIP_LOOPING;
atomic_store(&clip->state, CLIP_LOOPING);
clip->read_position = 0;
break;
}
@@ -861,24 +861,24 @@ void engine_redo(Engine *engine) {
if (clip_idx < 0 || clip_idx >= MAX_CLIPS) continue;
Clip *clip = &engine->clips[clip_idx];
if (!clip->buffer) continue;
switch (clip->state) {
switch ((ClipState)atomic_load(&clip->state)) {
case CLIP_EMPTY:
clip->state = CLIP_RECORDING;
atomic_store(&clip->state, CLIP_RECORDING);
clip->write_position = 0;
clip->buffer_size = 0;
atomic_store(&clip->buffer_size, 0);
clip->read_position = 0;
break;
case CLIP_RECORDING:
clip->state = CLIP_LOOPING;
clip->buffer_size = clip->write_position;
atomic_store(&clip->state, CLIP_LOOPING);
atomic_store(&clip->buffer_size, clip->write_position);
clip->read_position = 0;
break;
case CLIP_LOOPING:
clip->state = CLIP_STOPPED;
atomic_store(&clip->state, CLIP_STOPPED);
clip->read_position = 0;
break;
case CLIP_STOPPED:
clip->state = CLIP_LOOPING;
atomic_store(&clip->state, CLIP_LOOPING);
clip->read_position = 0;
break;
}
@@ -891,8 +891,8 @@ void engine_redo(Engine *engine) {
if (clip_idx < 0 || clip_idx >= MAX_CLIPS) break;
Clip *clip = &engine->clips[clip_idx];
if (!clip->buffer) break;
clip->state = CLIP_EMPTY;
clip->buffer_size = 0;
atomic_store(&clip->state, CLIP_EMPTY);
atomic_store(&clip->buffer_size, 0);
clip->write_position = 0;
clip->read_position = 0;
memset(clip->buffer, 0, MAX_BUFFER_SIZE * sizeof(float));