9_midi_controller.scd 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. // Module 9: MIDI Controller - FIXED VERSION
  2. // Save as "9_midi_controller.scd" (REPLACE YOUR EXISTING VERSION)
  3. (
  4. // MIDI note handling using synth pool
  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. if(~returnSynthToPool.notNil, {
  19. ~returnSynthToPool.value(~midiNoteKeys[num]);
  20. });
  21. ~midiNoteKeys.removeAt(num);
  22. });
  23. // Get a synth from the pool instead of creating new one
  24. if(~startPoolSynth.notNil, {
  25. var synth = ~startPoolSynth.value(noteKey, freq, amp);
  26. if(synth.notNil, {
  27. // Track this note
  28. ~midiNoteKeys[num] = noteKey;
  29. // Uncomment for debugging: ["MIDI Note ON: % - Got synth from pool".format(num)].postln;
  30. }, {
  31. ["MIDI Note ON: % - No free synths available!".format(num)].postln;
  32. });
  33. }, {
  34. "ERROR: Synth pool not initialized! Run ~initializeSynthPool.value first".postln;
  35. });
  36. } {
  37. // Treat as noteOff if it is a noteOn with velocity 0
  38. if(~midiNoteKeys[num].notNil, {
  39. if(~returnSynthToPool.notNil, {
  40. ~returnSynthToPool.value(~midiNoteKeys[num]);
  41. });
  42. ~midiNoteKeys.removeAt(num);
  43. // Uncomment for debugging: ["MIDI Note OFF: % (vel 0)".format(num)].postln;
  44. });
  45. }
  46. });
  47. // Note Off: return synth to pool instead of setting gate
  48. MIDIdef.noteOff(\noteOff, { |vel, num, chan, src|
  49. if(~midiNoteKeys[num].notNil, {
  50. if(~returnSynthToPool.notNil, {
  51. ~returnSynthToPool.value(~midiNoteKeys[num]);
  52. });
  53. ~midiNoteKeys.removeAt(num);
  54. // Uncomment for debugging: ["MIDI Note OFF: %".format(num)].postln;
  55. });
  56. });
  57. // All Notes Off (MIDI Panic) - return all MIDI synths to pool
  58. MIDIdef.cc(\allNotesOff, { |val, num, chan, src|
  59. if(num == 123, { // All Notes Off CC
  60. if(~returnSynthToPool.notNil, {
  61. ~midiNoteKeys.keysValuesDo({ |noteNum, noteKey|
  62. ~returnSynthToPool.value(noteKey);
  63. });
  64. });
  65. ~midiNoteKeys.clear;
  66. "MIDI: All notes off - All synths returned to pool".postln;
  67. });
  68. });
  69. // Cleanup all MIDI synths function
  70. ~cleanupAllMIDI = {
  71. if(~returnSynthToPool.notNil, {
  72. ~midiNoteKeys.keysValuesDo({ |noteNum, noteKey|
  73. ~returnSynthToPool.value(noteKey);
  74. });
  75. });
  76. ~midiNoteKeys.clear;
  77. "All MIDI synths returned to pool".postln;
  78. };
  79. // Get MIDI status
  80. ~getMIDIStatus = {
  81. "=== MIDI Status ===".postln;
  82. "Active MIDI notes: %".format(~midiNoteKeys.size).postln;
  83. if(~midiNoteKeys.size > 0, {
  84. "Playing notes: %".format(~midiNoteKeys.keys.asArray.sort).postln;
  85. });
  86. "==================".postln;
  87. };
  88. // Register cleanup with CmdPeriod
  89. CmdPeriod.add({
  90. ~cleanupAllMIDI.value;
  91. });
  92. "MIDI functions loaded with synth pool integration.".postln;
  93. "Notes will use pre-allocated synths from the pool.".postln;
  94. "No more memory leaks or stuck notes!".postln;
  95. "Available functions:".postln;
  96. " ~cleanupAllMIDI.value - Return all MIDI synths to pool".postln;
  97. " ~getMIDIStatus.value - Show active MIDI notes".postln;
  98. )