// cppcheck-suppress missingIncludeSystem #include "channel.h" #include #include #include #include #include /* Helper: zero a scene and set its state to IDLE */ void init_scene(scene_t *sc) { memset(sc, 0, sizeof(scene_t)); atomic_store(&sc->state, STATE_IDLE); atomic_store(&sc->prev_state, -1); } void channel_add(jack_client_t *client, int idx) { char in_name[64], out_name[64]; pid_t pid = getpid(); snprintf(in_name, sizeof(in_name), "ch%din", next_channel_id); snprintf(out_name, sizeof(out_name), "ch%dout", next_channel_id); /* Always register audio ports (needed for pass-through even for MIDI * channels?) */ channels[idx].audio_in = jack_port_register( client, in_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); channels[idx].audio_out = jack_port_register( client, out_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); if (!channels[idx].audio_in || !channels[idx].audio_out) { fprintf(stderr, "Failed to register ports for channel %d\n", next_channel_id); /* Do NOT mark channel active – process loop will skip it */ atomic_store(&channels[idx].active, 0); return; } /* If this is a MIDI channel, register MIDI ports */ if (channels[idx].type == CHANNEL_MIDI) { char midi_in_name[64], midi_out_name[64]; snprintf(midi_in_name, sizeof(midi_in_name), "ch%dmidiin", next_channel_id); snprintf(midi_out_name, sizeof(midi_out_name), "ch%dmidiout", next_channel_id); channels[idx].midi_in = jack_port_register( client, midi_in_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); channels[idx].midi_out = jack_port_register( client, midi_out_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); if (!channels[idx].midi_in || !channels[idx].midi_out) { fprintf(stderr, "Failed to register MIDI ports for channel %d\n", next_channel_id); atomic_store(&channels[idx].active, 0); jack_port_unregister(client, channels[idx].audio_in); jack_port_unregister(client, channels[idx].audio_out); return; } } else { channels[idx].midi_in = NULL; channels[idx].midi_out = NULL; } atomic_store(&channels[idx].active, 1); /* Initialise first scene */ channels[idx].scene_count = 1; channels[idx].current_scene = 0; init_scene(&channels[idx].scenes[0]); channels[idx].save_ring = NULL; atomic_store(&channels[idx].save_complete, 0); next_channel_id++; channel_count++; } void channel_remove(jack_client_t *client, int idx) { (void)client; atomic_store_explicit(&channels[idx].active, 0, memory_order_release); atomic_fetch_sub_explicit(&channel_count, 1, memory_order_release); } void channel_add_scene(jack_client_t *client, int idx) { (void)client; if (atomic_load(&channels[idx].scene_count) >= MAX_SCENES) return; int ns = atomic_load(&channels[idx].scene_count); init_scene(&channels[idx].scenes[ns]); atomic_fetch_add(&channels[idx].scene_count, 1); } void channel_remove_scene(jack_client_t *client, int idx) { (void)client; int sc = atomic_load(&channels[idx].scene_count); if (sc <= 1) return; int cs = atomic_load(&channels[idx].current_scene); /* shift remaining scenes down (atomic copy of fields) */ for (int i = cs; i < sc - 1; i++) { atomic_store(&channels[idx].scenes[i].loop_count, atomic_load(&channels[idx].scenes[i + 1].loop_count)); atomic_store(&channels[idx].scenes[i].record_pos, atomic_load(&channels[idx].scenes[i + 1].record_pos)); atomic_store(&channels[idx].scenes[i].playback_pos, atomic_load(&channels[idx].scenes[i + 1].playback_pos)); atomic_store(&channels[idx].scenes[i].state, atomic_load(&channels[idx].scenes[i + 1].state)); atomic_store(&channels[idx].scenes[i].prev_state, atomic_load(&channels[idx].scenes[i + 1].prev_state)); /* copy loop data (may race with RT thread; acceptable for this release) */ memcpy(channels[idx].scenes[i].loop.audio_buffer, channels[idx].scenes[i + 1].loop.audio_buffer, LOOP_BUF_SIZE * sizeof(float)); } atomic_fetch_sub(&channels[idx].scene_count, 1); int new_sc = atomic_load(&channels[idx].scene_count); if (cs >= new_sc) atomic_store(&channels[idx].current_scene, new_sc - 1); } void channel_next_scene(jack_client_t *client, int idx) { (void)client; int sc = atomic_load(&channels[idx].scene_count); if (sc > 1) { int cs = atomic_load(&channels[idx].current_scene); atomic_store(&channels[idx].current_scene, (cs + 1) % sc); } } void channel_prev_scene(jack_client_t *client, int idx) { (void)client; int sc = atomic_load(&channels[idx].scene_count); if (sc > 1) { int cs = atomic_load(&channels[idx].current_scene); atomic_store(&channels[idx].current_scene, (cs - 1 + sc) % sc); } }