// Module 6: Test Functions & Effect Chain Setup - FIXED (No s.sync) // Save as "6_test_functions.scd" (REPLACE YOUR EXISTING VERSION) ( // Function to start effects chain - FIXED VERSION ~startEffectsChain = { // Stop existing effects first ~stopEffectsChain.value; "Starting effects chain...".postln; // Create audio buses for effects chain ~sourceBus = Bus.audio(s, 2); ~reverbBus = Bus.audio(s, 2); ~delayBus = Bus.audio(s, 2); ~filterBus = Bus.audio(s, 2); // Create control buses for modulation ~lfoControlBus = Bus.control(s, 1); // Create effects synths in chain with error handling try { ~filterSynth = Synth(\lpf, [ \in, ~sourceBus, \out, ~delayBus, \cutoff, 1000, \res, 0.5 ]); ~delaySynth = Synth(\delay, [ \in, ~delayBus, \out, ~reverbBus, \delaytime, 0.4, \feedback, 0.3, \mix, 0.3 ], ~filterSynth, \addAfter); ~reverbSynth = Synth(\reverb, [ \in, ~reverbBus, \out, 0, \mix, 0.2, \room, 0.5, \damp, 0.5 ], ~delaySynth, \addAfter); // Create LFO separately ~lfoSynth = Synth(\lfoEffect, [ \out, ~lfoControlBus, \freq, 1, \min, 100, \max, 5000, \waveform, 0 ]); // Schedule LFO mapping after a short delay (instead of s.sync) SystemClock.sched(0.1, { if(~filterSynth.notNil and: { ~lfoControlBus.notNil }, { try { ~filterSynth.map(\cutoff, ~lfoControlBus); "LFO mapped to filter cutoff".postln; } { |error| "Warning: Could not map LFO to filter: %".format(error).postln; }; }); nil; // Don't reschedule }); // Store the effects chain nodes in an array for easy access ~effectsChain = [~filterSynth, ~delaySynth, ~reverbSynth, ~lfoSynth]; "Effects chain started successfully".postln; "Chain includes: Filter -> Delay -> Reverb + LFO".postln; } { |error| "Error starting effects chain: %".format(error).postln; ~stopEffectsChain.value; }; }; // Function to stop effects chain ~stopEffectsChain = { if(~effectsChain.notNil, { ~effectsChain.do({ |synth| if(synth.notNil, { try { synth.free; } { |error| // Silent cleanup }; }); }); ~effectsChain = nil; }); // Free individual synth references [~filterSynth, ~delaySynth, ~reverbSynth, ~lfoSynth].do({ |synth| if(synth.notNil, { try { synth.free; } { |error| // Silent cleanup }; }); }); ~filterSynth = nil; ~delaySynth = nil; ~reverbSynth = nil; ~lfoSynth = nil; // Free buses [~sourceBus, ~reverbBus, ~delayBus, ~filterBus, ~lfoControlBus].do({ |bus| if(bus.notNil, { try { bus.free; } { |error| // Silent cleanup }; }); }); ~sourceBus = nil; ~reverbBus = nil; ~delayBus = nil; ~filterBus = nil; ~lfoControlBus = nil; "Effects chain stopped and cleaned up".postln; }; // Function to reset the entire system ~resetSystem = { // Stop all effects ~stopEffectsChain.value; // Clear OSC system if it exists if(~cleanupOSCSystem.notNil, { try { ~cleanupOSCSystem.value; } { |error| }; }); // Clear MIDI system if it exists if(~cleanupAllMIDI.notNil, { try { ~cleanupAllMIDI.value; } { |error| }; }); // Clear synth pool if it exists if(~cleanupSynthPool.notNil, { try { ~cleanupSynthPool.value; } { |error| }; }); // Clear all OSC definitions OSCdef.freeAll; // Reset touch synths tracking ~touchSynths = (); "System reset complete".postln; // Restart effects chain SystemClock.sched(0.5, { ~startEffectsChain.value; nil; }); }; // Test function for parameter changes - FIXED to not use problematic timing ~testParameters = { // Start effects chain if not already running if(~effectsChain.isNil, { ~startEffectsChain.value; }); // Test different oscillator types with varying parameters var testParams = [ [\rgbSynth, 440, 0.2, -0.5, 0.05, 0.5], [\rgbSynth, 330, 0.15, 0, 0.01, 0.8], [\rgbSynth, 220, 0.1, 0.5, 0.1, 1.2], [\rgbSynth, 550, 0.18, -0.2, 0.02, 0.7], [\rgbSynth, 660, 0.15, 0.3, 0.05, 1.0] ]; testParams.do { |params, i| var synthType, freq, amp, pan, attack, release; #synthType, freq, amp, pan, attack, release = params; // Create the synth with the specified parameters Synth(synthType, [ \out, ~sourceBus ? 0, \freq, freq, \amp, amp, \pan, pan, \ampAttack, attack, \ampRelease, release, \redAmt, ~synthParams.redAmt ? 0.5, \greenAmt, ~synthParams.greenAmt ? 0.5, \blueAmt, ~synthParams.blueAmt ? 0.5 ]); // Change effect parameters for demonstration if(~filterSynth.notNil, { try { ~filterSynth.set(\cutoff, 500 + (i * 1000)); } { |error| }; }); if(~delaySynth.notNil, { try { ~delaySynth.set(\mix, 0.2 + (i * 0.1)); } { |error| }; }); if(~reverbSynth.notNil, { try { ~reverbSynth.set(\room, 0.3 + (i * 0.1)); } { |error| }; }); // Log the current sound ["Testing synth:", synthType, freq, amp].postln; }; "Parameter test complete".postln; }; // Test function that creates sounds with scheduled timing (safer approach) ~testSequence = { var sounds = [ [440, 0.2, 0.1, 1.0], [330, 0.15, 0.2, 0.8], [550, 0.18, 0.3, 0.7], [660, 0.12, 0.4, 0.9], [220, 0.25, 0.5, 1.2] ]; // Start effects chain if needed if(~effectsChain.isNil, { ~startEffectsChain.value; }); sounds.do { |params, i| var freq, amp, delay, release; #freq, amp, delay, release = params; // Schedule each sound with SystemClock (safer than Routine) SystemClock.sched(delay * i, { Synth(\rgbSynth, [ \out, ~sourceBus ? 0, \freq, freq, \amp, amp, \ampRelease, release, \redAmt, ~synthParams.redAmt ? 0.5, \greenAmt, ~synthParams.greenAmt ? 0.5, \blueAmt, ~synthParams.blueAmt ? 0.5 ]); ["Scheduled sound:", freq, amp].postln; nil; // Don't reschedule }); }; "Test sequence scheduled".postln; }; // Function to simulate touch input safely ~simulateTouch = { |count=5| var touchCount = 0; "Simulating % touch events".format(count).postln; // Start the effects chain if needed if(~effectsChain.isNil, { ~startEffectsChain.value; }); // Schedule touch events count.do { |i| SystemClock.sched(i * 0.5, { var x = (-0.5 + 1.0.rand); var y = (-0.5 + 1.0.rand); var pressure = 1 + 7.rand; // Update pad values ~currentPadValues.x = x; ~currentPadValues.y = y; ~currentPadValues.pressure = pressure; // Trigger the change effect params function if(~changeEffectParams.notNil, { try { ~changeEffectParams.value; } { |error| "Error in changeEffectParams: %".format(error).postln; }; }); // Trigger sound if using appropriate pen type if(~smartTriggerSound.notNil, { try { ~smartTriggerSound.value; } { |error| "Error in smartTriggerSound: %".format(error).postln; }; }); touchCount = touchCount + 1; ["Simulated touch #%: x=%, y=%, pressure=%".format(touchCount, x.round(0.01), y.round(0.01), pressure.round(0.01))].postln; nil; // Don't reschedule }); }; // Schedule completion message SystemClock.sched(count * 0.5 + 1, { "Touch simulation complete".postln; nil; }); }; // Memory monitoring function ~checkMemory = { var activeSynths = s.numSynths; var activeGroups = s.numGroups; "=== Memory Status ===".postln; "Active synths: %".format(activeSynths).postln; "Active groups: %".format(activeGroups).postln; if(~midiNoteKeys.notNil, { "Active MIDI notes: %".format(~midiNoteKeys.size).postln; }); if(~activeSynths.notNil, { "Active pool synths: %".format(~activeSynths.size).postln; }); if(~effectsChain.notNil, { "Effects chain active: true".postln; }, { "Effects chain active: false".postln; }); // Warning if too many synths if(activeSynths > 50, { "WARNING: High number of active synths (%). Consider running cleanup.".format(activeSynths).postln; }); "===================".postln; }; // Emergency cleanup function ~emergencyCleanup = { "EMERGENCY CLEANUP - Stopping all sounds".postln; // Free all synths on server s.freeAll; // Clear all tracking dictionaries if(~midiNoteKeys.notNil, { ~midiNoteKeys.clear; }); if(~activeSynths.notNil, { ~activeSynths.clear; }); if(~touchSynths.notNil, { ~touchSynths.clear; }); // Clear buses and effects ~stopEffectsChain.value; // Reset pool if it exists if(~cleanupSynthPool.notNil, { try { ~cleanupSynthPool.value; } { |error| "Error during pool cleanup: %".format(error).postln; }; }); "Emergency cleanup complete".postln; // Wait a moment then restart effects SystemClock.sched(1, { ~startEffectsChain.value; nil; }); }; // Register emergency cleanup with Cmd+Period CmdPeriod.add({ ~emergencyCleanup.value; }); "Test functions loaded with NO S.SYNC CALLS.".postln; "Available test functions:".postln; " ~startEffectsChain.value - Start the effects chain".postln; " ~stopEffectsChain.value - Stop the effects chain".postln; " ~testParameters.value - Test different synth parameters".postln; " ~testSequence.value - Test scheduled sound sequence".postln; " ~simulateTouch.value(10) - Simulate 10 touch events".postln; " ~checkMemory.value - Check current memory usage".postln; " ~emergencyCleanup.value - Emergency cleanup all sounds".postln; " ~resetSystem.value - Complete system reset".postln; )