| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- // Module 5: OSC Communication Setup - FIXED FOR MEMORY MANAGEMENT
- // Save as "5_osc_communication.scd"
- (
- // Clear any existing OSC definitions
- OSCdef.freeAll;
- // Variables to track current pen type and color
- ~currentPenType = \pen;
- ~currentColor = (r: 0.0, g: 0.0, b: 1.0); // Default blue
- ~currentPadValues = (x: 0.0, y: 0.0, pressure: 1.0); //Default pad values
- // Track active synths to prevent memory leaks
- ~activeSynths = IdentityDictionary.new;
- ~synthCounter = 0;
- ~synthParams = (
- out: 0,
- freq: 440,
- amp: 0.5,
- ampAttack: 0.01,
- ampRelease: 1,
- filterAttack: 0,
- filterRelease: 0,
- filterMin: 200,
- filterMax: 5000,
- pitchAttack: 0,
- pitchRelease: 0,
- pitchRatio: 2,
- redAmt: 0.5,
- greenAmt: 0.5,
- blueAmt: 0.5
- );
- // Function to clean up old synths
- ~cleanupSynths = {
- ~activeSynths.keysValuesDo({ |key, synth|
- if(synth.isPlaying.not, {
- ~activeSynths.removeAt(key);
- });
- });
-
- // If too many synths are active, force cleanup of oldest ones
- if(~activeSynths.size > 10, {
- var oldestKeys = ~activeSynths.keys.asArray.sort.copyRange(0, ~activeSynths.size - 6);
- oldestKeys.do({ |key|
- if(~activeSynths[key].notNil, {
- ~activeSynths[key].set(\gate, 0);
- ~activeSynths.removeAt(key);
- });
- });
- });
- };
- // Function to create a new synth safely
- ~createSafeSynth = { |synthType = \rgbSynth, args|
- var synth, synthId;
-
- // Clean up old synths first
- ~cleanupSynths.value;
-
- // Create new synth
- synthId = ~synthCounter;
- ~synthCounter = ~synthCounter + 1;
-
- // Add default parameters if not provided
- args = args ++ [
- \ampAttack, ~synthParams.ampAttack,
- \ampRelease, ~synthParams.ampRelease,
- \filterAttack, ~synthParams.filterAttack,
- \filterRelease, ~synthParams.filterRelease,
- \pitchAttack, ~synthParams.pitchAttack,
- \pitchRelease, ~synthParams.pitchRelease,
- \redAmt, ~synthParams.redAmt,
- \greenAmt, ~synthParams.greenAmt,
- \blueAmt, ~synthParams.blueAmt
- ];
-
- synth = Synth(synthType, args);
- ~activeSynths[synthId] = synth;
-
- // Schedule automatic cleanup
- SystemClock.sched(~synthParams.ampAttack + ~synthParams.ampRelease + 1, {
- if(~activeSynths[synthId].notNil, {
- ~activeSynths[synthId].set(\gate, 0);
- ~activeSynths.removeAt(synthId);
- });
- nil; // Don't reschedule
- });
-
- synth;
- };
- // Define OSC responder for creating sounds (only for envelope-controlling pens)
- ~triggerSound = {
- var x = ~currentPadValues.x;
- var y = ~currentPadValues.y;
- var pressure = ~currentPadValues.pressure;
- var freq = x.linexp(-0.5, 0.5, 100, 2000);
- var amp = pressure.linlin(1, 8, 0.1, 0.8);
- // Only create sounds for envelope-controlling pen types
- if([\pen, \monoline, \marker].includes(~currentPenType.asSymbol), {
- ~createSafeSynth.value(\rgbSynth, [
- \out, ~sourceBus ? 0,
- \freq, freq,
- \amp, amp
- ]);
-
- ["Sound triggered:", ~currentPenType, freq, amp].postln;
- });
- };
- // Function to update effect parameters safely
- ~changeEffectParams = {
- var x = ~currentPadValues.x;
- var y = ~currentPadValues.y;
- var pressure = ~currentPadValues.pressure;
- // Log the received data
- ["Touch data:", x, y, pressure, ~currentPenType, ~currentColor].postln;
- // Handle touch based on current pen type
- switch(~currentPenType.asSymbol,
- // Pen - Controls amplitude envelope
- \pen, {
- var ampAttack = y.linexp(-0.5, 0.5, 0.001, 5);
- var ampRelease = x.linexp(-0.5, 0.5, 0.001, 10);
- ~synthParams.ampAttack = ampAttack;
- ~synthParams.ampRelease = ampRelease;
- ["Amplitude envelope:", ampAttack, ampRelease].postln;
- },
- // Monoline - Controls filter envelope
- \monoline, {
- var filterAttack = y.linexp(-0.5, 0.5, 0.001, 5);
- var filterRelease = x.linexp(-0.5, 0.5, 0.001, 10);
- ~synthParams.filterAttack = filterAttack;
- ~synthParams.filterRelease = filterRelease;
- ["Filter envelope:", filterAttack, filterRelease].postln;
- },
- // Marker - Controls pitch envelope
- \marker, {
- var pitchAttack = y.linexp(-0.5, 0.5, 0.001, 5);
- var pitchRelease = x.linexp(-0.5, 0.5, 0.001, 10);
- ~synthParams.pitchAttack = pitchAttack;
- ~synthParams.pitchRelease = pitchRelease;
- ["Pitch envelope:", pitchAttack, pitchRelease].postln;
- },
- // Pencil - Effect preset 1
- \pencil, {
- // Apply Preset 1 effects - with safety checks
- if(~filterSynth.notNil and: { ~filterSynth.isPlaying }, {
- ~filterSynth.set(
- \cutoff, x.linexp(-0.5, 0.5, 20, 18000),
- \res, y.linlin(-0.5, 0.5, 0, 1)
- );
- });
- if(~lfoSynth.notNil and: { ~lfoSynth.isPlaying }, {
- ~lfoSynth.set(
- \freq, x.linlin(-0.5, 0.5, 0, 15),
- \intensity, pressure.linlin(1, 8, 0, 1)
- );
- });
- if(~reverbSynth.notNil and: { ~reverbSynth.isPlaying }, {
- ~reverbSynth.set(
- \room, y.linlin(-0.5, 0.5, 0.1, 0.9)
- );
- });
- ["Pencil preset:", "Cutoff", x.linexp(-0.5, 0.5, 20, 18000), "LFO", x.linlin(-0.5, 0.5, 0, 15)].postln;
- },
- // Crayon - Effect preset 2
- \crayon, {
- // Apply Preset 2 effects - with safety checks
- if(~lfoSynth.notNil and: { ~lfoSynth.isPlaying }, {
- ~lfoSynth.set(
- \freq, x.linlin(-0.5, 0.5, 15, 1),
- \intensity, x.linlin(-0.5, 0.5, 0, 1)
- );
- });
- if(~delaySynth.notNil and: { ~delaySynth.isPlaying }, {
- ~delaySynth.set(
- \delaytime, x.linlin(-0.5, 0.5, 0.01, 1.0)
- );
- });
- if(~filterSynth.notNil and: { ~filterSynth.isPlaying }, {
- ~filterSynth.set(
- \cutoff, y.linexp(-0.5, 0.5, 20, 18000),
- \res, pressure.linlin(1, 5, 0, 1)
- );
- });
- if(~reverbSynth.notNil and: { ~reverbSynth.isPlaying }, {
- ~reverbSynth.set(
- \mix, y.linlin(-0.5, 0.5, 0, 1)
- );
- });
- ["Crayon preset:", "LFO", x.linlin(-0.5, 0.5, 15, 1), "Filter", y.linexp(-0.5, 0.5, 20, 18000)].postln;
- },
- // Fountain pen - Effect preset 3 (placeholder)
- \fountainPen, {
- // Apply Preset 3 effects (TBD in documentation)
- ["Fountain pen preset (TBD)"].postln;
- },
- // Water color - Effect preset 4 (placeholder)
- \waterColor, {
- // Apply Preset 4 effects (TBD in documentation)
- ["Water color preset (TBD)"].postln;
- }
- );
- };
- // ----- OSC Pad Values -----
- // OSC responder for x coordinate
- OSCdef(\xOSC, { |msg, time, addr, port|
- var x = msg[1].asFloat;
- // Update current pad value and change effects
- ~currentPadValues.x = x;
- ~changeEffectParams.value;
- }, '/aspectX');
- // OSC responder for y coordinate
- OSCdef(\yOSC, { |msg, time, addr, port|
- var y = msg[1].asFloat;
- // Update current pad value and change effects
- ~currentPadValues.y = y;
- ~changeEffectParams.value;
- }, '/aspectY');
- // OSC responder for pressure coordinate
- OSCdef(\pressureOSC, { |msg, time, addr, port|
- var pressure = msg[1].asFloat;
- // Update current pad value and change effects
- ~currentPadValues.pressure = pressure;
- ~changeEffectParams.value;
-
- // Trigger sound for envelope-controlling pens
- ~triggerSound.value;
- }, '/pressure');
- // ----- OSC Pen Types -----
- // OSC responder for pen
- OSCdef(\penOSC, { |msg, time, addr, port|
- var penType = msg[1].asFloat;
- if (penType == 1.0) {
- ~currentPenType = \pen;
- ["Current pen type:", ~currentPenType].postln;
- }
- }, '/pen');
- // OSC responder for monoline
- OSCdef(\monolineOSC, { |msg, time, addr, port|
- var penType = msg[1].asFloat;
- if (penType == 1.0) {
- ~currentPenType = \monoline;
- ["Current pen type:", ~currentPenType].postln;
- }
- }, '/monoline');
- // OSC responder for marker
- OSCdef(\markerOSC, { |msg, time, addr, port|
- var penType = msg[1].asFloat;
- if (penType == 1.0) {
- ~currentPenType = \marker;
- ["Current pen type:", ~currentPenType].postln;
- }
- }, '/marker');
- // OSC responder for pencil
- OSCdef(\pencilOSC, { |msg, time, addr, port|
- var penType = msg[1].asFloat;
- if (penType == 1.0) {
- ~currentPenType = \pencil;
- if(~initializePreset1.notNil, { ~initializePreset1.value; });
- ["Current pen type:", ~currentPenType].postln;
- }
- }, '/pencil');
- // OSC responder for crayon
- OSCdef(\crayonOSC, { |msg, time, addr, port|
- var penType = msg[1].asFloat;
- if (penType == 1.0) {
- ~currentPenType = \crayon;
- if(~initializePreset2.notNil, { ~initializePreset2.value; });
- ["Current pen type:", ~currentPenType].postln;
- }
- }, '/crayon');
- // OSC responder for fountainPen
- OSCdef(\fountainPenOSC, { |msg, time, addr, port|
- var penType = msg[1].asFloat;
- if (penType == 1.0) {
- ~currentPenType = \fountainPen;
- if(~initializePreset3.notNil, { ~initializePreset3.value; });
- ["Current pen type:", ~currentPenType].postln;
- }
- }, '/fountainPen');
- // OSC responder for waterColor
- OSCdef(\waterColorOSC, { |msg, time, addr, port|
- var penType = msg[1].asFloat;
- if (penType == 1.0) {
- ~currentPenType = \waterColor;
- if(~initializePreset4.notNil, { ~initializePreset4.value; });
- ["Current pen type:", ~currentPenType].postln;
- }
- }, '/waterColor');
- // ----- OSC RGB Colors -----
- // OSC responder for red changes
- OSCdef(\redOSC, { |msg, time, addr, port|
- var component = msg[1].asFloat;
- // Update current color
- ~currentColor.r = component;
- ~synthParams.redAmt = component;
- ["Color changed:", ~currentColor].postln;
- }, '/r');
- // OSC responder for green changes
- OSCdef(\greenOSC, { |msg, time, addr, port|
- var component = msg[1].asFloat;
- // Update current color
- ~currentColor.g = component;
- ~synthParams.greenAmt = component;
- ["Color changed:", ~currentColor].postln;
- }, '/g');
- // OSC responder for blue changes
- OSCdef(\blueOSC, { |msg, time, addr, port|
- var component = msg[1].asFloat;
- // Update current color
- ~currentColor.b = component;
- ~synthParams.blueAmt = component;
- ["Color changed:", ~currentColor].postln;
- }, '/b');
- // Cleanup function
- ~cleanupOSCSystem = {
- ~activeSynths.keysValuesDo({ |key, synth|
- synth.set(\gate, 0);
- });
- ~activeSynths.clear;
- "OSC system cleaned up".postln;
- };
- // Start the OSC server on port 57120 (default SuperCollider port)
- thisProcess.openUDPPort(57120);
- "OSC server ready on port 57120".postln;
- "Registered OSC commands for pad values, pen types, and colors".postln;
- "Ready to receive data from iDraw OSC app".postln;
- "To cleanup system, run: ~cleanupOSCSystem.value".postln;
- )
|