| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668 |
- // Module 5: OSC Communication Setup with Fixed Synth Pool (No s.sync)
- // Save as "5_osc_communication.scd" (REPLACE PREVIOUS VERSION)
- (
- // 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
- // Touch state management
- ~touchState = (
- isActive: false,
- currentTouchKey: nil
- );
- ~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
- );
- // ========== SIMPLE SYNTH POOL SYSTEM ==========
- // Synth Pool Configuration
- ~poolSize = 16; // Number of pre-allocated synths
- ~synthPool = Array.newClear(~poolSize); // Array of synths
- ~activeSynths = IdentityDictionary.new; // Track which synths are in use
- ~freeIndices = Array.series(~poolSize); // Available synth indices (0,1,2...15)
- ~synthPoolGroup = nil;
- ~poolInitialized = false;
- // Clean up existing pool completely
- ~cleanupSynthPool = {
- "Cleaning up synth pool...".postln;
-
- // Set flag
- ~poolInitialized = false;
-
- // Return all active synths to pool (without errors)
- if(~activeSynths.notNil, {
- ~activeSynths.keys.do({ |key|
- try {
- ~returnSynthToPool.value(key);
- } {
- // Silent cleanup
- };
- });
- });
-
- // Free the group (this frees all synths)
- if(~synthPoolGroup.notNil, {
- try {
- ~synthPoolGroup.free;
- } {
- // Silent cleanup
- };
- ~synthPoolGroup = nil;
- });
-
- // Clear data structures
- ~synthPool = Array.newClear(~poolSize);
- ~activeSynths.clear;
- ~freeIndices = Array.series(~poolSize); // Reset to [0,1,2,3...15]
-
- // Reset touch state
- ~touchState.isActive = false;
- ~touchState.currentTouchKey = nil;
-
- "Synth pool cleaned up".postln;
- };
- // Initialize the synth pool - FIXED VERSION (no s.sync)
- ~initializeSynthPool = {
- // Clean up first
- ~cleanupSynthPool.value;
-
- "Creating synth pool with % synths...".format(~poolSize).postln;
-
- // Create a group to hold all pool synths
- ~synthPoolGroup = Group.new;
-
- // Create all synths at once without s.sync
- ~poolSize.do({ |i|
- try {
- var synth = Synth(\rgbSynth, [
- \out, ~sourceBus ? 0,
- \gate, 0, // Start with gate closed (silent)
- \amp, 0,
- \freq, 440,
- \ampAttack, 0.01,
- \ampRelease, 1,
- \filterAttack, 0,
- \filterRelease, 0,
- \pitchAttack, 0,
- \pitchRelease, 0,
- \redAmt, 0.5,
- \greenAmt, 0.5,
- \blueAmt, 0.5
- ], ~synthPoolGroup);
-
- // Store in array
- ~synthPool[i] = synth;
-
- } { |error|
- "Error creating synth %: %".format(i, error).postln;
- };
- });
-
- // Mark as initialized
- ~poolInitialized = true;
-
- "Synth pool initialized with % synths".format(~poolSize).postln;
- "Free synths: %".format(~freeIndices.size).postln;
- "Pool status: %".format(~poolInitialized).postln;
- };
- // Get a free synth from the pool
- ~getSynthFromPool = { |key|
- var synthIndex, synth = nil;
-
- if(~poolInitialized != true, {
- "Error: Synth pool not initialized!".postln;
- nil;
- }, {
- if(~freeIndices.size > 0, {
- // Get the first available synth index
- synthIndex = ~freeIndices.removeAt(0);
- synth = ~synthPool[synthIndex];
-
- // Check if synth exists
- if(synth.notNil, {
- // Mark it as active
- ~activeSynths[key] = (synth: synth, index: synthIndex);
-
- // Update parameters with current settings
- try {
- synth.set(
- \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
- );
- } { |error|
- "Error setting synth parameters: %".format(error).postln;
- };
-
- synth;
- }, {
- // Synth is nil, put index back and return nil
- ~freeIndices.add(synthIndex);
- "Warning: Synth % is nil".format(synthIndex).postln;
- nil;
- });
- }, {
- "Warning: No free synths in pool!".postln;
- nil;
- });
- });
- };
- // Return a synth to the pool
- ~returnSynthToPool = { |key|
- var synthData;
-
- if(~activeSynths.notNil, {
- synthData = ~activeSynths[key];
-
- if(synthData.notNil, {
- var synth = synthData.synth;
- var index = synthData.index;
-
- // Release the synth safely
- if(synth.notNil, {
- try {
- synth.set(\gate, 0, \amp, 0);
- } { |error|
- // Silent error handling
- };
- });
-
- // Return to free pool
- if(~freeIndices.notNil, {
- ~freeIndices.add(index);
- });
-
- // Remove from active tracking
- ~activeSynths.removeAt(key);
- });
- });
- };
- // Start a synth with specific parameters
- ~startPoolSynth = { |key, freq, amp, duration=nil|
- var synth = ~getSynthFromPool.value(key);
-
- if(synth.notNil, {
- // Start the synth
- try {
- synth.set(
- \gate, 1,
- \freq, freq,
- \amp, amp
- );
-
- // If duration is specified, schedule automatic release
- if(duration.notNil, {
- SystemClock.sched(duration, {
- ~returnSynthToPool.value(key);
- nil;
- });
- });
-
- synth;
- } { |error|
- "Error starting synth: %".format(error).postln;
- ~returnSynthToPool.value(key);
- nil;
- };
- }, {
- nil;
- });
- };
- // Update an active synth
- ~updatePoolSynth = { |key, freq=nil, amp=nil|
- var synthData;
-
- if(~activeSynths.notNil, {
- synthData = ~activeSynths[key];
-
- if(synthData.notNil, {
- var synth = synthData.synth;
-
- if(synth.notNil, {
- try {
- if(freq.notNil, { synth.set(\freq, freq); });
- if(amp.notNil, { synth.set(\amp, amp); });
- } { |error|
- // Silent error handling
- };
- });
- });
- });
- };
- // Update all active synths with new envelope/color parameters
- ~updateAllPoolSynths = {
- if(~activeSynths.notNil, {
- ~activeSynths.keysValuesDo({ |key, synthData|
- var synth = synthData.synth;
-
- if(synth.notNil, {
- try {
- synth.set(
- \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
- );
- } { |error|
- // Silent error handling during updates
- };
- });
- });
- });
- };
- // Get pool status
- ~getPoolStatus = {
- "=== Synth Pool Status ===".postln;
- "Pool initialized: %".format(~poolInitialized).postln;
- "Total synths: %".format(~poolSize).postln;
- if(~activeSynths.notNil, {
- "Active synths: %".format(~activeSynths.size).postln;
- }, {
- "Active synths: 0 (activeSynths is nil)".postln;
- });
- if(~freeIndices.notNil, {
- "Free synths: %".format(~freeIndices.size).postln;
- }, {
- "Free synths: 0 (freeIndices is nil)".postln;
- });
- "========================".postln;
- };
- // ========== TOUCH HANDLING ==========
- // Function to handle touch begin (gets synth from pool)
- ~handleTouchBegin = {
- 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.5);
- // Only create sounds for envelope-controlling pen types
- if([\pen, \monoline, \marker].includes(~currentPenType.asSymbol), {
- // End previous touch if it exists
- if(~touchState.currentTouchKey.notNil, {
- ~returnSynthToPool.value(~touchState.currentTouchKey);
- });
-
- // Create unique key for this touch
- ~touchState.currentTouchKey = "touch_" ++ UniqueID.next;
-
- // Get synth from pool and start it
- ~startPoolSynth.value(~touchState.currentTouchKey, freq, amp);
-
- ~touchState.isActive = true;
-
- ["Touch BEGIN - Got synth from pool:", ~currentPenType, freq, amp].postln;
- });
- };
- // Function to handle touch movement (updates pool synth)
- ~handleTouchMove = {
- 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.5);
- // Only update if we have an active touch for envelope-controlling pens
- if(~touchState.isActive and: { ~touchState.currentTouchKey.notNil } and: {
- [\pen, \monoline, \marker].includes(~currentPenType.asSymbol)
- }, {
- // Update the existing synth parameters
- ~updatePoolSynth.value(~touchState.currentTouchKey, freq, amp);
- });
- };
- // Function to handle touch end (returns synth to pool)
- ~handleTouchEnd = {
- if(~touchState.currentTouchKey.notNil, {
- ~returnSynthToPool.value(~touchState.currentTouchKey);
- ~touchState.currentTouchKey = nil;
- ~touchState.isActive = false;
-
- ["Touch END - Returned synth to pool"].postln;
- });
- };
- // Smart trigger function
- ~smartTriggerSound = {
- var pressure = ~currentPadValues.pressure;
-
- // Detect touch begin: pressure goes from low to high
- if(pressure > 2 and: { ~touchState.isActive.not }, {
- ~handleTouchBegin.value;
- });
-
- // Detect touch movement: pressure stays high and we have active touch
- if(pressure > 1 and: { ~touchState.isActive }, {
- ~handleTouchMove.value;
- });
-
- // Detect touch end: pressure goes to very low or zero
- if(pressure <= 1 and: { ~touchState.isActive }, {
- ~handleTouchEnd.value;
- });
- };
- // Function to update effect parameters AND update all active synths
- ~changeEffectParams = {
- var x = ~currentPadValues.x;
- var y = ~currentPadValues.y;
- var pressure = ~currentPadValues.pressure;
- var paramsChanged = false;
- // Update synthesis parameters
- 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;
- paramsChanged = true;
- },
- // 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;
- paramsChanged = true;
- },
- // 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;
- paramsChanged = true;
- },
- // Pencil - Effect preset 1
- \pencil, {
- // Apply Preset 1 effects - with safety checks
- if(~filterSynth.notNil and: { ~filterSynth.isPlaying }, {
- try {
- ~filterSynth.set(
- \cutoff, x.linexp(-0.5, 0.5, 20, 18000),
- \res, y.linlin(-0.5, 0.5, 0, 1)
- );
- } { |error|
- // Silent error handling
- };
- });
- if(~lfoSynth.notNil and: { ~lfoSynth.isPlaying }, {
- try {
- ~lfoSynth.set(
- \freq, x.linlin(-0.5, 0.5, 0, 15)
- );
- } { |error|
- // Silent error handling
- };
- });
- if(~reverbSynth.notNil and: { ~reverbSynth.isPlaying }, {
- try {
- ~reverbSynth.set(
- \room, y.linlin(-0.5, 0.5, 0.1, 0.9)
- );
- } { |error|
- // Silent error handling
- };
- });
- },
- // Crayon - Effect preset 2
- \crayon, {
- // Apply Preset 2 effects - with safety checks
- if(~lfoSynth.notNil and: { ~lfoSynth.isPlaying }, {
- try {
- ~lfoSynth.set(
- \freq, x.linlin(-0.5, 0.5, 15, 1)
- );
- } { |error|
- // Silent error handling
- };
- });
- if(~delaySynth.notNil and: { ~delaySynth.isPlaying }, {
- try {
- ~delaySynth.set(
- \delaytime, x.linlin(-0.5, 0.5, 0.01, 1.0)
- );
- } { |error|
- // Silent error handling
- };
- });
- if(~filterSynth.notNil and: { ~filterSynth.isPlaying }, {
- try {
- ~filterSynth.set(
- \cutoff, y.linexp(-0.5, 0.5, 20, 18000),
- \res, pressure.linlin(1, 5, 0, 1)
- );
- } { |error|
- // Silent error handling
- };
- });
- if(~reverbSynth.notNil and: { ~reverbSynth.isPlaying }, {
- try {
- ~reverbSynth.set(
- \mix, y.linlin(-0.5, 0.5, 0, 1)
- );
- } { |error|
- // Silent error handling
- };
- });
- }
- );
-
- // Update all active synths if envelope parameters changed
- if(paramsChanged, {
- ~updateAllPoolSynths.value;
- });
- };
- // ========== OSC RESPONDERS ==========
- // ----- OSC Pad Values -----
- OSCdef(\xOSC, { |msg, time, addr, port|
- var x = msg[1].asFloat;
- ~currentPadValues.x = x;
- ~changeEffectParams.value;
- if(~touchState.isActive, { ~handleTouchMove.value; });
- }, '/aspectX');
- OSCdef(\yOSC, { |msg, time, addr, port|
- var y = msg[1].asFloat;
- ~currentPadValues.y = y;
- ~changeEffectParams.value;
- if(~touchState.isActive, { ~handleTouchMove.value; });
- }, '/aspectY');
- OSCdef(\pressureOSC, { |msg, time, addr, port|
- var pressure = msg[1].asFloat;
- ~currentPadValues.pressure = pressure;
- ~changeEffectParams.value;
- ~smartTriggerSound.value;
- }, '/pressure');
- // ----- OSC Pen Types -----
- OSCdef(\penOSC, { |msg, time, addr, port|
- var penType = msg[1].asFloat;
- if (penType == 1.0) {
- ~currentPenType = \pen;
- ["Current pen type:", ~currentPenType].postln;
- }
- }, '/pen');
- OSCdef(\monolineOSC, { |msg, time, addr, port|
- var penType = msg[1].asFloat;
- if (penType == 1.0) {
- ~currentPenType = \monoline;
- ["Current pen type:", ~currentPenType].postln;
- }
- }, '/monoline');
- OSCdef(\markerOSC, { |msg, time, addr, port|
- var penType = msg[1].asFloat;
- if (penType == 1.0) {
- ~currentPenType = \marker;
- ["Current pen type:", ~currentPenType].postln;
- }
- }, '/marker');
- OSCdef(\pencilOSC, { |msg, time, addr, port|
- var penType = msg[1].asFloat;
- if (penType == 1.0) {
- ~currentPenType = \pencil;
- if(~initializePreset1.notNil, {
- try { ~initializePreset1.value; } { |error| };
- });
- ["Current pen type:", ~currentPenType].postln;
- }
- }, '/pencil');
- OSCdef(\crayonOSC, { |msg, time, addr, port|
- var penType = msg[1].asFloat;
- if (penType == 1.0) {
- ~currentPenType = \crayon;
- if(~initializePreset2.notNil, {
- try { ~initializePreset2.value; } { |error| };
- });
- ["Current pen type:", ~currentPenType].postln;
- }
- }, '/crayon');
- OSCdef(\fountainPenOSC, { |msg, time, addr, port|
- var penType = msg[1].asFloat;
- if (penType == 1.0) {
- ~currentPenType = \fountainPen;
- if(~initializePreset3.notNil, {
- try { ~initializePreset3.value; } { |error| };
- });
- ["Current pen type:", ~currentPenType].postln;
- }
- }, '/fountainPen');
- OSCdef(\waterColorOSC, { |msg, time, addr, port|
- var penType = msg[1].asFloat;
- if (penType == 1.0) {
- ~currentPenType = \waterColor;
- if(~initializePreset4.notNil, {
- try { ~initializePreset4.value; } { |error| };
- });
- ["Current pen type:", ~currentPenType].postln;
- }
- }, '/waterColor');
- // ----- OSC RGB Colors -----
- OSCdef(\redOSC, { |msg, time, addr, port|
- var component = msg[1].asFloat;
- ~currentColor.r = component;
- ~synthParams.redAmt = component;
- ~updateAllPoolSynths.value;
- ["Color changed (red):", component].postln;
- }, '/r');
- OSCdef(\greenOSC, { |msg, time, addr, port|
- var component = msg[1].asFloat;
- ~currentColor.g = component;
- ~synthParams.greenAmt = component;
- ~updateAllPoolSynths.value;
- ["Color changed (green):", component].postln;
- }, '/g');
- OSCdef(\blueOSC, { |msg, time, addr, port|
- var component = msg[1].asFloat;
- ~currentColor.b = component;
- ~synthParams.blueAmt = component;
- ~updateAllPoolSynths.value;
- ["Color changed (blue):", component].postln;
- }, '/b');
- // ========== CLEANUP ==========
- // Cleanup function
- ~cleanupOSCSystem = {
- // End any active touch
- ~handleTouchEnd.value;
-
- // Reset touch state
- ~touchState.isActive = false;
- ~touchState.currentTouchKey = nil;
-
- "OSC system cleaned up".postln;
- };
- // Register cleanup with CmdPeriod
- CmdPeriod.add({
- ~cleanupOSCSystem.value;
- ~cleanupSynthPool.value;
- });
- // ========== INITIALIZATION ==========
- // Start the OSC server on port 57120
- thisProcess.openUDPPort(57120);
- "========================================".postln;
- "OSC Communication with FIXED Synth Pool loaded".postln;
- "========================================".postln;
- "Available functions:".postln;
- " ~initializeSynthPool.value - Initialize 16 synths".postln;
- " ~getPoolStatus.value - Show pool status".postln;
- " ~cleanupSynthPool.value - Clean up pool".postln;
- " ~cleanupOSCSystem.value - Clean up OSC".postln;
- "".postln;
- "IMPORTANT: Run ~initializeSynthPool.value after effects chain is ready!".postln;
- "NO MORE S.SYNC CALLS - Should work without Routine errors!".postln;
- "========================================".postln;
- )
|