feat: add parallel MIDI grid with separate clip storage and view toggle

Co-authored-by: aider (deepseek/deepseek-coder) <aider@aider.chat>
This commit is contained in:
Loic Coenen
2026-05-03 18:49:21 +00:00
parent 5e4d4e4d44
commit 61ab2f0b19
6 changed files with 216 additions and 25 deletions

View File

@@ -167,6 +167,34 @@ static AppState clip_trigger(AppState state, int clip_index) {
return state;
}
static AppState midi_clip_trigger(AppState state, int clip_index) {
if (clip_index < 0 || clip_index >= MAX_CLIPS) return state;
MidiClip *clip = &state.midi_clips[clip_index];
switch (clip->state) {
case CLIP_EMPTY:
clip->state = CLIP_RECORDING;
clip->event_count = 0;
clip->read_index = 0;
break;
case CLIP_RECORDING:
clip->state = CLIP_LOOPING;
clip->read_index = 0;
break;
case CLIP_LOOPING:
clip->state = CLIP_STOPPED;
clip->read_index = 0;
break;
case CLIP_STOPPED:
clip->state = CLIP_LOOPING;
clip->read_index = 0;
break;
}
return state;
}
static AppState scene_trigger(AppState state, int scene_index) {
if (scene_index < 0 || scene_index >= MAX_SCENES * 8) return state; // 8 grids
@@ -454,6 +482,32 @@ AppState reducer(AppState state, Action action) {
case ACTION_PROCESS_AUDIO:
return state;
case ACTION_SET_SHOW_MIDI_GRID:
state.show_midi_grid = action.data.set_show_midi_grid.show;
return state;
case ACTION_MIDI_CLIP_TRIGGER:
return midi_clip_trigger(state, action.data.midi_clip_trigger.clip_index);
case ACTION_MIDI_CLIP_RESET: {
int idx = action.data.midi_clip_reset.clip_index;
if (idx >= 0 && idx < MAX_CLIPS) {
state.midi_clips[idx].state = CLIP_EMPTY;
state.midi_clips[idx].event_count = 0;
state.midi_clips[idx].read_index = 0;
}
return state;
}
case ACTION_SET_CHANNEL_NAME: {
int ch = action.data.set_channel_name.channel;
if (ch >= 0 && ch < MAX_CHANNELS) {
strncpy(state.channel_names[ch], action.data.set_channel_name.name, 63);
state.channel_names[ch][63] = '\0';
}
return state;
}
case ACTION_SAVE_PROJECT: {
fs_save_project(action.data.save_project.filename, &state);
return state;