9_midi_controller.scd 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // Module 9: MIDI Controller - FIXED FOR MEMORY MANAGEMENT
  2. // Save as "9_midi_controller.scd"
  3. (
  4. // MIDI note handling with proper memory management
  5. var midiIn, synths;
  6. ~midiSynths = IdentityDictionary.new; // Store active synths keyed by note number
  7. ~midiCleanupTask = nil; // For scheduled cleanup
  8. // Function to cleanup old MIDI synths
  9. ~cleanupMIDISynths = {
  10. var toRemove = Array.new;
  11. ~midiSynths.keysValuesDo({ |noteNum, synth|
  12. if(synth.isPlaying.not, {
  13. toRemove.add(noteNum);
  14. });
  15. });
  16. toRemove.do({ |noteNum|
  17. ~midiSynths.removeAt(noteNum);
  18. });
  19. // Log cleanup if synths were removed
  20. if(toRemove.size > 0, {
  21. ["Cleaned up % inactive MIDI synths".format(toRemove.size)].postln;
  22. });
  23. };
  24. // Function to force cleanup if too many synths are active
  25. ~forceMIDICleanup = {
  26. if(~midiSynths.size > 20, {
  27. var oldestNotes = ~midiSynths.keys.asArray.copyRange(0, ~midiSynths.size - 15);
  28. oldestNotes.do({ |noteNum|
  29. if(~midiSynths[noteNum].notNil, {
  30. ~midiSynths[noteNum].set(\gate, 0);
  31. ~midiSynths.removeAt(noteNum);
  32. });
  33. });
  34. ["Force cleaned up % MIDI synths".format(oldestNotes.size)].postln;
  35. });
  36. };
  37. // Start periodic cleanup task
  38. ~startMIDICleanupTask = {
  39. if(~midiCleanupTask.notNil, { ~midiCleanupTask.stop; });
  40. ~midiCleanupTask = Task({
  41. loop {
  42. 3.wait; // Wait 3 seconds between cleanups
  43. ~cleanupMIDISynths.value;
  44. ~forceMIDICleanup.value;
  45. }
  46. }).play;
  47. "MIDI cleanup task started".postln;
  48. };
  49. // Stop cleanup task
  50. ~stopMIDICleanupTask = {
  51. if(~midiCleanupTask.notNil, {
  52. ~midiCleanupTask.stop;
  53. ~midiCleanupTask = nil;
  54. "MIDI cleanup task stopped".postln;
  55. });
  56. };
  57. // Connect to MIDI
  58. MIDIClient.init;
  59. MIDIIn.connectAll;
  60. // Note On: create synth
  61. MIDIdef.noteOn(\noteOn, { |vel, num, chan, src|
  62. var freq = num.midicps;
  63. if (vel > 0) {
  64. // Check if note is already playing and release it first
  65. if(~midiSynths[num].notNil, {
  66. ~midiSynths[num].set(\gate, 0);
  67. ~midiSynths.removeAt(num);
  68. });
  69. // Force cleanup if too many synths
  70. ~forceMIDICleanup.value;
  71. // Create new synth with proper parameters
  72. var synth = Synth(\rgbSynth, [
  73. \freq, freq,
  74. \amp, (vel/127) * 0.5, // Reduce amplitude to prevent overload
  75. \ampAttack, ~synthParams.ampAttack ? 0.01,
  76. \ampRelease, ~synthParams.ampRelease ? 1,
  77. \filterAttack, ~synthParams.filterAttack ? 0,
  78. \filterRelease, ~synthParams.filterRelease ? 0,
  79. \pitchAttack, ~synthParams.pitchAttack ? 0,
  80. \pitchRelease, ~synthParams.pitchRelease ? 0,
  81. \redAmt, ~synthParams.redAmt ? 0.5,
  82. \greenAmt, ~synthParams.greenAmt ? 0.5,
  83. \blueAmt, ~synthParams.blueAmt ? 0.5,
  84. \out, ~sourceBus ? 0
  85. ]);
  86. ~midiSynths[num] = synth;
  87. // Schedule automatic cleanup for sustained notes
  88. SystemClock.sched(10, { // Auto-cleanup after 10 seconds maximum
  89. if(~midiSynths[num] == synth, {
  90. synth.set(\gate, 0);
  91. ~midiSynths.removeAt(num);
  92. });
  93. nil; // Don't reschedule
  94. });
  95. } {
  96. // Treat as noteOff if it is a noteOn with velocity 0
  97. if(~midiSynths[num].notNil, {
  98. ~midiSynths[num].set(\gate, 0);
  99. ~midiSynths.removeAt(num);
  100. });
  101. }
  102. });
  103. // Note Off: release synth
  104. MIDIdef.noteOff(\noteOff, { |vel, num, chan, src|
  105. if(~midiSynths[num].notNil, {
  106. ~midiSynths[num].set(\gate, 0);
  107. ~midiSynths.removeAt(num);
  108. });
  109. });
  110. // Cleanup all MIDI synths function
  111. ~cleanupAllMIDISynths = {
  112. ~midiSynths.keysValuesDo({ |noteNum, synth|
  113. synth.set(\gate, 0);
  114. });
  115. ~midiSynths.clear;
  116. ~stopMIDICleanupTask.value;
  117. "All MIDI synths cleaned up".postln;
  118. };
  119. // Start the cleanup task
  120. ~startMIDICleanupTask.value;
  121. // Register cleanup with CmdPeriod
  122. CmdPeriod.add({
  123. ~cleanupAllMIDISynths.value;
  124. });
  125. "MIDI functions loaded with memory management.".postln;
  126. "Active MIDI synths will be automatically cleaned up.".postln;
  127. "To manually cleanup all MIDI synths: ~cleanupAllMIDISynths.value".postln;
  128. "Current active MIDI synths: % can be checked with ~midiSynths.size".postln;
  129. )