5_osc_communication.scd 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. // Module 5: OSC Communication Setup - FIXED FOR MEMORY MANAGEMENT
  2. // Save as "5_osc_communication.scd"
  3. (
  4. // Clear any existing OSC definitions
  5. OSCdef.freeAll;
  6. // Variables to track current pen type and color
  7. ~currentPenType = \pen;
  8. ~currentColor = (r: 0.0, g: 0.0, b: 1.0); // Default blue
  9. ~currentPadValues = (x: 0.0, y: 0.0, pressure: 1.0); //Default pad values
  10. // Track active synths to prevent memory leaks
  11. ~activeSynths = IdentityDictionary.new;
  12. ~synthCounter = 0;
  13. ~synthParams = (
  14. out: 0,
  15. freq: 440,
  16. amp: 0.5,
  17. ampAttack: 0.01,
  18. ampRelease: 1,
  19. filterAttack: 0,
  20. filterRelease: 0,
  21. filterMin: 200,
  22. filterMax: 5000,
  23. pitchAttack: 0,
  24. pitchRelease: 0,
  25. pitchRatio: 2,
  26. redAmt: 0.5,
  27. greenAmt: 0.5,
  28. blueAmt: 0.5
  29. );
  30. // Function to clean up old synths
  31. ~cleanupSynths = {
  32. ~activeSynths.keysValuesDo({ |key, synth|
  33. if(synth.isPlaying.not, {
  34. ~activeSynths.removeAt(key);
  35. });
  36. });
  37. // If too many synths are active, force cleanup of oldest ones
  38. if(~activeSynths.size > 10, {
  39. var oldestKeys = ~activeSynths.keys.asArray.sort.copyRange(0, ~activeSynths.size - 6);
  40. oldestKeys.do({ |key|
  41. if(~activeSynths[key].notNil, {
  42. ~activeSynths[key].set(\gate, 0);
  43. ~activeSynths.removeAt(key);
  44. });
  45. });
  46. });
  47. };
  48. // Function to create a new synth safely
  49. ~createSafeSynth = { |synthType = \rgbSynth, args|
  50. var synth, synthId;
  51. // Clean up old synths first
  52. ~cleanupSynths.value;
  53. // Create new synth
  54. synthId = ~synthCounter;
  55. ~synthCounter = ~synthCounter + 1;
  56. // Add default parameters if not provided
  57. args = args ++ [
  58. \ampAttack, ~synthParams.ampAttack,
  59. \ampRelease, ~synthParams.ampRelease,
  60. \filterAttack, ~synthParams.filterAttack,
  61. \filterRelease, ~synthParams.filterRelease,
  62. \pitchAttack, ~synthParams.pitchAttack,
  63. \pitchRelease, ~synthParams.pitchRelease,
  64. \redAmt, ~synthParams.redAmt,
  65. \greenAmt, ~synthParams.greenAmt,
  66. \blueAmt, ~synthParams.blueAmt
  67. ];
  68. synth = Synth(synthType, args);
  69. ~activeSynths[synthId] = synth;
  70. // Schedule automatic cleanup
  71. SystemClock.sched(~synthParams.ampAttack + ~synthParams.ampRelease + 1, {
  72. if(~activeSynths[synthId].notNil, {
  73. ~activeSynths[synthId].set(\gate, 0);
  74. ~activeSynths.removeAt(synthId);
  75. });
  76. nil; // Don't reschedule
  77. });
  78. synth;
  79. };
  80. // Define OSC responder for creating sounds (only for envelope-controlling pens)
  81. ~triggerSound = {
  82. var x = ~currentPadValues.x;
  83. var y = ~currentPadValues.y;
  84. var pressure = ~currentPadValues.pressure;
  85. var freq = x.linexp(-0.5, 0.5, 100, 2000);
  86. var amp = pressure.linlin(1, 8, 0.1, 0.8);
  87. // Only create sounds for envelope-controlling pen types
  88. if([\pen, \monoline, \marker].includes(~currentPenType.asSymbol), {
  89. ~createSafeSynth.value(\rgbSynth, [
  90. \out, ~sourceBus ? 0,
  91. \freq, freq,
  92. \amp, amp
  93. ]);
  94. ["Sound triggered:", ~currentPenType, freq, amp].postln;
  95. });
  96. };
  97. // Function to update effect parameters safely
  98. ~changeEffectParams = {
  99. var x = ~currentPadValues.x;
  100. var y = ~currentPadValues.y;
  101. var pressure = ~currentPadValues.pressure;
  102. // Log the received data
  103. ["Touch data:", x, y, pressure, ~currentPenType, ~currentColor].postln;
  104. // Handle touch based on current pen type
  105. switch(~currentPenType.asSymbol,
  106. // Pen - Controls amplitude envelope
  107. \pen, {
  108. var ampAttack = y.linexp(-0.5, 0.5, 0.001, 5);
  109. var ampRelease = x.linexp(-0.5, 0.5, 0.001, 10);
  110. ~synthParams.ampAttack = ampAttack;
  111. ~synthParams.ampRelease = ampRelease;
  112. ["Amplitude envelope:", ampAttack, ampRelease].postln;
  113. },
  114. // Monoline - Controls filter envelope
  115. \monoline, {
  116. var filterAttack = y.linexp(-0.5, 0.5, 0.001, 5);
  117. var filterRelease = x.linexp(-0.5, 0.5, 0.001, 10);
  118. ~synthParams.filterAttack = filterAttack;
  119. ~synthParams.filterRelease = filterRelease;
  120. ["Filter envelope:", filterAttack, filterRelease].postln;
  121. },
  122. // Marker - Controls pitch envelope
  123. \marker, {
  124. var pitchAttack = y.linexp(-0.5, 0.5, 0.001, 5);
  125. var pitchRelease = x.linexp(-0.5, 0.5, 0.001, 10);
  126. ~synthParams.pitchAttack = pitchAttack;
  127. ~synthParams.pitchRelease = pitchRelease;
  128. ["Pitch envelope:", pitchAttack, pitchRelease].postln;
  129. },
  130. // Pencil - Effect preset 1
  131. \pencil, {
  132. // Apply Preset 1 effects - with safety checks
  133. if(~filterSynth.notNil and: { ~filterSynth.isPlaying }, {
  134. ~filterSynth.set(
  135. \cutoff, x.linexp(-0.5, 0.5, 20, 18000),
  136. \res, y.linlin(-0.5, 0.5, 0, 1)
  137. );
  138. });
  139. if(~lfoSynth.notNil and: { ~lfoSynth.isPlaying }, {
  140. ~lfoSynth.set(
  141. \freq, x.linlin(-0.5, 0.5, 0, 15),
  142. \intensity, pressure.linlin(1, 8, 0, 1)
  143. );
  144. });
  145. if(~reverbSynth.notNil and: { ~reverbSynth.isPlaying }, {
  146. ~reverbSynth.set(
  147. \room, y.linlin(-0.5, 0.5, 0.1, 0.9)
  148. );
  149. });
  150. ["Pencil preset:", "Cutoff", x.linexp(-0.5, 0.5, 20, 18000), "LFO", x.linlin(-0.5, 0.5, 0, 15)].postln;
  151. },
  152. // Crayon - Effect preset 2
  153. \crayon, {
  154. // Apply Preset 2 effects - with safety checks
  155. if(~lfoSynth.notNil and: { ~lfoSynth.isPlaying }, {
  156. ~lfoSynth.set(
  157. \freq, x.linlin(-0.5, 0.5, 15, 1),
  158. \intensity, x.linlin(-0.5, 0.5, 0, 1)
  159. );
  160. });
  161. if(~delaySynth.notNil and: { ~delaySynth.isPlaying }, {
  162. ~delaySynth.set(
  163. \delaytime, x.linlin(-0.5, 0.5, 0.01, 1.0)
  164. );
  165. });
  166. if(~filterSynth.notNil and: { ~filterSynth.isPlaying }, {
  167. ~filterSynth.set(
  168. \cutoff, y.linexp(-0.5, 0.5, 20, 18000),
  169. \res, pressure.linlin(1, 5, 0, 1)
  170. );
  171. });
  172. if(~reverbSynth.notNil and: { ~reverbSynth.isPlaying }, {
  173. ~reverbSynth.set(
  174. \mix, y.linlin(-0.5, 0.5, 0, 1)
  175. );
  176. });
  177. ["Crayon preset:", "LFO", x.linlin(-0.5, 0.5, 15, 1), "Filter", y.linexp(-0.5, 0.5, 20, 18000)].postln;
  178. },
  179. // Fountain pen - Effect preset 3 (placeholder)
  180. \fountainPen, {
  181. // Apply Preset 3 effects (TBD in documentation)
  182. ["Fountain pen preset (TBD)"].postln;
  183. },
  184. // Water color - Effect preset 4 (placeholder)
  185. \waterColor, {
  186. // Apply Preset 4 effects (TBD in documentation)
  187. ["Water color preset (TBD)"].postln;
  188. }
  189. );
  190. };
  191. // ----- OSC Pad Values -----
  192. // OSC responder for x coordinate
  193. OSCdef(\xOSC, { |msg, time, addr, port|
  194. var x = msg[1].asFloat;
  195. // Update current pad value and change effects
  196. ~currentPadValues.x = x;
  197. ~changeEffectParams.value;
  198. }, '/aspectX');
  199. // OSC responder for y coordinate
  200. OSCdef(\yOSC, { |msg, time, addr, port|
  201. var y = msg[1].asFloat;
  202. // Update current pad value and change effects
  203. ~currentPadValues.y = y;
  204. ~changeEffectParams.value;
  205. }, '/aspectY');
  206. // OSC responder for pressure coordinate
  207. OSCdef(\pressureOSC, { |msg, time, addr, port|
  208. var pressure = msg[1].asFloat;
  209. // Update current pad value and change effects
  210. ~currentPadValues.pressure = pressure;
  211. ~changeEffectParams.value;
  212. // Trigger sound for envelope-controlling pens
  213. ~triggerSound.value;
  214. }, '/pressure');
  215. // ----- OSC Pen Types -----
  216. // OSC responder for pen
  217. OSCdef(\penOSC, { |msg, time, addr, port|
  218. var penType = msg[1].asFloat;
  219. if (penType == 1.0) {
  220. ~currentPenType = \pen;
  221. ["Current pen type:", ~currentPenType].postln;
  222. }
  223. }, '/pen');
  224. // OSC responder for monoline
  225. OSCdef(\monolineOSC, { |msg, time, addr, port|
  226. var penType = msg[1].asFloat;
  227. if (penType == 1.0) {
  228. ~currentPenType = \monoline;
  229. ["Current pen type:", ~currentPenType].postln;
  230. }
  231. }, '/monoline');
  232. // OSC responder for marker
  233. OSCdef(\markerOSC, { |msg, time, addr, port|
  234. var penType = msg[1].asFloat;
  235. if (penType == 1.0) {
  236. ~currentPenType = \marker;
  237. ["Current pen type:", ~currentPenType].postln;
  238. }
  239. }, '/marker');
  240. // OSC responder for pencil
  241. OSCdef(\pencilOSC, { |msg, time, addr, port|
  242. var penType = msg[1].asFloat;
  243. if (penType == 1.0) {
  244. ~currentPenType = \pencil;
  245. if(~initializePreset1.notNil, { ~initializePreset1.value; });
  246. ["Current pen type:", ~currentPenType].postln;
  247. }
  248. }, '/pencil');
  249. // OSC responder for crayon
  250. OSCdef(\crayonOSC, { |msg, time, addr, port|
  251. var penType = msg[1].asFloat;
  252. if (penType == 1.0) {
  253. ~currentPenType = \crayon;
  254. if(~initializePreset2.notNil, { ~initializePreset2.value; });
  255. ["Current pen type:", ~currentPenType].postln;
  256. }
  257. }, '/crayon');
  258. // OSC responder for fountainPen
  259. OSCdef(\fountainPenOSC, { |msg, time, addr, port|
  260. var penType = msg[1].asFloat;
  261. if (penType == 1.0) {
  262. ~currentPenType = \fountainPen;
  263. if(~initializePreset3.notNil, { ~initializePreset3.value; });
  264. ["Current pen type:", ~currentPenType].postln;
  265. }
  266. }, '/fountainPen');
  267. // OSC responder for waterColor
  268. OSCdef(\waterColorOSC, { |msg, time, addr, port|
  269. var penType = msg[1].asFloat;
  270. if (penType == 1.0) {
  271. ~currentPenType = \waterColor;
  272. if(~initializePreset4.notNil, { ~initializePreset4.value; });
  273. ["Current pen type:", ~currentPenType].postln;
  274. }
  275. }, '/waterColor');
  276. // ----- OSC RGB Colors -----
  277. // OSC responder for red changes
  278. OSCdef(\redOSC, { |msg, time, addr, port|
  279. var component = msg[1].asFloat;
  280. // Update current color
  281. ~currentColor.r = component;
  282. ~synthParams.redAmt = component;
  283. ["Color changed:", ~currentColor].postln;
  284. }, '/r');
  285. // OSC responder for green changes
  286. OSCdef(\greenOSC, { |msg, time, addr, port|
  287. var component = msg[1].asFloat;
  288. // Update current color
  289. ~currentColor.g = component;
  290. ~synthParams.greenAmt = component;
  291. ["Color changed:", ~currentColor].postln;
  292. }, '/g');
  293. // OSC responder for blue changes
  294. OSCdef(\blueOSC, { |msg, time, addr, port|
  295. var component = msg[1].asFloat;
  296. // Update current color
  297. ~currentColor.b = component;
  298. ~synthParams.blueAmt = component;
  299. ["Color changed:", ~currentColor].postln;
  300. }, '/b');
  301. // Cleanup function
  302. ~cleanupOSCSystem = {
  303. ~activeSynths.keysValuesDo({ |key, synth|
  304. synth.set(\gate, 0);
  305. });
  306. ~activeSynths.clear;
  307. "OSC system cleaned up".postln;
  308. };
  309. // Start the OSC server on port 57120 (default SuperCollider port)
  310. thisProcess.openUDPPort(57120);
  311. "OSC server ready on port 57120".postln;
  312. "Registered OSC commands for pad values, pen types, and colors".postln;
  313. "Ready to receive data from iDraw OSC app".postln;
  314. "To cleanup system, run: ~cleanupOSCSystem.value".postln;
  315. )