| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- // Module 9: MIDI Controller - FIXED FOR MEMORY MANAGEMENT
- // Save as "9_midi_controller.scd"
- (
- // MIDI note handling with proper memory management
- var midiIn, synths;
- ~midiSynths = IdentityDictionary.new; // Store active synths keyed by note number
- ~midiCleanupTask = nil; // For scheduled cleanup
- // Function to cleanup old MIDI synths
- ~cleanupMIDISynths = {
- var toRemove = Array.new;
-
- ~midiSynths.keysValuesDo({ |noteNum, synth|
- if(synth.isPlaying.not, {
- toRemove.add(noteNum);
- });
- });
-
- toRemove.do({ |noteNum|
- ~midiSynths.removeAt(noteNum);
- });
-
- // Log cleanup if synths were removed
- if(toRemove.size > 0, {
- ["Cleaned up % inactive MIDI synths".format(toRemove.size)].postln;
- });
- };
- // Function to force cleanup if too many synths are active
- ~forceMIDICleanup = {
- if(~midiSynths.size > 20, {
- var oldestNotes = ~midiSynths.keys.asArray.copyRange(0, ~midiSynths.size - 15);
- oldestNotes.do({ |noteNum|
- if(~midiSynths[noteNum].notNil, {
- ~midiSynths[noteNum].set(\gate, 0);
- ~midiSynths.removeAt(noteNum);
- });
- });
- ["Force cleaned up % MIDI synths".format(oldestNotes.size)].postln;
- });
- };
- // Start periodic cleanup task
- ~startMIDICleanupTask = {
- if(~midiCleanupTask.notNil, { ~midiCleanupTask.stop; });
-
- ~midiCleanupTask = Task({
- loop {
- 3.wait; // Wait 3 seconds between cleanups
- ~cleanupMIDISynths.value;
- ~forceMIDICleanup.value;
- }
- }).play;
-
- "MIDI cleanup task started".postln;
- };
- // Stop cleanup task
- ~stopMIDICleanupTask = {
- if(~midiCleanupTask.notNil, {
- ~midiCleanupTask.stop;
- ~midiCleanupTask = nil;
- "MIDI cleanup task stopped".postln;
- });
- };
- // Connect to MIDI
- MIDIClient.init;
- MIDIIn.connectAll;
- // Note On: create synth
- MIDIdef.noteOn(\noteOn, { |vel, num, chan, src|
- var freq = num.midicps;
- if (vel > 0) {
- // Check if note is already playing and release it first
- if(~midiSynths[num].notNil, {
- ~midiSynths[num].set(\gate, 0);
- ~midiSynths.removeAt(num);
- });
-
- // Force cleanup if too many synths
- ~forceMIDICleanup.value;
-
- // Create new synth with proper parameters
- var synth = Synth(\rgbSynth, [
- \freq, freq,
- \amp, (vel/127) * 0.5, // Reduce amplitude to prevent overload
- \ampAttack, ~synthParams.ampAttack ? 0.01,
- \ampRelease, ~synthParams.ampRelease ? 1,
- \filterAttack, ~synthParams.filterAttack ? 0,
- \filterRelease, ~synthParams.filterRelease ? 0,
- \pitchAttack, ~synthParams.pitchAttack ? 0,
- \pitchRelease, ~synthParams.pitchRelease ? 0,
- \redAmt, ~synthParams.redAmt ? 0.5,
- \greenAmt, ~synthParams.greenAmt ? 0.5,
- \blueAmt, ~synthParams.blueAmt ? 0.5,
- \out, ~sourceBus ? 0
- ]);
-
- ~midiSynths[num] = synth;
-
- // Schedule automatic cleanup for sustained notes
- SystemClock.sched(10, { // Auto-cleanup after 10 seconds maximum
- if(~midiSynths[num] == synth, {
- synth.set(\gate, 0);
- ~midiSynths.removeAt(num);
- });
- nil; // Don't reschedule
- });
- } {
- // Treat as noteOff if it is a noteOn with velocity 0
- if(~midiSynths[num].notNil, {
- ~midiSynths[num].set(\gate, 0);
- ~midiSynths.removeAt(num);
- });
- }
- });
- // Note Off: release synth
- MIDIdef.noteOff(\noteOff, { |vel, num, chan, src|
- if(~midiSynths[num].notNil, {
- ~midiSynths[num].set(\gate, 0);
- ~midiSynths.removeAt(num);
- });
- });
- // Cleanup all MIDI synths function
- ~cleanupAllMIDISynths = {
- ~midiSynths.keysValuesDo({ |noteNum, synth|
- synth.set(\gate, 0);
- });
- ~midiSynths.clear;
- ~stopMIDICleanupTask.value;
- "All MIDI synths cleaned up".postln;
- };
- // Start the cleanup task
- ~startMIDICleanupTask.value;
- // Register cleanup with CmdPeriod
- CmdPeriod.add({
- ~cleanupAllMIDISynths.value;
- });
- "MIDI functions loaded with memory management.".postln;
- "Active MIDI synths will be automatically cleaned up.".postln;
- "To manually cleanup all MIDI synths: ~cleanupAllMIDISynths.value".postln;
- "Current active MIDI synths: % can be checked with ~midiSynths.size".postln;
- )
|