feat: add batch undo support for scene triggers
Co-authored-by: aider (deepseek/deepseek-coder) <aider@aider.chat>
This commit is contained in:
88
dispatcher.c
88
dispatcher.c
@@ -116,21 +116,25 @@ AppState dispatcher_get_state(void) {
|
|||||||
// Reducer implementation
|
// 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) {
|
static AppState clip_trigger(AppState state, int clip_index) {
|
||||||
if (clip_index < 0 || clip_index >= MAX_CLIPS) return state;
|
if (clip_index < 0 || clip_index >= MAX_CLIPS) return state;
|
||||||
|
|
||||||
Clip *clip = &state.clips[clip_index];
|
Clip *clip = &state.clips[clip_index];
|
||||||
|
|
||||||
// Save undo info
|
// Do NOT save undo here - caller will do it
|
||||||
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++;
|
|
||||||
|
|
||||||
switch (clip->state) {
|
switch (clip->state) {
|
||||||
case CLIP_EMPTY:
|
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) {
|
static AppState scene_trigger(AppState state, int scene_index) {
|
||||||
if (scene_index < 0 || scene_index >= MAX_SCENES) return state;
|
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++) {
|
for (int ch = 0; ch < MAX_CHANNELS; ch++) {
|
||||||
int clip_idx = CLIP_INDEX(scene_index, ch);
|
int clip_idx = CLIP_INDEX(scene_index, ch);
|
||||||
state = clip_trigger(state, clip_idx);
|
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];
|
Clip *clip = &state.clips[clip_index];
|
||||||
|
|
||||||
// Save undo info
|
save_undo_state(&state, clip_index);
|
||||||
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++;
|
|
||||||
|
|
||||||
clip->state = CLIP_EMPTY;
|
clip->state = CLIP_EMPTY;
|
||||||
clip->buffer_size = 0;
|
clip->buffer_size = 0;
|
||||||
@@ -196,26 +205,41 @@ static AppState clip_reset(AppState state, int clip_index) {
|
|||||||
static AppState undo_action(AppState state) {
|
static AppState undo_action(AppState state) {
|
||||||
if (state.undo.undo_index <= 0) return 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 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
|
||||||
|
|
||||||
|
// 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) {
|
if (clip_idx >= 0 && clip_idx < MAX_CLIPS) {
|
||||||
Clip *clip = &state.clips[clip_idx];
|
Clip *clip = &state.clips[clip_idx];
|
||||||
clip->state = state.undo.prev_clip_states[undo_idx];
|
clip->state = state.undo.prev_clip_states[current_idx];
|
||||||
clip->buffer_size = state.undo.prev_buffer_sizes[undo_idx];
|
clip->buffer_size = state.undo.prev_buffer_sizes[current_idx];
|
||||||
clip->write_position = state.undo.prev_write_positions[undo_idx];
|
clip->write_position = state.undo.prev_write_positions[current_idx];
|
||||||
clip->read_position = state.undo.prev_read_positions[undo_idx];
|
clip->read_position = state.undo.prev_read_positions[current_idx];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.undo.undo_index--;
|
state.undo.undo_index -= batch_size;
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static AppState redo_action(AppState state) {
|
static AppState redo_action(AppState state) {
|
||||||
if (state.undo.redo_index <= state.undo.undo_index) return 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 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;
|
||||||
|
|
||||||
|
// 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) {
|
if (clip_idx >= 0 && clip_idx < MAX_CLIPS) {
|
||||||
Clip *clip = &state.clips[clip_idx];
|
Clip *clip = &state.clips[clip_idx];
|
||||||
@@ -241,15 +265,19 @@ static AppState redo_action(AppState state) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state.undo.undo_index++;
|
state.undo.undo_index += batch_size;
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppState reducer(AppState state, Action action) {
|
AppState reducer(AppState state, Action action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ACTION_TRIGGER_CLIP:
|
case ACTION_TRIGGER_CLIP: {
|
||||||
return clip_trigger(state, action.data.trigger_clip.clip_index);
|
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:
|
case ACTION_TRIGGER_SCENE:
|
||||||
return scene_trigger(state, action.data.trigger_scene.scene_index);
|
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_buffer_sizes[MAX_UNDO_HISTORY];
|
||||||
size_t prev_write_positions[MAX_UNDO_HISTORY];
|
size_t prev_write_positions[MAX_UNDO_HISTORY];
|
||||||
size_t prev_read_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;
|
} undo;
|
||||||
|
|
||||||
// Carla host
|
// Carla host
|
||||||
|
|||||||
@@ -40,8 +40,10 @@ static AppState create_test_state(void) {
|
|||||||
state.undo.undo_index = 0;
|
state.undo.undo_index = 0;
|
||||||
state.undo.redo_index = 0;
|
state.undo.redo_index = 0;
|
||||||
state.undo.count = 0;
|
state.undo.count = 0;
|
||||||
|
state.undo.current_batch_size = 0;
|
||||||
for (int i = 0; i < MAX_UNDO_HISTORY; i++) {
|
for (int i = 0; i < MAX_UNDO_HISTORY; i++) {
|
||||||
state.undo.prev_clip_indices[i] = -1;
|
state.undo.prev_clip_indices[i] = -1;
|
||||||
|
state.undo.batch_sizes[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// JACK info
|
// JACK info
|
||||||
|
|||||||
Reference in New Issue
Block a user