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:
92
engine.c
92
engine.c
@@ -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));
|
||||
|
||||
4
engine.h
4
engine.h
@@ -32,9 +32,9 @@ typedef enum {
|
||||
|
||||
|
||||
typedef struct {
|
||||
ClipState state;
|
||||
atomic_int state; // ClipState (atomic for thread-safe access)
|
||||
float *buffer;
|
||||
size_t buffer_size;
|
||||
atomic_size_t buffer_size; // size_t (atomic for thread-safe access)
|
||||
size_t write_position;
|
||||
size_t read_position;
|
||||
bool is_playing;
|
||||
|
||||
8
tui.c
8
tui.c
@@ -4,6 +4,7 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
#define GRID_ROWS 8
|
||||
#define GRID_COLS 8
|
||||
@@ -215,7 +216,7 @@ static void draw_cell(int row, int col, bool selected) {
|
||||
int y = row * CELL_HEIGHT + 1;
|
||||
int x = col * CELL_WIDTH + 1;
|
||||
|
||||
int color = state_to_color(clip->state);
|
||||
int color = state_to_color((ClipState)atomic_load(&clip->state));
|
||||
if (selected) {
|
||||
color = COLOR_SELECTED;
|
||||
} else if (current_mode == MODE_VISUAL && is_in_visual_selection(row, col)) {
|
||||
@@ -237,7 +238,7 @@ static void draw_cell(int row, int col, bool selected) {
|
||||
|
||||
// Draw state indicator
|
||||
char state_char;
|
||||
switch (clip->state) {
|
||||
switch ((ClipState)atomic_load(&clip->state)) {
|
||||
case CLIP_EMPTY: state_char = ' '; break;
|
||||
case CLIP_RECORDING: state_char = 'R'; break;
|
||||
case CLIP_LOOPING: state_char = 'L'; break;
|
||||
@@ -272,7 +273,8 @@ static void draw_grid(void) {
|
||||
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 1, 0,
|
||||
"Selected: Clip %d | State: %s | Buffer: %zu samples",
|
||||
clip_idx, clip_state_to_string(clip->state), clip->buffer_size);
|
||||
clip_idx, clip_state_to_string((ClipState)atomic_load(&clip->state)),
|
||||
(size_t)atomic_load(&clip->buffer_size));
|
||||
|
||||
TransportState transport_state = (TransportState)atomic_load(&g_engine->transport->state_atomic);
|
||||
ClockSource clock_source = (ClockSource)atomic_load(&g_engine->transport->clock_source_atomic);
|
||||
|
||||
Reference in New Issue
Block a user