// Module 9: MIDI Controller - FIXED VERSION // Save as "9_midi_controller.scd" (REPLACE YOUR EXISTING VERSION) ( // MIDI note handling using synth pool var midiIn, synths; ~midiNoteKeys = IdentityDictionary.new; // Track which pool keys are used for MIDI notes // Connect to MIDI MIDIClient.init; MIDIIn.connectAll; // Note On: get synth from pool instead of creating new one MIDIdef.noteOn(\noteOn, { |vel, num, chan, src| var freq = num.midicps; var amp = (vel/127) * 0.4; // Reduced max amplitude var noteKey = "midi_" ++ num ++ "_" ++ chan; if (vel > 0) { // Check if note is already playing and release it first if(~midiNoteKeys[num].notNil, { if(~returnSynthToPool.notNil, { ~returnSynthToPool.value(~midiNoteKeys[num]); }); ~midiNoteKeys.removeAt(num); }); // Get a synth from the pool instead of creating new one if(~startPoolSynth.notNil, { var synth = ~startPoolSynth.value(noteKey, freq, amp); if(synth.notNil, { // Track this note ~midiNoteKeys[num] = noteKey; // Uncomment for debugging: ["MIDI Note ON: % - Got synth from pool".format(num)].postln; }, { ["MIDI Note ON: % - No free synths available!".format(num)].postln; }); }, { "ERROR: Synth pool not initialized! Run ~initializeSynthPool.value first".postln; }); } { // Treat as noteOff if it is a noteOn with velocity 0 if(~midiNoteKeys[num].notNil, { if(~returnSynthToPool.notNil, { ~returnSynthToPool.value(~midiNoteKeys[num]); }); ~midiNoteKeys.removeAt(num); // Uncomment for debugging: ["MIDI Note OFF: % (vel 0)".format(num)].postln; }); } }); // Note Off: return synth to pool instead of setting gate MIDIdef.noteOff(\noteOff, { |vel, num, chan, src| if(~midiNoteKeys[num].notNil, { if(~returnSynthToPool.notNil, { ~returnSynthToPool.value(~midiNoteKeys[num]); }); ~midiNoteKeys.removeAt(num); // Uncomment for debugging: ["MIDI Note OFF: %".format(num)].postln; }); }); // All Notes Off (MIDI Panic) - return all MIDI synths to pool MIDIdef.cc(\allNotesOff, { |val, num, chan, src| if(num == 123, { // All Notes Off CC if(~returnSynthToPool.notNil, { ~midiNoteKeys.keysValuesDo({ |noteNum, noteKey| ~returnSynthToPool.value(noteKey); }); }); ~midiNoteKeys.clear; "MIDI: All notes off - All synths returned to pool".postln; }); }); // Cleanup all MIDI synths function ~cleanupAllMIDI = { if(~returnSynthToPool.notNil, { ~midiNoteKeys.keysValuesDo({ |noteNum, noteKey| ~returnSynthToPool.value(noteKey); }); }); ~midiNoteKeys.clear; "All MIDI synths returned to pool".postln; }; // Get MIDI status ~getMIDIStatus = { "=== MIDI Status ===".postln; "Active MIDI notes: %".format(~midiNoteKeys.size).postln; if(~midiNoteKeys.size > 0, { "Playing notes: %".format(~midiNoteKeys.keys.asArray.sort).postln; }); "==================".postln; }; // Register cleanup with CmdPeriod CmdPeriod.add({ ~cleanupAllMIDI.value; }); "MIDI functions loaded with synth pool integration.".postln; "Notes will use pre-allocated synths from the pool.".postln; "No more memory leaks or stuck notes!".postln; "Available functions:".postln; " ~cleanupAllMIDI.value - Return all MIDI synths to pool".postln; " ~getMIDIStatus.value - Show active MIDI notes".postln; )