9_midi_controller.scd 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // Module 9: MIDI Controller - ROBUST VERSION
  2. // Save as "9_midi_controller.scd" (REPLACE YOUR EXISTING VERSION)
  3. (
  4. // MIDI note handling using synth pool - ROBUST VERSION
  5. var midiIn, synths;
  6. ~midiNoteKeys = IdentityDictionary.new; // Track which pool keys are used for MIDI notes
  7. // Connect to MIDI
  8. MIDIClient.init;
  9. MIDIIn.connectAll;
  10. // Note On: get synth from pool instead of creating new one
  11. MIDIdef.noteOn(\noteOn, { |vel, num, chan, src|
  12. var freq = num.midicps;
  13. var amp = (vel/127) * 0.4; // Reduced max amplitude
  14. var noteKey = "midi_" ++ num ++ "_" ++ chan;
  15. if (vel > 0) {
  16. // Check if note is already playing and release it first
  17. if(~midiNoteKeys[num].notNil, {
  18. // Safely return synth to pool
  19. try {
  20. if(~returnSynthToPool.notNil, {
  21. ~returnSynthToPool.value(~midiNoteKeys[num]);
  22. });
  23. } { |error|
  24. // Silent error handling
  25. };
  26. ~midiNoteKeys.removeAt(num);
  27. });
  28. // Check if synth pool functions exist and pool is initialized
  29. if(~startPoolSynth.notNil and: { ~poolInitialized == true }, {
  30. try {
  31. var synth = ~startPoolSynth.value(noteKey, freq, amp);
  32. if(synth.notNil, {
  33. // Track this note
  34. ~midiNoteKeys[num] = noteKey;
  35. // Uncomment for debugging: ["MIDI Note ON: % - Got synth from pool".format(num)].postln;
  36. }, {
  37. ["MIDI Note ON: % - No free synths available!".format(num)].postln;
  38. });
  39. } { |error|
  40. "MIDI Error getting synth from pool: %".format(error).postln;
  41. };
  42. }, {
  43. if(~poolInitialized != true, {
  44. "MIDI Error: Synth pool not initialized! Run ~initializeSynthPool.value first".postln;
  45. }, {
  46. "MIDI Error: Synth pool functions not available!".postln;
  47. });
  48. });
  49. } {
  50. // Treat as noteOff if it is a noteOn with velocity 0
  51. if(~midiNoteKeys[num].notNil, {
  52. try {
  53. if(~returnSynthToPool.notNil, {
  54. ~returnSynthToPool.value(~midiNoteKeys[num]);
  55. });
  56. } { |error|
  57. // Silent error handling
  58. };
  59. ~midiNoteKeys.removeAt(num);
  60. // Uncomment for debugging: ["MIDI Note OFF: % (vel 0)".format(num)].postln;
  61. });
  62. }
  63. });
  64. // Note Off: return synth to pool instead of setting gate
  65. MIDIdef.noteOff(\noteOff, { |vel, num, chan, src|
  66. if(~midiNoteKeys[num].notNil, {
  67. try {
  68. if(~returnSynthToPool.notNil, {
  69. ~returnSynthToPool.value(~midiNoteKeys[num]);
  70. });
  71. } { |error|
  72. // Silent error handling
  73. };
  74. ~midiNoteKeys.removeAt(num);
  75. // Uncomment for debugging: ["MIDI Note OFF: %".format(num)].postln;
  76. });
  77. });
  78. // All Notes Off (MIDI Panic) - return all MIDI synths to pool
  79. MIDIdef.cc(\allNotesOff, { |val, num, chan, src|
  80. if(num == 123, { // All Notes Off CC
  81. try {
  82. if(~returnSynthToPool.notNil, {
  83. ~midiNoteKeys.keysValuesDo({ |noteNum, noteKey|
  84. ~returnSynthToPool.value(noteKey);
  85. });
  86. });
  87. } { |error|
  88. "Error during All Notes Off: %".format(error).postln;
  89. };
  90. ~midiNoteKeys.clear;
  91. "MIDI: All notes off - All synths returned to pool".postln;
  92. });
  93. });
  94. // Cleanup all MIDI synths function
  95. ~cleanupAllMIDI = {
  96. try {
  97. if(~returnSynthToPool.notNil, {
  98. ~midiNoteKeys.keysValuesDo({ |noteNum, noteKey|
  99. ~returnSynthToPool.value(noteKey);
  100. });
  101. });
  102. } { |error|
  103. "Error during MIDI cleanup: %".format(error).postln;
  104. };
  105. ~midiNoteKeys.clear;
  106. "All MIDI synths returned to pool".postln;
  107. };
  108. // Get MIDI status
  109. ~getMIDIStatus = {
  110. "=== MIDI Status ===".postln;
  111. if(~midiNoteKeys.notNil, {
  112. "Active MIDI notes: %".format(~midiNoteKeys.size).postln;
  113. if(~midiNoteKeys.size > 0, {
  114. "Playing notes: %".format(~midiNoteKeys.keys.asArray.sort).postln;
  115. });
  116. }, {
  117. "MIDI note tracking not initialized".postln;
  118. });
  119. if(~poolInitialized.notNil, {
  120. "Synth pool initialized: %".format(~poolInitialized).postln;
  121. }, {
  122. "Synth pool status: Unknown".postln;
  123. });
  124. "==================".postln;
  125. };
  126. // Register cleanup with CmdPeriod
  127. CmdPeriod.add({
  128. ~cleanupAllMIDI.value;
  129. });
  130. "MIDI functions loaded with ROBUST synth pool integration.".postln;
  131. "Notes will use pre-allocated synths from the pool.".postln;
  132. "No more memory leaks or stuck notes!".postln;
  133. "Available functions:".postln;
  134. " ~cleanupAllMIDI.value - Return all MIDI synths to pool".postln;
  135. " ~getMIDIStatus.value - Show active MIDI notes".postln;
  136. )