//////////////////////////////////////////////////// /* GRANULATING SAMPLER INSTRUMENT ////////////////// ////////////////////////////////////rc@kiben.net//// //////////////////////////////////////////////////// | .-. | / \ .-. | / \ / \ .-. .-. _ +--/-------\-----/-----\-----/---\---/---\---/-\- | / \ / \ / '-' '-' |/ '-' '-' Ljudmila OTF Hackathon, Maribor 1-6 March 2022 */ //////////////////////////////////////////// // guitar MAC address 98:CD:AC:63:78:E2 // RUNME 0 ========================================= s.boot(); // RUNME 1 ========================================= // ------------------------------------------------- ( // SETUP ----------------------------------------- - // --------------------------------------------------- // Allocate a 4 second mono buffer b = Buffer.alloc(s, s.sampleRate * 4.0, 1); // load a test file f = Buffer.read(s, thisProcess.nowExecutingPath.dirname +/+ "sounds/Front_Left.wav"); // Open port 57121 for ESP32 UDP OSC thisProcess.openUDPPort(57121); ); // RUNME 2 ========================================== // ------------------------------------------------- ( // DEFINE SYNTHS --------------------------------- <-- // ------------------------------------------------- ( // direct signal + reverb ------------------------- SynthDef("dryIn", { arg in=0, out=0; var signal, dry, wet; dry = SoundIn.ar(in); wet = FreeVerb.ar(dry, mix:0.4, room:0.55, damp:0); signal = Pan2.ar(wet, 0); signal = signal * 0.9; Out.ar(0, signal); }).add; ); ( // mono buffer recorder ---------------------------- // with loop and overdub modes ///////////////////// // make record togglable between looping, direct or onset detection trigger SynthDef("recBuf",{ arg in=0, out=0, bufnum=0, t_trig=0, l=0, overdub=0; var sig, chain, onsets; sig = SoundIn.ar(in); // Onsets chain = FFT(LocalBuf(512), sig); onsets = Onsets.kr(chain, MouseX.kr(0,1), \rcomplex); RecordBuf.ar(sig, bufnum, loop:l, preLevel: overdub, doneAction: 0, trigger:t_trig,); }).add; ); // ---------------------------------------------------- ( // Granulator -------------------------------------- // ---------------------------------------------------- // a custom envelope //var winenv; //winenv = Env([0, 1, 0], [0.5, 0.5], [8, -8]); //z = Buffer.sendCollection(s, winenv.discretize, 1); // ~rndpos = LFNoise2.kr(0.1).range(0, 1); SynthDef(\buf_grain, { | out, gate = 1, amp = 1, sndbuf, envbuf, pan=0, imp=50, pitchmodeindex=0, sensorpitch=1, positionmodeindex=0, sensorposition=0, impulsemodeindex=0, sensorimpulse=0, rlow= -1, rhigh=1 | var signal, env, freqdev, pos, pitchmode0, pitchmode1, pitch, positionmode0, positionmode1, position, impulsemode0, impulsemode1, impulse; // grain pitch mode -untransposed, random, or sensor controlled pitchmode0 = 1; pitchmode1 = LFNoise1.kr.range(rlow, rhigh); pitch = Select.kr(pitchmodeindex, [pitchmode0, pitchmode1, sensorpitch]); // granular playhead mode - fixed random or sensor controlled positionmode0 = 1; positionmode1 = LFNoise1.kr.range(0, 1); position = Select.kr(positionmodeindex, [positionmode0, positionmode1, sensorposition]); // grain impulse frequency mode - random, metric or sensor based impulse modes impulsemode0 = Impulse.kr(imp); impulsemode1 = Dust.kr(imp); impulse = Select.kr(impulsemodeindex, [impulsemode0, impulsemode1, sensorimpulse]); env = EnvGen.kr( Env([0, 1, 0], [1, 1], \sin, 1), gate, levelScale: amp, doneAction: 2); signal = GrainBuf.ar(2, impulse, // selectable between impulse.kr and dust.kr 0.1, sndbuf, pitch, position, 2, pan, envbuf); signal = FreeVerb2.ar(signal[0], signal[1], mix:0.4, room:0.5, damp:0 ); signal = signal* env; Out.ar(0,signal); }).add; ); ) // RUNME 3 ========================================== // ---------------------------------------------------- (( // START SYNTHS --------------------------------- // ---------------------------------------------------- r = Synth(\recBuf, [\in, 0, \out, 0, \bufnum, b, \t_trig, 0]); // recorder ~granulator1 = Synth(\buf_grain, [\sndbuf, b, \envbuf, -1]); // granulate ~gtrIn = Synth(\dryIn); // dry audio signaL )); ~gtrIn.free; // RUNME 4 ========================================== // ---------------------------------------------------- (( // OSC Receivers // ENABLE SENSOR AND SWITCH VALUES <-- // ---------------------------------------------------- ~pitchmode = 0; ~positionmode = 2; ~impulsemode = 0; ( // accel gyro quaternion mapping /////////////////// // msg[1] msg[2] msg[3] X Y Z ==> ROLL, PITCH, YAW o = OSCdef(\test, { arg msg, time, addr, recvPort, rlow, rhigh; // PAN ------- X Roll ~granulator1.set(\pan, msg[1].linlin(-0.3, 0.3, -1, 1)); // PITCH ------- Y Pitch ~granulator1.set(\sensorpitch, msg[3].linlin(-1, 1, 0, 4)); ~granulator1.set(\pitchmodeindex, ~pitchmode ); // POSITION ------- Z Yaw ~granulator1.set(\sensorposition, msg[2].linlin(-1, 1, 0, 4)); ~granulator1.set(\positionmodeindex, ~positionmode ); // RATE ------- X (roll [with PAN]) ~granulator1.set(\sensorimpulse, msg[2].linlin(-1, 1, 5, 50)); ~granulator1.set(\impulsemodeindex, ~impulsemode ); //msg.postln; }; , '/euler'); ); // ---------------------------------------------------- // button press mapping /////////////////////////////// // ---------------------------------------------------- p = OSCdef(\keys, { arg msg, time, addr, recvPort; case // BUTTON 0 && 3 RESET/ZERO SENSORS [in firmware] // BUTTON 0 ----> RECORD TO BUFFER 0 { msg[8] == 1 } { r.set( \t_trig, 1); msg.postln; } // // BUTTON 0 + 3 ----> ZERO EULER / MPU6050 // TOGGLE O ----> OVERDUB MODE ON/OFF { msg[12] == 0 } { r.set(\overdub, 0); } { msg[12] == 1 } { r.set(\overdub, 1); } // TOGGLE 2 { msg[14] == 0 } { ~granulator1.set(\positionmodeindex, 1 ); } { msg[14] == 1 } { ~granulator1.set(\positionmodeindex, 2 ); } // TOGGLE 3 ----> LOOP RECORDING MODE ON/OFF { msg[15] == 0 } { r.set(\l, 0); } { msg[15] == 1 } { r.set(\l, 1); } } , '/keys'); //msg.postln; ); ); // ---------------------------------------------------- // ---------------------------------------------------- // PERFORMABLE PARAMETERS ///////////////////////////// // ---------------------------------------------------- // ---------------------------------------------------- // start recording (without button) r.set(\in, 0, \l, 0, \t_trig, 1); // set recorder loop and overdub modes r.set(\l, 1); // looping record on - MAPPED TO SWITCH 3 r.set(\l, 0); // off r.set(\overdub, 1); // overdub mode on - MAPPED TO SWITCH 4 r.set(\overdub, 0); // off /// random pitch/rate low and high values ~granulator1.set(\rlow, 0); ~granulator1.set(\rlow, -10); ~granulator1.set(\rhigh, 1); ~granulator1.set(\rhigh, 10); ~impulsemode = 0; // impulse ~impulsemode = 1; // dust ~impulsemode = 2; // sensor ~pitchmode = 0; // no transpose ~pitchmode = 1; // random ~pitchmode = 2; // sensor (pitch) input ~positionmode = 0; // middle of sample ~positionmode = 1; // random position ~positionmode = 2; // sensor (position) input // set grain impulse rate ~granulator1.set(\imp, 50); // fix doneaction ~granulator1.set(\gate, 1); // granulator on ~granulator1.set(\gate, 0); // granulator off ///////////////////////////////////////////////// // ----------------------------------------------- // ----------------------------------------------- // ----------------------------------------------- // DUMP ALL OSC MESSAGES (for debugging) //////// OSCFunc.trace(true); OSCFunc.trace(false); thisProcess.openPorts; // list all open ports // ----------------------------------------------- // CODE DUMP ZONE /////////////////////////////// // switch to the custom env //~granulator1.set(\envbuf, z); //~granulator1.set(\envbuf, -1); //n = NetAddr("127.0.0.1", NetAddr.langPort); //n = NetAddr.new("224.0.1.9", 6696);