@@ -170,6 +170,42 @@ void AudioCallback(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out,
170170 }
171171}
172172
173+ // Send a CC out (channel 1) to mirror our state to the editor.
174+ static inline void TxCc (MidiUsbHandler& m, int cc, float v01) {
175+ int v = static_cast <int >(v01 * 127 .0f + 0 .5f );
176+ uint8_t msg[3 ] = {0xB0 , static_cast <uint8_t >(cc),
177+ static_cast <uint8_t >(v < 0 ? 0 : (v > 127 ? 127 : v))};
178+ m.SendMessage (msg, 3 );
179+ }
180+
181+ // Device -> editor mirror: when a hardware control (mode/FX/VAR toggle, the 12
182+ // shift-layer knobs, or bypass) changes, transmit the matching CC so Propagator
183+ // follows the physical surface. Change-detected; runs in the main loop (not the ISR).
184+ static void EchoControls (MidiUsbHandler& m, Hothouse& hw) {
185+ using namespace params ::midi;
186+ static int lMode = -1 , lFx = -1 , lVar = -1 , lByp = -1 ;
187+ static float lMK[ShiftKnobs::kKnobs ] = {-1 , -1 , -1 , -1 , -1 , -1 };
188+ static float lFK[ShiftKnobs::kKnobs ] = {-1 , -1 , -1 , -1 , -1 , -1 };
189+
190+ int mode = g_active;
191+ int fx = static_cast <int >(g_fx.GetMode ());
192+ int var = (g_varSel >= 0 ) ? g_varSel : TogglePos (hw, Hothouse::TOGGLESWITCH_2 );
193+ int byp = g_bypass ? 1 : 0 ;
194+ if (mode != lMode) { lMode = mode; TxCc (m, kCcModeSelect , mode / 2 .0f ); }
195+ if (fx != lFx) { lFx = fx; TxCc (m, kCcFxSelect , fx / 2 .0f ); }
196+ if (var != lVar) { lVar = var; TxCc (m, kCcVar , var / 2 .0f ); }
197+ if (byp != lByp) { lByp = byp; TxCc (m, kCcFootsw1 , byp ? 1 .0f : 0 .0f ); }
198+
199+ const float * mk = g_shift.Values (ShiftKnobs::MODE );
200+ const float * fk = g_shift.Values (ShiftKnobs::FX );
201+ for (int i = 0 ; i < ShiftKnobs::kKnobs ; ++i) {
202+ float dm = mk[i] - lMK[i]; if (dm < 0 ) dm = -dm;
203+ float df = fk[i] - lFK[i]; if (df < 0 ) df = -df;
204+ if (dm > 0 .008f ) { lMK[i] = mk[i]; TxCc (m, kCcModeKnobBase + i, mk[i]); }
205+ if (df > 0 .008f ) { lFK[i] = fk[i]; TxCc (m, kCcFxKnobBase + i, fk[i]); }
206+ }
207+ }
208+
173209int main () {
174210 hw.Init ();
175211
@@ -221,6 +257,7 @@ int main() {
221257
222258 while (true ) {
223259 if (PumpMidi (midi, g_modes[g_active], g_shift, g_modeSel, g_fxSel, g_clock, g_delaySync, g_cpu, g_mod, g_master, g_bypass, g_varSel)) g_last_midi_ms = System::GetNow ();
260+ EchoControls (midi, hw); // mirror physical control changes back to the editor
224261 g_clock.Update (System::GetNow ()); // drop back to internal tempo if clock stops
225262 // Onboard LED: ~1 Hz heartbeat = app is alive; solid while MIDI arrives; a fast
226263 // ~5 Hz blink warns that the CPU watchdog tripped (change mode/FX to recover).
0 commit comments