feat: add batch undo support for scene triggers
Co-authored-by: aider (deepseek/deepseek-coder) <aider@aider.chat>
This commit is contained in:
136
dispatcher.c
136
dispatcher.c
@@ -116,21 +116,25 @@ AppState dispatcher_get_state(void) {
|
||||
// Reducer implementation
|
||||
// ============================================================
|
||||
|
||||
static void save_undo_state(AppState *state, int clip_index) {
|
||||
int undo_idx = state->undo.undo_index % MAX_UNDO_HISTORY;
|
||||
state->undo.prev_clip_states[undo_idx] = state->clips[clip_index].state;
|
||||
state->undo.prev_clip_indices[undo_idx] = clip_index;
|
||||
state->undo.prev_buffer_sizes[undo_idx] = state->clips[clip_index].buffer_size;
|
||||
state->undo.prev_write_positions[undo_idx] = state->clips[clip_index].write_position;
|
||||
state->undo.prev_read_positions[undo_idx] = state->clips[clip_index].read_position;
|
||||
state->undo.batch_sizes[undo_idx] = 0; // Will be set by caller
|
||||
state->undo.undo_index++;
|
||||
state->undo.redo_index = state->undo.undo_index;
|
||||
if (state->undo.count < MAX_UNDO_HISTORY) state->undo.count++;
|
||||
}
|
||||
|
||||
static AppState clip_trigger(AppState state, int clip_index) {
|
||||
if (clip_index < 0 || clip_index >= MAX_CLIPS) return state;
|
||||
|
||||
Clip *clip = &state.clips[clip_index];
|
||||
|
||||
// Save undo info
|
||||
int undo_idx = state.undo.undo_index % MAX_UNDO_HISTORY;
|
||||
state.undo.prev_clip_states[undo_idx] = clip->state;
|
||||
state.undo.prev_clip_indices[undo_idx] = clip_index;
|
||||
state.undo.prev_buffer_sizes[undo_idx] = clip->buffer_size;
|
||||
state.undo.prev_write_positions[undo_idx] = clip->write_position;
|
||||
state.undo.prev_read_positions[undo_idx] = clip->read_position;
|
||||
state.undo.undo_index++;
|
||||
state.undo.redo_index = state.undo.undo_index;
|
||||
if (state.undo.count < MAX_UNDO_HISTORY) state.undo.count++;
|
||||
// Do NOT save undo here - caller will do it
|
||||
|
||||
switch (clip->state) {
|
||||
case CLIP_EMPTY:
|
||||
@@ -160,6 +164,20 @@ static AppState clip_trigger(AppState state, int clip_index) {
|
||||
static AppState scene_trigger(AppState state, int scene_index) {
|
||||
if (scene_index < 0 || scene_index >= MAX_SCENES) return state;
|
||||
|
||||
// Save undo info for all clips in the scene as a batch
|
||||
int batch_start = state.undo.undo_index;
|
||||
for (int ch = 0; ch < MAX_CHANNELS; ch++) {
|
||||
int clip_idx = CLIP_INDEX(scene_index, ch);
|
||||
save_undo_state(&state, clip_idx);
|
||||
}
|
||||
// Mark all entries in this batch with the batch size
|
||||
int batch_end = state.undo.undo_index;
|
||||
for (int i = batch_start; i < batch_end; i++) {
|
||||
int idx = i % MAX_UNDO_HISTORY;
|
||||
state.undo.batch_sizes[idx] = batch_end - batch_start;
|
||||
}
|
||||
|
||||
// Now apply the changes
|
||||
for (int ch = 0; ch < MAX_CHANNELS; ch++) {
|
||||
int clip_idx = CLIP_INDEX(scene_index, ch);
|
||||
state = clip_trigger(state, clip_idx);
|
||||
@@ -173,16 +191,7 @@ static AppState clip_reset(AppState state, int clip_index) {
|
||||
|
||||
Clip *clip = &state.clips[clip_index];
|
||||
|
||||
// Save undo info
|
||||
int undo_idx = state.undo.undo_index % MAX_UNDO_HISTORY;
|
||||
state.undo.prev_clip_states[undo_idx] = clip->state;
|
||||
state.undo.prev_clip_indices[undo_idx] = clip_index;
|
||||
state.undo.prev_buffer_sizes[undo_idx] = clip->buffer_size;
|
||||
state.undo.prev_write_positions[undo_idx] = clip->write_position;
|
||||
state.undo.prev_read_positions[undo_idx] = clip->read_position;
|
||||
state.undo.undo_index++;
|
||||
state.undo.redo_index = state.undo.undo_index;
|
||||
if (state.undo.count < MAX_UNDO_HISTORY) state.undo.count++;
|
||||
save_undo_state(&state, clip_index);
|
||||
|
||||
clip->state = CLIP_EMPTY;
|
||||
clip->buffer_size = 0;
|
||||
@@ -196,60 +205,79 @@ static AppState clip_reset(AppState state, int clip_index) {
|
||||
static AppState undo_action(AppState state) {
|
||||
if (state.undo.undo_index <= 0) return state;
|
||||
|
||||
// Get the batch size for the current undo entry
|
||||
int undo_idx = (state.undo.undo_index - 1) % MAX_UNDO_HISTORY;
|
||||
int clip_idx = state.undo.prev_clip_indices[undo_idx];
|
||||
int batch_size = state.undo.batch_sizes[undo_idx];
|
||||
if (batch_size == 0) batch_size = 1; // Single clip operation
|
||||
|
||||
if (clip_idx >= 0 && clip_idx < MAX_CLIPS) {
|
||||
Clip *clip = &state.clips[clip_idx];
|
||||
clip->state = state.undo.prev_clip_states[undo_idx];
|
||||
clip->buffer_size = state.undo.prev_buffer_sizes[undo_idx];
|
||||
clip->write_position = state.undo.prev_write_positions[undo_idx];
|
||||
clip->read_position = state.undo.prev_read_positions[undo_idx];
|
||||
// Undo all clips in the batch
|
||||
for (int i = 0; i < batch_size; i++) {
|
||||
int current_idx = (state.undo.undo_index - 1 - i) % MAX_UNDO_HISTORY;
|
||||
int clip_idx = state.undo.prev_clip_indices[current_idx];
|
||||
|
||||
if (clip_idx >= 0 && clip_idx < MAX_CLIPS) {
|
||||
Clip *clip = &state.clips[clip_idx];
|
||||
clip->state = state.undo.prev_clip_states[current_idx];
|
||||
clip->buffer_size = state.undo.prev_buffer_sizes[current_idx];
|
||||
clip->write_position = state.undo.prev_write_positions[current_idx];
|
||||
clip->read_position = state.undo.prev_read_positions[current_idx];
|
||||
}
|
||||
}
|
||||
|
||||
state.undo.undo_index--;
|
||||
state.undo.undo_index -= batch_size;
|
||||
return state;
|
||||
}
|
||||
|
||||
static AppState redo_action(AppState state) {
|
||||
if (state.undo.redo_index <= state.undo.undo_index) return state;
|
||||
|
||||
// Get the batch size for the next redo entry
|
||||
int redo_idx = state.undo.undo_index % MAX_UNDO_HISTORY;
|
||||
int clip_idx = state.undo.prev_clip_indices[redo_idx];
|
||||
int batch_size = state.undo.batch_sizes[redo_idx];
|
||||
if (batch_size == 0) batch_size = 1;
|
||||
|
||||
if (clip_idx >= 0 && clip_idx < MAX_CLIPS) {
|
||||
Clip *clip = &state.clips[clip_idx];
|
||||
switch (clip->state) {
|
||||
case CLIP_EMPTY:
|
||||
clip->state = CLIP_RECORDING;
|
||||
clip->write_position = 0;
|
||||
clip->buffer_size = 0;
|
||||
clip->read_position = 0;
|
||||
break;
|
||||
case CLIP_RECORDING:
|
||||
clip->state = CLIP_LOOPING;
|
||||
clip->buffer_size = clip->write_position;
|
||||
clip->read_position = 0;
|
||||
break;
|
||||
case CLIP_LOOPING:
|
||||
clip->state = CLIP_STOPPED;
|
||||
clip->read_position = 0;
|
||||
break;
|
||||
case CLIP_STOPPED:
|
||||
clip->state = CLIP_LOOPING;
|
||||
clip->read_position = 0;
|
||||
break;
|
||||
// Redo all clips in the batch
|
||||
for (int i = 0; i < batch_size; i++) {
|
||||
int current_idx = (state.undo.undo_index + i) % MAX_UNDO_HISTORY;
|
||||
int clip_idx = state.undo.prev_clip_indices[current_idx];
|
||||
|
||||
if (clip_idx >= 0 && clip_idx < MAX_CLIPS) {
|
||||
Clip *clip = &state.clips[clip_idx];
|
||||
switch (clip->state) {
|
||||
case CLIP_EMPTY:
|
||||
clip->state = CLIP_RECORDING;
|
||||
clip->write_position = 0;
|
||||
clip->buffer_size = 0;
|
||||
clip->read_position = 0;
|
||||
break;
|
||||
case CLIP_RECORDING:
|
||||
clip->state = CLIP_LOOPING;
|
||||
clip->buffer_size = clip->write_position;
|
||||
clip->read_position = 0;
|
||||
break;
|
||||
case CLIP_LOOPING:
|
||||
clip->state = CLIP_STOPPED;
|
||||
clip->read_position = 0;
|
||||
break;
|
||||
case CLIP_STOPPED:
|
||||
clip->state = CLIP_LOOPING;
|
||||
clip->read_position = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.undo.undo_index++;
|
||||
state.undo.undo_index += batch_size;
|
||||
return state;
|
||||
}
|
||||
|
||||
AppState reducer(AppState state, Action action) {
|
||||
switch (action.type) {
|
||||
case ACTION_TRIGGER_CLIP:
|
||||
return clip_trigger(state, action.data.trigger_clip.clip_index);
|
||||
case ACTION_TRIGGER_CLIP: {
|
||||
int clip_idx = action.data.trigger_clip.clip_index;
|
||||
save_undo_state(&state, clip_idx);
|
||||
return clip_trigger(state, clip_idx);
|
||||
}
|
||||
|
||||
case ACTION_TRIGGER_SCENE:
|
||||
return scene_trigger(state, action.data.trigger_scene.scene_index);
|
||||
|
||||
@@ -81,6 +81,8 @@ typedef struct {
|
||||
size_t prev_buffer_sizes[MAX_UNDO_HISTORY];
|
||||
size_t prev_write_positions[MAX_UNDO_HISTORY];
|
||||
size_t prev_read_positions[MAX_UNDO_HISTORY];
|
||||
int batch_sizes[MAX_UNDO_HISTORY]; // NEW: track batch sizes
|
||||
int current_batch_size; // NEW: current batch being recorded
|
||||
} undo;
|
||||
|
||||
// Carla host
|
||||
|
||||
@@ -40,8 +40,10 @@ static AppState create_test_state(void) {
|
||||
state.undo.undo_index = 0;
|
||||
state.undo.redo_index = 0;
|
||||
state.undo.count = 0;
|
||||
state.undo.current_batch_size = 0;
|
||||
for (int i = 0; i < MAX_UNDO_HISTORY; i++) {
|
||||
state.undo.prev_clip_indices[i] = -1;
|
||||
state.undo.batch_sizes[i] = 0;
|
||||
}
|
||||
|
||||
// JACK info
|
||||
|
||||
Reference in New Issue
Block a user