diff --git a/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp b/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp index 347ddf2..2343f2d 100644 --- a/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp @@ -43,7 +43,7 @@ ARGUMENT:: minSliceLength The length in samples that the Slice will stay ON. Changes of states during that period will be ignored. ARGUMENT:: highPassFreq - The frequency of the fourth-order Linkwitz–Riley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter. + The frequency of the fourth-order link::https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter##Linkwitz–Riley high-pass filter::. This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter. RETURNS:: An audio stream with square envelopes around the slices. The latency between the input and the output is dependant on the relation between the two envelope followers. @@ -54,7 +54,8 @@ code:: // detrending explained // Our source here is a sinewave that does not go to silence and has sharp-ish amplitude bumps as onsets we try to track ( -{var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6)); +{ + var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6)); env = FluidAmpSlice.ar(source,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 10,floor: -60); [source, env] }.plot(0.08); diff --git a/release-packaging/HelpSource/Classes/FluidBufAmpGate.schelp b/release-packaging/HelpSource/Classes/FluidBufAmpGate.schelp index 70a137f..643887d 100644 --- a/release-packaging/HelpSource/Classes/FluidBufAmpGate.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufAmpGate.schelp @@ -17,79 +17,78 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: METHOD:: process - This is the method that calls for the slicing to be calculated on a given source buffer. +This is the method that calls for the slicing to be calculated on a given source buffer. ARGUMENT:: server - The server on which the buffers to be processed are allocated. +The server on which the buffers to be processed are allocated. ARGUMENT:: source - The index of the buffer to use as the source material to be sliced through novelty identification. The different channels of multichannel buffers will be summed. +The index of the buffer to use as the source material to be sliced through novelty identification. The different channels of multichannel buffers will be summed. ARGUMENT:: startFrame - Where in the srcBuf should the slicing process start, in sample. +Where in the srcBuf should the slicing process start, in sample. ARGUMENT:: numFrames - How many frames should be processed. +How many frames should be processed. ARGUMENT:: startChan - For multichannel sources, which channel should be processed. +For multichannel sources, which channel should be processed. ARGUMENT:: numChans - For multichannel sources, how many channel should be summed. +For multichannel sources, how many channel should be summed. ARGUMENT:: indices - The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis. +The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis. ARGUMENT:: rampUp - The number of samples the envelope follower will take to reach the next value when raising. +The number of samples the envelope follower will take to reach the next value when raising. ARGUMENT:: rampDown - The number of samples the envelope follower will take to reach the next value when falling. +The number of samples the envelope follower will take to reach the next value when falling. ARGUMENT:: onThreshold - The threshold in dB of the envelope follower to trigger an onset, aka to go ON when in OFF state. +The threshold in dB of the envelope follower to trigger an onset, aka to go ON when in OFF state. ARGUMENT:: offThreshold - The threshold in dB of the envelope follower to trigger an offset, , aka to go ON when in OFF state. +The threshold in dB of the envelope follower to trigger an offset, , aka to go ON when in OFF state. ARGUMENT:: minSliceLength - The length in samples that the Slice will stay ON. Changes of states during that period will be ignored. +The length in samples that the Slice will stay ON. Changes of states during that period will be ignored. ARGUMENT:: minSilenceLength - The length in samples that the Slice will stay OFF. Changes of states during that period will be ignored. +The length in samples that the Slice will stay OFF. Changes of states during that period will be ignored. ARGUMENT:: minLengthAbove - The length in samples that the envelope have to be above the threshold to consider it a valid transition to ON. The Slice will start at the first sample when the condition is met. Therefore, this affects the latency. +The length in samples that the envelope have to be above the threshold to consider it a valid transition to ON. The Slice will start at the first sample when the condition is met. Therefore, this affects the latency. ARGUMENT:: minLengthBelow - The length in samples that the envelope have to be below the threshold to consider it a valid transition to OFF. The Slice will end at the first sample when the condition is met. Therefore, this affects the latency. +The length in samples that the envelope have to be below the threshold to consider it a valid transition to OFF. The Slice will end at the first sample when the condition is met. Therefore, this affects the latency. ARGUMENT:: lookBack - The length of the buffer kept before an onset to allow the algorithm, once a new Slice is detected, to go back in time (up to that many samples) to find the minimum amplitude as the Slice onset point. This affects the latency of the algorithm. +The length of the buffer kept before an onset to allow the algorithm, once a new Slice is detected, to go back in time (up to that many samples) to find the minimum amplitude as the Slice onset point. This affects the latency of the algorithm. ARGUMENT:: lookAhead - The length of the buffer kept after an offset to allow the algorithm, once the Slice is considered finished, to wait further in time (up to that many samples) to find a minimum amplitude as the Slice offset point. This affects the latency of the algorithm. +The length of the buffer kept after an offset to allow the algorithm, once the Slice is considered finished, to wait further in time (up to that many samples) to find a minimum amplitude as the Slice offset point. This affects the latency of the algorithm. ARGUMENT:: highPassFreq - The frequency of the fourth-order Linkwitz–Riley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter. +The frequency of the fourth-order link::https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter##Linkwitz–Riley high-pass filter::. This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter. ARGUMENT:: action - A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument. +A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument. RETURNS:: - Nothing, as the destination buffer is declared in the function call. +Nothing, as the destination buffer is declared in the function call. EXAMPLES:: code:: // define a test signal and a destination buffer ( -b = Buffer.sendCollection(s, Array.fill(44100,{|i| sin(i*pi/ (44100/640)) * (sin(i*pi/ 22050)).abs})); -c = Buffer.new(s); + b = Buffer.sendCollection(s, Array.fill(44100,{|i| sin(i*pi/ (44100/640)) * (sin(i*pi/ 22050)).abs})); + c = Buffer.new(s); ) b.play b.plot - //basic tests: absThresh sanity FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12) c.query @@ -140,30 +139,33 @@ STRONG::A musical example.:: CODE:: //load a buffer ( -b = Buffer.read(s,File.realpath(FluidBufAmpGate.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); -c = Buffer.new(s); + b = Buffer.read(s, File.realpath(FluidBufAmpGate.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); + c = Buffer.new(s); ) // slice the samples -FluidBufAmpGate.process(s, b, indices:c, rampUp:110, rampDown:2205, onThreshold:-27, offThreshold: -31, minSilenceLength:1100, lookBack:441, highPassFreq:40) -c.query -c.getn(0,c.numFrames*2,{|item|item.postln;}) -//reformatting to read the onsets and offsets as pairs -c.getn(0,c.numFrames*2,{|items|items.reshape(c.numFrames,2).do({|x| x.postln});}) - +( +Routine{ + FluidBufAmpGate.process(s, b, indices:c, rampUp:110, rampDown:2205, onThreshold:-27, offThreshold: -31, minSilenceLength:1100, lookBack:441, highPassFreq:40).wait; + c.query; + c.getn(0,c.numFrames*2,{|item|item.postln;}); + //reformatting to read the onsets and offsets as pairs + c.getn(0,c.numFrames*2,{|items|items.reshape(c.numFrames,2).do({|x| x.postln});}); +}.play +) //loops over a splice with the MouseX, taking the respective onset and offset of a given slice ( -{ - BufRd.ar(1, b, - Phasor.ar(0,1, - BufRd.kr(2, c, - MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0], - BufRd.kr(2, c, - MouseX.kr(1, BufFrames.kr(c)), 0, 1)[1], - BufRd.kr(2,c, - MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0] - ), 0, 1); -}.play; + { + BufRd.ar(1, b, + Phasor.ar(0,1, + BufRd.kr(2, c, + MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0], + BufRd.kr(2, c, + MouseX.kr(1, BufFrames.kr(c)), 0, 1)[1], + BufRd.kr(2,c, + MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0] + ), 0, 1); + }.play; ) :: @@ -178,15 +180,16 @@ b.play // create a new buffer as destinations c = Buffer.new(s); - +OSCFunc.trace(false) //run the process on them ( -// with basic params -Routine{ - t = Main.elapsedTime; - FluidBufAmpGate.process(s, b, indices: c, rampUp:1, rampDown:10, onThreshold: -30); - (Main.elapsedTime - t).postln; -}.play + // with basic params + Routine{ + var t = Main.elapsedTime; + var proc= FluidBufAmpGate.process(s, b, indices: c, rampUp:1, rampDown:10, onThreshold: -30); + proc.wait; + (Main.elapsedTime - t).postln; + }.play ) // list the indicies of detected attacks - the two input channels have been summed. The two channels of the output, respectively onset and offset indices, are interleaved as this is the SuperCollider buffer data formatting diff --git a/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp index f5739cc..c7dbdd5 100644 --- a/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp @@ -143,14 +143,16 @@ b.play c = Buffer.new(s); //run the process on them -( -// with basic params -Routine{ - t = Main.elapsedTime; - FluidBufAmpSlice.process(s,b, indices: c, fastRampUp: 10,fastRampDown: 2205,slowRampUp: 4410,slowRampDown: 4410, onThreshold: 10,offThreshold: 5); - (Main.elapsedTime - t).postln; -}.play -) + ( + // with basic params + Routine{ + var t = Main.elapsedTime; + var proc = FluidBufAmpSlice.process(s,b, indices: c, fastRampUp: 10,fastRampDown: 2205,slowRampUp: 4410,slowRampDown: 4410, onThreshold: 10,offThreshold: 5); + proc.wait; + c.query; + (Main.elapsedTime - t).postln; + }.play + ) // list the indicies of detected attacks - the two input channels have been summed. c.getn(0,c.numFrames,{|item|(item * 2).postln;}) diff --git a/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp b/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp index 2f38ff1..6aa0b4f 100644 --- a/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp @@ -89,6 +89,7 @@ FluidBufAudioTransport.process(s,b,source2:c,destination:d,interpolation:0.5,act // listen to the source and the result b.play c.play +d.updateInfo d.play // more interesting sources: two cardboard bowing gestures diff --git a/release-packaging/HelpSource/Classes/FluidBufCompose.schelp b/release-packaging/HelpSource/Classes/FluidBufCompose.schelp index 5c2b323..7a878b0 100644 --- a/release-packaging/HelpSource/Classes/FluidBufCompose.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufCompose.schelp @@ -68,29 +68,42 @@ d = Buffer.new(s); ) // with basic params (basic summing of each full buffer in all dimensions) -FluidBufCompose.process(s, source: b, destination: d); -FluidBufCompose.process(s, source: c, destination: d, destGain: 1.0); -d.query; -d.play; - +( +Routine{ + FluidBufCompose.process(s, source: b, destination: d).wait; + FluidBufCompose.process(s, source: c, destination: d, destGain: 1.0).wait; + d.query; + d.play; +}.play; +) //constructing a mono buffer, with a quiet punch from the synth, with a choked piano resonance from the left channel -d.free; d = Buffer.new(s); -FluidBufCompose.process(s, source: b, numFrames: 9000, gain: 0.5, destination: d); -FluidBufCompose.process(s, source: c, startFrame:30000, numFrames:44100, numChans:1, gain:0.9, destination: d, destGain: 1.0); -d.query; -d.play; - +( +Routine{ + d.free; d = Buffer.new(s); + FluidBufCompose.process(s, source: b, numFrames: 9000, gain: 0.5, destination: d).wait; + FluidBufCompose.process(s, source: c, startFrame:30000, numFrames:44100, numChans:1, gain:0.9, destination: d, destGain: 1.0).wait; + d.query; + d.play; +}.play +) //constructing a stereo buffer, with the end of the mono synth in both channels, with a piano resonance in swapped stereo -d.free; d = Buffer.new(s); -FluidBufCompose.process(s, source: b, startFrame: 441000, numChans: 2, gain: 0.6, destination: d); -FluidBufCompose.process(s, source: c, numFrames: 78000, startChan: 1, numChans: 2, gain: 0.5, destStartFrame: 22050, destination: d, destGain: 1.0); -d.query; -d.play; - +( +Routine{ + d.free; d = Buffer.new(s); + FluidBufCompose.process(s, source: b, startFrame: 441000, numChans: 2, gain: 0.6, destination: d).wait; + FluidBufCompose.process(s, source: c, numFrames: 78000, startChan: 1, numChans: 2, gain: 0.5, destStartFrame: 22050, destination: d, destGain: 1.0).wait; + d.query; + d.play; +}.play +) //constructing a one second buffer: the first second of each buffer, the mono synth on the right, the piano on the left -d.free; d = Buffer.new(s); -FluidBufCompose.process(s, source: b, numFrames: 44100, numChans: 1, destStartChan: 1, destination: d); -FluidBufCompose.process(s, source: c, numFrames:44100, numChans:1, destination: d, destGain: 1.0); -d.query; -d.play; +( +Routine{ + d.free; d = Buffer.new(s); + FluidBufCompose.process(s, source: b, numFrames: 44100, numChans: 1, destStartChan: 1, destination: d).wait; + FluidBufCompose.process(s, source: c, numFrames:44100, numChans:1, destination: d, destGain: 1.0).wait; + d.query; + d.play; +}.play +) :: diff --git a/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp b/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp index 1b744b0..7acc5e5 100644 --- a/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp @@ -83,7 +83,7 @@ FluidBufPitch.process(s,b,numFrames:512 * 10,numChans:1,features:~pitchdata,acti // Flatten and print the flat buffer. We expect to see larger numbers (20-2000) interleaved with smaller (0-1) ( -FluidBufFlatten.process(s,~pitchdata,~flatdata,action:{ +FluidBufFlatten.process(s,~pitchdata,~flatdata,axis:1,action:{ ~flatdata.loadToFloatArray(action:{ |a| a.postln; }) diff --git a/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp b/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp index 711bf5b..93cb75e 100644 --- a/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp @@ -123,16 +123,17 @@ code:: d = Buffer.new(s); e = Buffer.new(s); ) - +OSCFunc.trace(true, true) // run with basic parameters ( Routine{ t = Main.elapsedTime; - FluidBufHPSS.process(s, b, harmonic: c, percussive: d); + FluidBufHPSS.process(s, b, harmonic: c, percussive: d).wait; (Main.elapsedTime - t).postln; }.play ) - +c.query +d.query //play the harmonic c.play; //play the percussive @@ -143,11 +144,12 @@ d.play; //more daring parameters, in mode 2 ( - Routine{ - t = Main.elapsedTime; - FluidBufHPSS.process(s, b, harmonic: c, percussive: d, residual:e, harmFilterSize:31, maskingMode:2, harmThreshFreq1: 0.005, harmThreshAmp1: 7.5, harmThreshFreq2: 0.168, harmThreshAmp2: 7.5, percThreshFreq1: 0.004, percThreshAmp1: 26.5, percThreshFreq2: 0.152, percThreshAmp2: 26.5,windowSize:4096,hopSize:512); - (Main.elapsedTime - t).postln; - }.play +Routine{ + t = Main.elapsedTime; + FluidBufHPSS.process(s, b, harmonic: c, percussive: d, residual:e, harmFilterSize:31, maskingMode:2, harmThreshFreq1: 0.005, harmThreshAmp1: 7.5, harmThreshFreq2: 0.168, harmThreshAmp2: 7.5, percThreshFreq1: 0.004, percThreshAmp1: 26.5, percThreshFreq2: 0.152, percThreshAmp2: 26.5,windowSize:4096,hopSize:512) + .wait; + (Main.elapsedTime - t).postln; +}.play ) //play the harmonic @@ -171,9 +173,12 @@ c = Buffer.read(s,File.realpath(FluidBufHPSS.class.filenameSymbol).dirname.withT ) // composite one on left one on right as test signals -FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b) -b.play - +( +Routine{ + FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b).wait; + b.play +}.play +) // create 2 new buffers as destinations d = Buffer.new(s); e = Buffer.new(s); @@ -181,7 +186,7 @@ d = Buffer.new(s); e = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufHPSS.process(s, b, harmonic: d, percussive:e); + FluidBufHPSS.process(s, b, harmonic: d, percussive:e).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp b/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp index be696d5..1dd9b68 100644 --- a/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp @@ -73,7 +73,7 @@ b.play; b.plot; ( Routine{ t = Main.elapsedTime; - FluidBufLoudness.process(s, source:b, features: c); + FluidBufLoudness.process(s, source:b, features: c).wait; (Main.elapsedTime - t).postln; }.play ) @@ -109,7 +109,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufLoudness.process(s, b, features: c, windowSize: 17640, hopSize:4410); + FluidBufLoudness.process(s, b, features: c, windowSize: 17640, hopSize:4410).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp index ec1f1bd..1389c0d 100644 --- a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp @@ -78,7 +78,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufMFCC.process(s, b, features: c); + FluidBufMFCC.process(s, b, features: c).wait; (Main.elapsedTime - t).postln; }.play ) @@ -108,7 +108,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufMFCC.process(s, b, numCoeffs:5, features: c); + FluidBufMFCC.process(s, b, numCoeffs:5, features: c).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp index da18154..4a7d21a 100644 --- a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp @@ -79,7 +79,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufMelBands.process(s, b, features: c, numBands:10); + FluidBufMelBands.process(s, b, features: c, numBands:10).wait; (Main.elapsedTime - t).postln; }.play ) @@ -109,7 +109,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufMelBands.process(s, b, features: c, numBands:10); + FluidBufMelBands.process(s, b, features: c, numBands:10).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp index ba97ecb..70953ae 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp @@ -142,7 +142,7 @@ d.play //////(beware !!!! loud!!!) ( // separate them in 2 components Routine { - FluidBufNMF.process(s, d, resynth:e, bases: f, activations:g, components:2); + FluidBufNMF.process(s, d, resynth:e, bases: f, activations:g, components:2).wait; e.query; f.query; g.query; @@ -179,16 +179,15 @@ y = Buffer.new(s); // matrix factorisation, requesting everything - wait for the computation time to appear. ( Routine{ - t = Main.elapsedTime; - FluidBufNMF.process(s,b, 0,-1,0,-1,c,x,0,y,0,5,100,~frame_size,~hop_size,~fft_size); - (Main.elapsedTime - t).postln; + var t = Main.elapsedTime; + FluidBufNMF.process(s,b, 0,-1,0,-1,c,x,0,y,0,5,100,~frame_size,~hop_size,~fft_size) + .wait; + (Main.elapsedTime - t).postln; }.play ) //look at the resynthesised components, the bases and the activations c.plot; x.plot; y.plot; -//null test of the sum of sources -{(PlayBuf.ar(5,c,doneAction:2).sum)+(-1*PlayBuf.ar(1,b,doneAction:2))}.play // play the components spread in the stereo field {Splay.ar(PlayBuf.ar(5,c,doneAction:2))}.play @@ -196,6 +195,9 @@ c.plot; x.plot; y.plot; //play a single source {PlayBuf.ar(5,c,doneAction:2)[~which_component].dup}.play +//null test of the sum of sources +{(PlayBuf.ar(5,c,doneAction:2).sum)+(-1*PlayBuf.ar(1,b,doneAction:2))}.play + //play noise using one of the bases as filter. ( { @@ -251,7 +253,7 @@ b.play // train using the first 2 seconds of the sound file ( Routine { - FluidBufNMF.process(s,b,0,44100*5,0,1, ~originalNMF, ~bases, components:10); + FluidBufNMF.process(s,b,0,44100*5,0,1, ~originalNMF, ~bases, components:10).wait; ~originalNMF.query; }.play; ) @@ -296,7 +298,7 @@ z.do({|chan| FluidBufCompose.process(s, ~bases, startChan:chan, numChans: 1, des //process the whole file, splitting it with the 2 trained bases ( Routine{ - FluidBufNMF.process(s, b, resynth: ~sortedNMF, bases: ~trainedBases, basesMode: 2, components:2); + FluidBufNMF.process(s, b, resynth: ~sortedNMF, bases: ~trainedBases, basesMode: 2, components:2).wait; ~originalNMF.query; }.play; ) @@ -354,7 +356,7 @@ e.query ( // use the seeding basis, without updating Routine { - FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 2, activations:g, components:3); + FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 2, activations:g, components:3).wait; e.query; f.query; g.query; @@ -373,7 +375,7 @@ g.plot; ( // use the seeding bases, with updating this time Routine { - FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 1, activations:g, components:3); + FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 1, activations:g, components:3).wait; e.query; f.query; g.query; diff --git a/release-packaging/HelpSource/Classes/FluidBufNNDSVD.schelp b/release-packaging/HelpSource/Classes/FluidBufNNDSVD.schelp index b0656d1..746eee2 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNNDSVD.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNNDSVD.schelp @@ -71,16 +71,24 @@ b = Buffer.read(s,File.realpath(FluidBufNNDSVD.class.filenameSymbol).dirname.wit ) //how many bases do I need to decompose the buffer with 90% accuracy -FluidBufNNDSVD.process(s, b, ~bases, ~activations, coverage: 0.9, method: 1, action: {\done.postln;}) - +( +Routine{ + FluidBufNNDSVD.process(s, b, ~bases, ~activations, coverage: 0.9, method: 1).wait; + "% bases".format(~bases.numChannels).postln; +}.play; +) //check how many bases we are returned: -~bases.numChannels + //try the same process with less accuracy -FluidBufNNDSVD.process(s, b, ~bases, ~activations, coverage: 0.5, action: {\done.postln;}) -~bases.numChannels +( +Routine{ + FluidBufNNDSVD.process(s, b, ~bases, ~activations, coverage: 0.5).wait; + "% bases".format(~bases.numChannels).postln; +}.play +) //use the bases to run NMF on FluidBufNMF.process(s, b, resynth: ~resynth, bases: ~bases, activations: ~activations,actMode: 2, components: ~bases.numChannels, action: {\done.postln;}) -{PlayBuf.ar(~resynth.numChannels, ~resynth)[1]}.play +{PlayBuf.ar(~resynth.numChannels, ~resynth)[2]}.play :: diff --git a/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp b/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp index 0dba199..c1d0d87 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp @@ -87,7 +87,7 @@ c = Buffer.new(s); // with basic params, with a minimum slight length to avoid over Routine{ t = Main.elapsedTime; - FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.4,filterSize: 4, minSliceLength: 8); + FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.4,filterSize: 4, minSliceLength: 8).wait; (Main.elapsedTime - t).postln; }.play ) @@ -120,11 +120,13 @@ c = Buffer.new(s); ) // process with a given filterSize -FluidBufNoveltySlice.process(s,b, indices: c, kernelSize:31, threshold:0.1, filterSize:1) - +( +Routine{ + FluidBufNoveltySlice.process(s,b, indices: c, kernelSize:31, threshold:0.1, filterSize:1).wait; //check the number of slices: it is the number of frames in the transBuf minus the boundary index. -c.query; - + c.query; +}.play; +) //play slice number 3 ( { @@ -164,7 +166,7 @@ c = Buffer.new(s); // with basic params Routine{ t = Main.elapsedTime; - FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.3); + FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.3).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp index 4942473..8516677 100644 --- a/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp @@ -93,7 +93,7 @@ c = Buffer.new(s); // with basic params Routine{ t = Main.elapsedTime; - FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.5); + FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.5).wait; (Main.elapsedTime - t).postln; }.play ) @@ -134,7 +134,7 @@ c = Buffer.new(s); // with basic params Routine{ t = Main.elapsedTime; - FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.00001); + FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.00001).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp index 4caac6a..225f068 100644 --- a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp @@ -44,7 +44,7 @@ ARGUMENT:: algorithm TABLE:: ## 0 || Cepstrum: Returns a pitch estimate as the location of the second highest peak in the Cepstrum of the signal (after DC). ## 1 || Harmonic Product Spectrum: Implements the Harmonic Product Spectrum algorithm for pitch detection . See e.g. FOOTNOTE:: A. Lerch, "An Introduction to Audio Content Analysis: Applications in Signal Processing and Music Informatics." John Wiley & Sons, 2012.https://onlinelibrary.wiley.com/doi/book/10.1002/9781118393550 :: - ## 2 || YinFFT: Implements the frequency domain version of the YIN algorithm, as described in FOOTNOTE::P. M. Brossier, "Automatic Annotation of Musical Audio for Interactive Applications.” QMUL, London, UK, 2007. :: See also https://essentia.upf.edu/documentation/reference/streaming_PitchYinFFT.html +## 2 || YinFFT: Implements the frequency domain version of the YIN algorithm, as described in FOOTNOTE::P. M. Brossier, "Automatic Annotation of Musical Audio for Interactive Applications." QMUL, London, UK, 2007. :: See also https://essentia.upf.edu/documentation/reference/streaming_PitchYinFFT.html :: ARGUMENT:: minFreq @@ -87,7 +87,7 @@ b.play; b.plot; ( Routine{ t = Main.elapsedTime; - FluidBufPitch.process(s, b, features: c); + FluidBufPitch.process(s, b, features: c).wait; (Main.elapsedTime - t).postln; }.play ) @@ -122,7 +122,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufPitch.process(s, b, features: c, minFreq:200, maxFreq:2000, unit:1); + FluidBufPitch.process(s, b, features: c, minFreq:200, maxFreq:2000, unit:1).wait; (Main.elapsedTime - t).postln; }.play ) @@ -143,7 +143,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufPitch.process(s, b, features: c,action:{c.loadToFloatArray(action: {|x| d = x.reshape((x.size()/2).asInteger, 2)})}); + FluidBufPitch.process(s, b, features: c,action:{c.loadToFloatArray(action: {|x| d = x.reshape((x.size()/2).asInteger, 2)})}).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufScale.schelp b/release-packaging/HelpSource/Classes/FluidBufScale.schelp index e24a528..e604433 100644 --- a/release-packaging/HelpSource/Classes/FluidBufScale.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufScale.schelp @@ -59,30 +59,49 @@ Nothing, as the destination buffer is declared in the function call. EXAMPLES:: code:: -// make a buffer of know qualities -b = Buffer.sendCollection(s,1.0.series(1.1,2.0)) -// and a destination buffer -c = Buffer(s) -// play with the scaling -FluidBufScale.process(s, b, destination: c, inputLow: 0, inputHigh: 1, outputLow: 20, outputHigh:10) -// retrieve the buffer and enjoy the results. -c.getn(0,10,{|x|x.postln;}) +( +Routine{ + // make a buffer of known qualities + b = Buffer.sendCollection(s,1.0.series(1.1,2.0)); + // and a destination buffer + c = Buffer(s); + // play with the scaling + FluidBufScale.process(s, b, destination: c, inputLow: 0, inputHigh: 1, outputLow: 20, outputHigh:10).wait; + // retrieve the buffer and enjoy the results. + c.getn(0,10,{|x|x.postln;}) +}.play +) // also works in multichannel - explore the following buffer -b = Buffer.sendCollection(s,-10.0.series(-9,10.0).scramble,2) -b.plot.plotMode_(\points) + //process -FluidBufScale.process(s, b, destination: c, inputLow: -20, inputHigh: 20, outputLow: 0, outputHigh:1) -//enjoy - same shape, different range -c.plot.plotMode_(\points) +( +Routine{ + b = Buffer.sendCollection(s,-10.0.series(-9,10.0).scramble,2); + c = Buffer(s); + s.sync; + defer{b.plot(bounds:Rect(400,400,400,400)).plotMode_(\points).bounds}; + FluidBufScale.process(s, b, destination: c, inputLow: -20, inputHigh: 20, outputLow: 0, outputHigh:1).wait; + //enjoy - same shape, different range + defer{c.plot(bounds:Rect(800,400,400,400)).plotMode_(\points)}; +}.play; +) //also works with a subset of the input, resizing the output -b = Buffer.sendCollection(s,0.0.series(0.1,3.0).reshape(3,10).flop.flat,3) -b.plot(separately: true).plotMode_(\points) -//process -FluidBufScale.process(s, b, startFrame: 3,numFrames: 4,startChan: 1,numChans: 1, destination: c, inputLow: 0, inputHigh: 3, outputLow: 0, outputHigh:1) -//enjoy -c.plot(separately: true).plotMode_(\points) -c.query -c.getn(0,4,{|x|x.postln;}) +( +Routine{ + b = Buffer.sendCollection(s,0.0.series(0.1,3.0).reshape(3,10).flop.flat,3); + c = Buffer(s); + s.sync; + defer{b.plot(separately: true,bounds:Rect(400,400,400,400)).plotMode_(\points)}; + //process + FluidBufScale.process(s, b, startFrame: 3,numFrames: 4,startChan: 1,numChans: 1, destination: c, inputLow: 0, inputHigh: 3, outputLow: 0, outputHigh:1).wait; + //enjoy + c.query; + c.getn(0,4,{|x|x.postln;}); + defer{c.plot(separately: true,bounds:Rect(800,400,400,400)).plotMode_(\points)}; +}.play +) :: + + diff --git a/release-packaging/HelpSource/Classes/FluidBufSines.schelp b/release-packaging/HelpSource/Classes/FluidBufSines.schelp index 152d651..89d34d2 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSines.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSines.schelp @@ -102,7 +102,7 @@ d = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufSines.process(s, b, sines: c, residual:d); + FluidBufSines.process(s, b, sines: c, residual:d).wait; (Main.elapsedTime - t).postln; }.play ) @@ -135,7 +135,7 @@ d = Buffer.new(s); e = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufSines.process(s, b, sines: d, residual:e, windowSize: 2048, hopSize: 256, fftSize: 16384); + FluidBufSines.process(s, b, sines: d, residual:e, windowSize: 2048, hopSize: 256, fftSize: 16384).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp index e85d93f..025551c 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp @@ -83,7 +83,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufSpectralShape.process(s, b, features: c); + FluidBufSpectralShape.process(s, b, features: c).wait; (Main.elapsedTime - t).postln; }.play ) @@ -113,7 +113,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufSpectralShape.process(s, b, features: c); + FluidBufSpectralShape.process(s, b, features: c).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufStats.schelp b/release-packaging/HelpSource/Classes/FluidBufStats.schelp index 8f378ad..8b4cb46 100644 --- a/release-packaging/HelpSource/Classes/FluidBufStats.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufStats.schelp @@ -73,7 +73,7 @@ STRONG::A didactic example:: CODE:: -// make a buffer of known lenght +// make a buffer of known length b = Buffer.alloc(s,101); // add known values - here, a ramp up @@ -86,7 +86,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufStats.process(s, b, stats:c, numDerivs:1); + FluidBufStats.process(s, b, stats:c, numDerivs:1).wait; (Main.elapsedTime - t).postln; }.play ) @@ -144,11 +144,13 @@ g= Array.new; Routine({ e.doAdjacentPairs({ arg start,end; - FluidBufStats.processBlocking(s,c,(start/512).asInteger,((end-start)/512).max(2).asInteger,0,1,d,1, action: {d.loadToFloatArray(action: { - arg array; - g = g.add(array[12]); - "% % %\n".postf((start/512).asInteger,((end-start)/512).max(2).asInteger, array[12]); - })}); + FluidBufStats.processBlocking(s,c,(start/512).asInteger,((end-start)/512).max(2).asInteger,0,1,d,1, + action: {d.loadToFloatArray(action: { + arg array; + g = g.add(array[12]); + "% % %\n".postf((start/512).asInteger,((end-start)/512 ).max(2).asInteger, array[12]);}) + } + ).wait; }); "Done".postln; }).play; @@ -182,10 +184,11 @@ c = Buffer.new(s); // run the stats and send back the values FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames * c.numChannels,{|item|d = item; d.postln})}); +OSCFunc.allEnabled //looking at the result is not easy to grasp, since it is interleaved: first number is mean of L, second is mean of R, third is stddev of L, fourth is stddev or R //this will make it tidier - the first value of each line is Left, the second is Right -d.reshape(14,2).do({|x,i|["mean\t\t","stddev\t\t","skew\t\t\t", "kurtosis\t", "min\t\t\t", "median\t\t", "max\t\t\t","d-mean\t","d-stddev\t","d-skew\t\t", "d-kurtosis", "d-min\t\t", "d-median\t", "d-max\t\t"].at(i).post;x.round(0.01).postln});"".postln; +d.reshape(14,2).do({|x,i|["mean\t\t","stddev\t\t","skew\t\t", "kurtosis\t\t", "min\t\t\t", "median\t\t", "max\t\t\t","d-mean\t","d-stddev\t","d-skew\t\t", "d-kurtosis", "d-min\t\t", "d-median\t", "d-max\t\t"].at(i).post;x.round(0.01).postln});"".postln; :: STRONG::Outliers and Weights:: diff --git a/release-packaging/HelpSource/Classes/FluidBufThreadDemo.schelp b/release-packaging/HelpSource/Classes/FluidBufThreadDemo.schelp index ea798a9..d067f25 100644 --- a/release-packaging/HelpSource/Classes/FluidBufThreadDemo.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufThreadDemo.schelp @@ -56,11 +56,11 @@ CODE:: b=Buffer.alloc(s,1); // a simple call, where we query the destination buffer upon completion with the action message. -FluidBufThreadDemo.process(s, b, 1000, {|x|x.get(0,{|y|y.postln});}); +FluidBufThreadDemo.process(s, b, 1000, action:{|x|x.get(0,{|y|y.postln});}); -// as the 'process' returns its parent UGen, we can cancel the process easily -c = FluidBufThreadDemo.process(s, b, 100000, {|x|x.get(0,{|y|y.postln});}); -c.cancel //it stops silently for now but check the synth count going down by 1. +// as the 'process' returns its instance, we can cancel the process easily +c = FluidBufThreadDemo.process(s, b, 100000, action: {|x|x.get(0,{|y|y.postln});}); +c.cancel; // if a simple call to the UGen is used, the progress can be monitored. The usual cmd. will cancel the job by freeing the synth. {c = FluidBufThreadDemo.kr(b,10000, Done.freeSelf); Poll.kr(Impulse.kr(2),c);}.scope; diff --git a/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp index 709a058..a5e6b2f 100644 --- a/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp @@ -85,7 +85,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufTransientSlice.process(s,b, indices:c); + FluidBufTransientSlice.process(s,b, indices:c).wait; (Main.elapsedTime - t).postln; }.play ) @@ -111,7 +111,7 @@ c.query; ( Routine{ t = Main.elapsedTime; - FluidBufTransientSlice.process(s,b, 0, 220500, 0, 1, c, 200, 2048, 1024, 1, 3, 1, 15, 30, 4410); + FluidBufTransientSlice.process(s,b, 0, 220500, 0, 1, c, 200, 2048, 1024, 1, 3, 1, 15, 30, 4410).wait; (Main.elapsedTime - t).postln; }.play ) @@ -138,7 +138,7 @@ c = Buffer.new(s); // with basic params Routine{ t = Main.elapsedTime; - FluidBufTransientSlice.process(s,b, indices: c, threshFwd: 1.2); + FluidBufTransientSlice.process(s,b, indices: c, threshFwd: 1.2).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufTransients.schelp b/release-packaging/HelpSource/Classes/FluidBufTransients.schelp index 4f71858..e984645 100644 --- a/release-packaging/HelpSource/Classes/FluidBufTransients.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufTransients.schelp @@ -89,7 +89,7 @@ d = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufTransients.process(s,b, transients:c, residual:d); + FluidBufTransients.process(s,b, transients:c, residual:d).wait; (Main.elapsedTime - t).postln; }.play ); @@ -106,7 +106,7 @@ d.play; ( Routine{ t = Main.elapsedTime; - FluidBufTransients.process(s,b, 0, 220500, 0, 1, c, d, 200, 2048, 1024, 1, 3, 1, 15, 30); + FluidBufTransients.process(s,b, 0, 220500, 0, 1, c, d, 200, 2048, 1024, 1, 3, 1, 15, 30).wait; (Main.elapsedTime - t).postln; }.play ) @@ -136,7 +136,7 @@ d = Buffer.new(s); e = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufTransients.process(s, b, transients: d, residual:e, threshFwd:1.2, clumpLength:40); + FluidBufTransients.process(s, b, transients: d, residual:e, threshFwd:1.2, clumpLength:40).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidDataSet.schelp b/release-packaging/HelpSource/Classes/FluidDataSet.schelp index a0029ce..4e6818e 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSet.schelp @@ -82,15 +82,17 @@ CODE:: s.boot; ( fork{ - ~ds = FluidDataSet.new(s,\simple1d_1); + ~ds = FluidDataSet.new(s); ~point = Buffer.alloc(s,1,1); s.sync; 10.do{|i| ~point.set(0,i); ~ds.addPoint(i.asString,~point,{("addPoint"+i).postln}); - s.sync; + s.sync; }; - ~ds.dump; s.sync; ~ds.free; + ~ds.dump; + s.sync; + ~ds.free; }; ) @@ -100,39 +102,39 @@ d = Dictionary.new; d.add(\cols -> 1); d.add(\data -> Dictionary.newFrom(10.collect{|i|[i.asString, [i.asFloat]]}.flatten)); fork{ - ~ds = FluidDataSet.new(s,\simple1d_2); s.sync; - ~ds.load(d); s.sync; - ~ds.dump; s.sync; ~ds.free; + ~ds = FluidDataSet.new(s); s.sync; + ~ds.load(d); s.sync; + ~ds.dump; s.sync; ~ds.free; } ) -// Using synth +// Using a synth ( -~ds = FluidDataSet.new(s,\simple1d_3); +~ds = FluidDataSet.new(s); { - var trig = Impulse.kr(20); - var count = PulseCount.kr(trig) - 1; - var buf = LocalBuf(1); - BufWr.kr(count, buf); - FluidDataSetWr.kr(\simple1d_3, buf: buf, trig: trig); - FreeSelf.kr(count - 8); -}.play.onFree{~ds.dump{|o| o.postln; ~ds.free}} + var trig = Impulse.kr(20); + var count = PulseCount.kr(trig) - 1; + var buf = LocalBuf(1); + BufWr.kr(count, buf); + FluidDataSetWr.kr(~ds.asUGenInput, buf: buf, trig: trig); + FreeSelf.kr(count - 8); +}.play.onFree{~ds.dump{|o| o.postln;~ds.free}} ) :: STRONG:: Merging Datasets:: - +s.dumpOSC code:: //this is how to add items between 2 datasets. //create 2 datasets ( -~dsA = FluidDataSet.new(s,\simple1d_4a); -~dsB = FluidDataSet.new(s,\simple1d_4b); +~dsA = FluidDataSet.new(s); +~dsB = FluidDataSet.new(s); ) - +Dictionary.new //feed them items with same dimensions but different labels -~dsA.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\one,[1],\two,[2]])])); -~dsB.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\three,[3],\four,[4]])])); +~dsA.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\one,1,\two,2])])); +~dsB.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\three,3,\four,4])])); ~dsA.print; ~dsB.print; @@ -141,8 +143,8 @@ code:: ~dsB.print; //change the content of the dataset to shared labels -~dsA.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\three,[333],\four,[444]])])); -~dsB.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\three,[3],\four,[4]])])); +~dsA.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\three,333,\four,444])])); +~dsB.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\three,3,\four,4])])); ~dsA.print; ~dsB.print; diff --git a/release-packaging/HelpSource/Classes/FluidDataSetQuery.schelp b/release-packaging/HelpSource/Classes/FluidDataSetQuery.schelp index 932443e..9b1d57d 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSetQuery.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSetQuery.schelp @@ -108,10 +108,8 @@ EXAMPLES:: code:: s.reboot; - - // Create a DataSet with random data -~dataSet= FluidDataSet(s,\help_fluid_dataset_query); +~dataSet= FluidDataSet(s); ( ~points = 100.collect{|i|5.collect{|j|j+(i/100)}}; @@ -122,8 +120,9 @@ fork{ s.sync; ~points.do{|x,i| ~tmpbuf.setn(0,x); + s.sync; ~dataSet.addPoint(i,~tmpbuf); - s.sync + // s.sync } } ) @@ -133,11 +132,12 @@ fork{ // Prepare a FluidDataSetQuery object ~query = FluidDataSetQuery.new; -~out = FluidDataSet(s,\help_fluid_dataset_query_out); +~out = FluidDataSet(s); // prepare a simple query ~query.filter(0,"<",0.04); ~query.addColumn(2); +s.dumpOSC ~query.transform(~dataSet, ~out); // check the result diff --git a/release-packaging/HelpSource/Classes/FluidKDTree.schelp b/release-packaging/HelpSource/Classes/FluidKDTree.schelp index f639ca8..45bfe03 100644 --- a/release-packaging/HelpSource/Classes/FluidKDTree.schelp +++ b/release-packaging/HelpSource/Classes/FluidKDTree.schelp @@ -48,8 +48,6 @@ A function that will run when the query returns, whose argument is an array of d EXAMPLES:: code:: - - // Make a DataSet of random 2D points s.reboot; ( @@ -117,13 +115,40 @@ fork{ ) :: -subsection:: Server Side Queries +subsection:: Queries in a Synth + +Input and output is done via buffers, which will need to be preallocated to the correct sizes: +LIST:: +##Your input buffer should be sized to the input data dimension (2, in this example) +##Your output buffer should be the number of neighbours * output dimensionality +:: + +We can't simply return labels (i.e. strings) from a UGen, so the query returns the actual data points from a DataSet instead. By default, this is the FluidDataSet against which the tree was fitted. However, by passing a different dataset to code::kr::'s code::lookupDataSet:: argument instead, you can return different points, so long as the labels in the two datasets match. In this way, the FluidKDTree can be used to perform nearest neighbour mappings in a synth. + +For instance, whilst fitting the tree against some n-dimensional descriptor data, our lookup dataset could use the same labels to map descriptor entries back to buffers, or locations in buffers, so that queries can be used to trigger audio. code:: -//populate the lookupDataSet + +( +Routine{ + var inputBuffer = Buffer.alloc(s,2); + var outputBuffer = Buffer.alloc(s,10);//5 neighbours * 2D data points + s.sync; + { + var trig = Impulse.kr(4); //can go as fast as ControlRate.ir/2 + var point = 2.collect{TRand.kr(0,1,trig)}; + point.collect{|p,i| BufWr.kr([p],inputBuffer,i)}; + ~tree.kr(trig,inputBuffer,outputBuffer,5,nil); + Poll.kr(trig, BufRd.kr(1,outputBuffer,Array.iota(5)),5.collect{|i| "Neighbour" + i}); + Silent.ar; + }.play; +}.play; +) + +//Using a lookup data set instead: //here we populate with numbers that are in effect the indicies, but it could be anything numerical that will be returned on the server-side and would be usable on that side ( -~dsL = FluidDataSet.new(s,\kdtree_help_indices); +~dsL = FluidDataSet.new(s); fork{ d = Dictionary.with( *[\cols -> 1,\data -> Dictionary.newFrom( @@ -132,31 +157,23 @@ fork{ ~dsL.load(d, {~dsL.print}); } ) - -// instantiate a tree with a lookup dataset. Note that this 'association' has to be done at instantiation. -~tree = FluidKDTree(s,numNeighbours:5, lookupDataSet:~dsL); -~tree.fit(~ds) - -//set the buffers and busses needed +~dsL.asUGenInput +s.dumpOSC +// Create the buffers, and make a synth, querying our tree with some random points ( -~inputPoint = Buffer.alloc(s,2); -~predictPoint = Buffer.alloc(s,5); -~pitchingBus = Bus.control; -~catchingBus = Bus.control; -) - -( -~tree.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~inputPoint).outBuffer_(~predictPoint); -{ - var trig = Impulse.kr(4); //can go as fast as ControlRate.ir/2 - var point = 2.collect{TRand.kr(0,1,trig)}; - point.collect{|p,i| BufWr.kr([p],~inputPoint,i)}; - Poll.kr(trig,point); - Out.kr(~pitchingBus.index,[trig]); - Poll.kr(In.kr(~catchingBus.index),BufRd.kr(1,~predictPoint,Array.iota(5))); - Silent.ar; -}.play(~tree.synth,addAction:\addBefore); - +Routine{ + var inputBuffer = Buffer.alloc(s,2); + var outputBuffer = Buffer.alloc(s,5);//5 neighbours * 1D points + s.sync; + { + var trig = Impulse.kr(4); //can go as fast as ControlRate.ir/2 + var point = 2.collect{TRand.kr(0,1,trig)}; + point.collect{|p,i| BufWr.kr([p],inputBuffer,i)}; + ~tree.kr(trig,inputBuffer,outputBuffer,5,~dsL); + Poll.kr(trig, BufRd.kr(1,outputBuffer,Array.iota(5)),5.collect{|i| "Neighbour" + i}); + Silent.ar; + }.play; +}.play; ) :: diff --git a/release-packaging/HelpSource/Classes/FluidKMeans.schelp b/release-packaging/HelpSource/Classes/FluidKMeans.schelp index 32f13e7..547a21d 100644 --- a/release-packaging/HelpSource/Classes/FluidKMeans.schelp +++ b/release-packaging/HelpSource/Classes/FluidKMeans.schelp @@ -87,7 +87,7 @@ fork{ // Create a KMeans instance and a LabelSet for the cluster labels in the server -~clusters = FluidLabelSet(s,\kmeans_help_clusters); +~clusters = FluidLabelSet(s); ~kmeans = FluidKMeans(s); // Fit into 4 clusters @@ -101,10 +101,15 @@ fork{ ) // Cols of kmeans should match DataSet, size is the number of clusters + ~kmeans.cols; ~kmeans.size; ~kmeans.dump; + ~clusters.getLabel(0,{|clusterID| + (0.asString+clusterID).postln; +}); + // Retrieve labels of clustered points ( ~assignments = Array.new(128); @@ -147,57 +152,38 @@ w.front; ~kmeans.predictPoint(~inbuf,{|x|x.postln;}); :: -subsection:: Server Side Queries +subsection:: Queries in a Synth This is the equivalent of predictPoint, but wholly on the server -FluidKMeans is accessed via its own synth, so we need to use -a bus to communicate with it. The inBus receives a trigger to query, using data -from inBuffer; a trigger is then send to outBus with the prediction in outBuffer + code:: -( -~ib = Bus.control(s); // input bus -~ob = Bus.control(s); //output bus -~tempPoint = Buffer.alloc(s,1,2); -~inpPoint = Buffer.alloc(s,2); -~outPoint = Buffer.alloc(s,1); -) -//We make two Synths. One, before FluidKMeans, generates a random point and sends -//a trigger to query. The second, after FluidKMeans, gives us the predicted cluster //triggering upadtes from the outBus +outputPoint.getToFloatArray(action:{|a|a.postln}) ( -//Set properties on FluidKMeans: -~kmeans.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); -//pitching -{ - var trig = Impulse.kr(5); - var point = WhiteNoise.kr(1.dup); - var copied; - Poll.kr(trig, point, [\pointX,\pointY]); - point.collect{ |p,i| BufWr.kr([p],~inpPoint,i)}; - Out.kr(~ib.index,[trig]); -}.play(~kmeans.synth,addAction:\addBefore); -//catching { - Poll.kr(In.kr(~ob),Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)),\cluster); -}.play(~kmeans.synth,addAction:\addAfter); + var trig = Impulse.kr(5); + var point = WhiteNoise.kr(1.dup); + var inputPoint = LocalBuf(2); + var outputPoint = LocalBuf(1); + Poll.kr(trig, point, [\pointX,\pointY]); + point.collect{ |p,i| BufWr.kr([p],inputPoint,i)}; + ~kmeans.kr(trig,inputPoint,outputPoint); + Poll.kr(trig,BufRd.kr(1,outputPoint,0,interpolation:0),\cluster); +}.play; ) // to sonify the output, here are random values alternating quadrant, generated more quickly as the cursor moves rightwards ( -//Set properties on FluidKMeans: -~kmeans.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); -//pitching { var trig = Impulse.kr(MouseX.kr(0,1).exprange(0.5,ControlRate.ir / 2)); var step = Stepper.kr(trig,max:3); var point = TRand.kr(-0.1, [0.1, 0.1], trig) + [step.mod(2).linlin(0,1,-0.6,0.6),step.div(2).linlin(0,1,-0.6,0.6)] ; - point.collect{|p,i| BufWr.kr([p],~inpPoint,i)}; - Out.kr(~ib.index,[trig]); - T2A.ar(trig)*0.1; -}.play(~kmeans.synth,addAction:\addBefore); -//catching -{ - SinOsc.ar((Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)) + 69).midicps,mul: 0.1); -}.play(~kmeans.synth,addAction:\addAfter); + var inputPoint = LocalBuf(2); + var outputPoint = LocalBuf(1); + point.collect{|p,i| BufWr.kr([p],inputPoint,i)}; + ~kmeans.kr(trig,inputPoint,outputPoint); + SinOsc.ar((BufRd.kr(1,outputPoint,0,interpolation:0) + 69).midicps,mul: 0.1); +}.play; ) + :: diff --git a/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp b/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp index 017c452..9f882a1 100644 --- a/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp +++ b/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp @@ -57,12 +57,12 @@ code:: ( ~classifier = FluidKNNClassifier(s); -~source= FluidDataSet(s,\knnclassify_help_examples); -~labels = FluidLabelSet(s,\knnclassify_help_labels); -~test = FluidDataSet(s,\knnclassify_help_test); -~mapping = FluidLabelSet(s,\knnclassify_help_mapping); +~source= FluidDataSet(s); +~labels = FluidLabelSet(s); +~test = FluidDataSet(s); +~mapping = FluidLabelSet(s); ) - +s.dumpOSC //Make some clumped 2D points and place into a DataSet ( ~examplepoints = [[0.5,0.5],[-0.5,0.5],[0.5,-0.5],[-0.5,-0.5]]; @@ -149,52 +149,33 @@ w.front; subsection::Server Side Queries This is the equivalent of predictPoint, but wholly on the server -FluidKNNClassifier is accessed via its own synth, so we need to use -a bus to communicate with it. The inBus receives a trigger to query, using data -from inBuffer; a trigger is then send to outBus with the prediction in outBuffer code:: -( -~ib = Bus.control(s); // input bus -~ob = Bus.control(s); //output bus -~inpPoint = Buffer.alloc(s,2); -~outPoint = Buffer.alloc(s,1); -) -//We make two Synths. One, before FluidKMeans, generates a random point and sends -//a trigger to query. The second, after FluidKMeans, gives us the predicted cluster //triggering upadtes from the outBus +//Generate a random point and sends a trigger to query, and return the class that point matches ( -//Set properties on FluidKNNClassifier: -~classifier.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); -//pitching { var trig = Impulse.kr(5); var point = WhiteNoise.kr(1.dup); + var inputPoint = LocalBuf(2); + var outputPoint = LocalBuf(1); Poll.kr(trig, point, [\pointX,\pointY]); - point.collect{ |p,i| BufWr.kr([p],~inpPoint,i)}; - Out.kr(~ib.index,[trig]); -}.play(~classifier.synth,addAction:\addBefore); -//catching -{ - Poll.kr(In.kr(~ob),Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)),\cluster); -}.play(~classifier.synth,addAction:\addAfter); + point.collect{ |p,i| BufWr.kr([p],inputPoint,i)}; + ~classifier.kr(trig,inputPoint,outputPoint); + Poll.kr(trig,BufRd.kr(1,outputPoint,0,interpolation:0),\cluster); +}.play; ) // to sonify the output, here are random values alternating quadrant. ( -//Set properties on FluidKNNClassifier: -~classifier.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); -//pitching { var trig = Impulse.kr(MouseX.kr(0,1).exprange(0.5,ControlRate.ir /2).poll(trig:2, label: "Query Frequency")); var step = Stepper.kr(trig,max:3); var point = TRand.kr(-0.1, [0.1, 0.1], trig) + [step.mod(2).linlin(0,1,-0.6,0.6),step.div(2).linlin(0,1,-0.6,0.6)] ; - point.collect{|p,i| BufWr.kr([p],~inpPoint,i)}; - Out.kr(~ib.index,[trig]); - T2A.ar(trig)*0.1; -}.play(~classifier.synth,addAction:\addBefore); -//catching -{ - SinOsc.ar((Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)) + 69).midicps,mul: 0.1); -}.play(~classifier.synth,addAction:\addAfter); + var inputPoint = LocalBuf(2); + var outputPoint = LocalBuf(1); + point.collect{|p,i| BufWr.kr([p],inputPoint,i)}; + ~classifier.kr(trig,inputPoint,outputPoint); + SinOsc.ar((BufRd.kr(1,outputPoint,0,interpolation:0) + 69).midicps,mul: 0.1); +}.play ) :: diff --git a/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp b/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp index b138593..9bde42d 100644 --- a/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp +++ b/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp @@ -53,10 +53,10 @@ code:: //Make a simple mapping between a ramp and a sine cycle, test with an exponentional ramp ( -~source = FluidDataSet(s,\knn_regress_src); -~target = FluidDataSet(s,\knn_regress_tgt); -~test = FluidDataSet(s,\knn_regress_test); -~output = FluidDataSet(s,\knn_regress_out); +~source = FluidDataSet(s); +~target = FluidDataSet(s); +~test = FluidDataSet(s); +~output = FluidDataSet(s); ~tmpbuf = Buffer.alloc(s,1); ~regressor = FluidKNNRegressor(s); ) @@ -106,7 +106,7 @@ d = Dictionary.with( //We should see a single cycle of a chirp ~outputdata.plot; - +s.dumpOSC // single point transform on arbitrary value ~inbuf = Buffer.loadCollection(s,[0.5]); ~regressor.predictPoint(~inbuf,{|x|x.postln;}); @@ -117,28 +117,15 @@ subsection:: Server Side Queries code:: //Setup ( -~inputPoint = Buffer.alloc(s,1); -~predictPoint = Buffer.alloc(s,1); -~pitchingBus = Bus.control; -~catchingBus = Bus.control; -) - -( -~regressor.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~inputPoint).outBuffer_(~predictPoint); - -~inputSynth = { +{ var input = Saw.kr(2).linlin(-1,1,0,1); var trig = Impulse.kr(ControlRate.ir/10); - BufWr.kr(input,~inputPoint,0); - Out.kr(~pitchingBus.index,[trig]); -}; - -~inputSynth.play(~regressor.synth,addAction:\addBefore); - -~outputSynth = { - Poll.kr(In.kr(~catchingBus.index),BufRd.kr(1,~predictPoint,0),"mapped value") -}; -~outputSynth.play(~regressor.synth,addAction:\addAfter); -~outputSynth.scope + var inputPoint = LocalBuf(1); + var outputPoint = LocalBuf(1); + BufWr.kr(input,inputPoint,0); + ~regressor.kr(trig,inputPoint,outputPoint); + BufRd.kr(1,outputPoint,0);//,"mapped value") +}.scope ) + :: diff --git a/release-packaging/HelpSource/Classes/FluidLabelSet.schelp b/release-packaging/HelpSource/Classes/FluidLabelSet.schelp index 8170ce1..a29d3d6 100644 --- a/release-packaging/HelpSource/Classes/FluidLabelSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidLabelSet.schelp @@ -56,7 +56,7 @@ Post an abbreviated content of the label set in the window by default, but you c EXAMPLES:: code:: -~ls = FluidLabelSet.new(s,\labelset_example); +~ls = FluidLabelSet.new(s); ["one", "two", "three"].collect{|x,i| ~ls.addLabel(i, x);}; ~ls.print; diff --git a/release-packaging/HelpSource/Classes/FluidMDS.schelp b/release-packaging/HelpSource/Classes/FluidMDS.schelp index 0107f2e..362521a 100644 --- a/release-packaging/HelpSource/Classes/FluidMDS.schelp +++ b/release-packaging/HelpSource/Classes/FluidMDS.schelp @@ -73,7 +73,6 @@ code:: ~mds = FluidMDS(s); ) - // Load audio and run an mfcc analysis, which gives us 13 points (we'll throw the 0th away) ( ~audio = Buffer.read(s,~audiofile); @@ -90,16 +89,16 @@ FluidBufMFCC.process(s,~audio, features: ~mfcc_feature); var chunkLen = (~mfcc_feature.numFrames / 100).asInteger; var stats = FluidBufStats.kr( source: ~mfcc_feature, startFrame: count * chunkLen, - startChan:1, numFrames: chunkLen, stats: ~stats, trig: trig + startChan:1, numFrames: chunkLen, stats: ~stats, trig: trig,blocking:1 ); var rd = BufRd.kr(12, ~stats, DC.kr(0), 0, 1); var bufWr, dsWr; 12.do{|i| bufWr = BufWr.kr(rd[i], buf, DC.kr(i)); }; - dsWr = FluidDataSetWr.kr(\mds_help_12D, buf: buf, trig: Done.kr(stats)); - LocalOut.kr( Done.kr(dsWr)); - FreeSelf.kr(count - 99); + dsWr = FluidDataSetWr.kr(~raw, buf: buf, trig: Done.kr(stats),blocking:1); + LocalOut.kr(Done.kr(dsWr)); + FreeSelf.kr(count - 99); Poll.kr(trig,count); }.play; ) @@ -109,12 +108,14 @@ FluidBufMFCC.process(s,~audio, features: ~mfcc_feature); //Then apply the MDS in-place on the standardized data to get 2 dimensions, using a Euclidean distance metric //Download the DataSet contents into an array for plotting ( + ~reducedarray = Array.new(100); ~standardizer.fitTransform(~raw, ~standardized); ~mds.fitTransform(~standardized, ~reduced, action:{ - ~reduced.dump{|x| 100.do{|i| - ~reducedarray.add(x["data"][i.asString]) - }}}); + "HETE".postln; + ~reduced.dump{|x| 100.do{|i| + ~reducedarray.add(x["data"][i.asString]) +}}}); ) //Visualise the 2D projection of our original 12D data @@ -175,5 +176,4 @@ w.drawFunc = { w.refresh; w.front; ) - :: diff --git a/release-packaging/HelpSource/Classes/FluidMLPClassifier.schelp b/release-packaging/HelpSource/Classes/FluidMLPClassifier.schelp index 68f9074..6522cd9 100644 --- a/release-packaging/HelpSource/Classes/FluidMLPClassifier.schelp +++ b/release-packaging/HelpSource/Classes/FluidMLPClassifier.schelp @@ -86,11 +86,11 @@ EXAMPLES:: code:: ( -~classifier = FluidMLPClassifier(s, [6], FluidMLPClassifier.tanh, 1000, 0.1, 0.1, 50, 0); -~sourcedata= FluidDataSet(s,\mlpclassify_help_examples); -~labels = FluidLabelSet(s,\mlpclassify_help_labels); -~testdata = FluidDataSet(s,\mlpclassify_help_test); -~predictedlabels = FluidLabelSet(s,\mlpclassify_help_mapping); +~classifier=FluidMLPClassifier(s).hidden_([6]).activation_(FluidMLPClassifier.tanh).maxIter_(1000).learnRate_(0.1).momentum_(0.1).batchSize_(50).validation_(0); +~sourcedata= FluidDataSet(s); +~labels = FluidLabelSet(s); +~testdata = FluidDataSet(s); +~predictedlabels = FluidLabelSet(s); ) //Make some clumped 2D points and place into a DataSet ( @@ -104,8 +104,8 @@ code:: ~labeldata.put("mlpclass"++i++\_++j,[~categories[i]]); } }; -~sourcedata.load(Dictionary.with(*[\cols->2,\data->~trainingset])); -~labels.load(Dictionary.with(*[\cols->1,\data->~labeldata])); +~sourcedata.load(Dictionary.with(*[\cols->2,\data->~trainingset]),action:{~sourcedata.print}); +~labels.load(Dictionary.with(*[\cols->1,\data->~labeldata]),action:{~labels.print}); ) //Fit the classifier to the example DataSet and labels, and then run prediction on the test data into our mapping label set @@ -119,14 +119,16 @@ code:: ~testset.put("mlpclass_test"++i++\_++j, ~centroids[i].collect{|x| x.gauss(0.5/3)}); } }; -~testdata.load(Dictionary.with(*[\cols->2,\data->~testset])); +~testdata.load(Dictionary.with(*[\cols->2,\data->~testset]),action:{~testdata.print}); ) //Run the test data through the network, into the predicted labelset ~classifier.predict(~testdata,~predictedlabels,action:{"Test complete".postln}); - +OSCFunc.trace(true,true) +OSCFunc.allEnabled //get labels from server -~predictedlabels.dump(action:{|d| ~labelsdict = d["data"]}); +~predictedlabels.dump(action:{|d|~labelsdict = d["data"]};~labelsdict.postln); + //Visualise: we're hoping to see colours neatly mapped to quandrants... ( c = Dictionary(); @@ -155,56 +157,34 @@ w.front; ~inbuf = Buffer.loadCollection(s,0.5.dup); ~classifier.predictPoint(~inbuf,{|x|x.postln;}); :: - - -subsection::Server Side Queries -This is the equivalent of predictPoint, but wholly on the server -FluidMLPClassifier is accessed via its own synth, so we need to use -a bus to communicate with it. The inBus receives a trigger to query, using data -from inBuffer; a trigger is then send to outBus with the prediction in outBuffer +subsection::Querying in a Synth +This is the equivalent of code::predictPoint::, but wholly on the server code:: ( -~ib = Bus.control(s); // input bus -~ob = Bus.control(s); //output bus -~inpPoint = Buffer.alloc(s,2); -~outPoint = Buffer.alloc(s,1); -) - -//We make two Synths. One, before FluidKMeans, generates a random point and sends -//a trigger to query. The second, after FluidKMeans, gives us the predicted cluster //triggering upadtes from the outBus -( -//Set properties on FluidMLPClassifier: -~classifier.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); -//pitching { var trig = Impulse.kr(5); var point = WhiteNoise.kr(1.dup); + var inputPoint = LocalBuf(2); + var outputPoint = LocalBuf(1); Poll.kr(trig, point, [\pointX,\pointY]); - point.collect{ |p,i| BufWr.kr([p],~inpPoint,i)}; - Out.kr(~ib.index,[trig]); -}.play(~classifier.synth,addAction:\addBefore); -//catching -{ - Poll.kr(In.kr(~ob),Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)),\cluster); -}.play(~classifier.synth,addAction:\addAfter); + point.collect{ |p,i| BufWr.kr([p],inputPoint,i)}; + ~classifier.kr(trig,inputPoint,outputPoint); + Poll.kr(trig,BufRd.kr(1,outputPoint,0,interpolation:0),\cluster); + Silent.ar; +}.play; ) // to sonify the output, here are random values alternating quadrant. ( -//Set properties on FluidMLPClassifier: -~classifier.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); -//pitching { var trig = Impulse.kr(MouseX.kr(0,1).exprange(0.5,ControlRate.ir /2).poll(trig: 2,label: "Query Frequency")); var step = Stepper.kr(trig,max:3); var point = TRand.kr(-0.1, [0.1, 0.1], trig) + [step.mod(2).linlin(0,1,-0.6,0.6),step.div(2).linlin(0,1,-0.6,0.6)] ; - point.collect{|p,i| BufWr.kr([p],~inpPoint,i)}; - Out.kr(~ib.index,[trig]); - T2A.ar(trig)*0.1; -}.play(~classifier.synth,addAction:\addBefore); -//catching -{ - SinOsc.ar((Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)) + 69).midicps,mul: 0.1); -}.play(~classifier.synth,addAction:\addAfter); + var inputPoint = LocalBuf(2); + var outputPoint = LocalBuf(1); + point.collect{|p,i| BufWr.kr([p],inputPoint,i)}; + ~classifier.kr(trig,inputPoint,outputPoint); + SinOsc.ar((BufRd.kr(1,outputPoint,0,interpolation:0) + 69).midicps,mul: 0.1) +}.play; ) :: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidMLPRegressor.schelp b/release-packaging/HelpSource/Classes/FluidMLPRegressor.schelp index a2d895f..2b8a8c0 100644 --- a/release-packaging/HelpSource/Classes/FluidMLPRegressor.schelp +++ b/release-packaging/HelpSource/Classes/FluidMLPRegressor.schelp @@ -101,12 +101,12 @@ code:: //Make a simple mapping between a ramp and a sine cycle, test with an exponentional ramp ( -~source = FluidDataSet(s,\mlp_regressor_source); -~target = FluidDataSet(s,\mlp_regressor_target); -~test = FluidDataSet(s,\mlp_regressor_dest); -~output = FluidDataSet(s,\mlp_regress_out); +~source = FluidDataSet(s); +~target = FluidDataSet(s); +~test = FluidDataSet(s); +~output = FluidDataSet(s); ~tmpbuf = Buffer.alloc(s,1); -~regressor = FluidMLPRegressor(s,[2], FluidMLPRegressor.tanh, FluidMLPRegressor.tanh, maxIter: 1000, learnRate: 0.1, momentum: 0.1, batchSize: 1, validation: 0); +~regressor=FluidMLPRegressor(s).hidden_([2]).activation_(FluidMLPRegressor.tanh).outputActivation_(FluidMLPRegressor.tanh).maxIter_(1000).learnRate_(0.1).momentum_(0.1).batchSize_(1).validation_(0); ) //Make source, target and test data @@ -153,10 +153,11 @@ d = Dictionary.with( ( ~outputdata = Array(128); ~regressor.predict(~test, ~output, action:{ - ~output.dump{|x| 128.do{|i| + ~output.dump{|x| 128.do{|i| ~outputdata.add(x["data"][i.asString][0]) }}; -}); +} +); ) //We should see a single cycle of a chirp. If not, fit a little more epochs @@ -166,37 +167,23 @@ d = Dictionary.with( ~inbuf = Buffer.loadCollection(s,[0.5]); ~outbuf = Buffer.new(s); ~regressor.predictPoint(~inbuf,~outbuf,{|x|x.postln;x.getn(0,1,{|y|y.postln;};)}); - :: -subsection:: Server Side Queries +subsection:: Querying in a Synth -code:: -//Setup -( -~inputPoint = Buffer.alloc(s,1); -~predictPoint = Buffer.alloc(s,1); -~pitchingBus = Bus.control; -~catchingBus = Bus.control; -) +This is the equivalent of calling code::predictPoint::, except wholly on the server +code:: ( -~regressor.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~inputPoint).outBuffer_(~predictPoint); - -~inputSynth = { +{ var input = Saw.kr(2).linlin(-1,1,0,1); var trig = Impulse.kr(ControlRate.ir/10); - BufWr.kr(input,~inputPoint,0); - Out.kr(~pitchingBus.index,[trig]); -}; - -~inputSynth.play(~regressor.synth,addAction:\addBefore); - -~outputSynth = { - Poll.kr(In.kr(~catchingBus.index),BufRd.kr(1,~predictPoint,0),"mapped value") -}; -~outputSynth.play(~regressor.synth,addAction:\addAfter); -~outputSynth.scope + var inputPoint = LocalBuf(1); + var outputPoint = LocalBuf(1); + BufWr.kr(input,inputPoint,0); + ~regressor.kr(trig,inputPoint,outputPoint,0,-1); + Poll.kr(trig,BufRd.kr(1,outputPoint,0),"mapped value"); +}.scope; ) diff --git a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp index fbe8375..db9c268 100644 --- a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp +++ b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp @@ -79,8 +79,7 @@ d.play //////(beware !!!! loud!!!) ( // separate them in 2 components Routine { - FluidBufNMF.process(s, d, bases: e, components:2); - s.sync; + FluidBufNMF.process(s, d, bases: e, components:2).wait; e.query; }.play ) @@ -112,7 +111,7 @@ c = Buffer.new(s); // train only 2 seconds ( Routine { - FluidBufNMF.process(s,b,0,88200,0,1, c, ~bases, components:10,fftSize:2048); + FluidBufNMF.process(s,b,0,88200,0,1, c, ~bases, components:10,fftSize:2048).wait; c.query; }.play; ) @@ -120,14 +119,15 @@ Routine { // wait for the query to print // find the component that has the picking sound checking the median spectral centroid ( -FluidBufSpectralShape.process(s, c, features: ~spectralshapes, action:{ - |shapes|FluidBufStats.process(s,shapes,stats:~stats, action:{ - |stats|stats.getn(0, (stats.numChannels * stats.numFrames) ,{ - |x| ~centroids = x.select({ - |item, index| (index.mod(7) == 0) && (index.div(70) == 5); - }) - }) - }) +FluidBufSpectralShape.process(s, c, features: ~spectralshapes, action:{ |shapes| + FluidBufStats.process(s,shapes,stats:~stats, action:{|stats| + stats.getn(0, (stats.numChannels * stats.numFrames), {|x| + ~centroids = x.select({ + |item, index| (index.mod(7) == 0) && (index.div(70) == 5); + }); + ~centroids.postln; + }) + }) }); ) diff --git a/release-packaging/HelpSource/Classes/FluidNormalize.schelp b/release-packaging/HelpSource/Classes/FluidNormalize.schelp index 6e2724c..9fce5bb 100644 --- a/release-packaging/HelpSource/Classes/FluidNormalize.schelp +++ b/release-packaging/HelpSource/Classes/FluidNormalize.schelp @@ -92,18 +92,18 @@ FluidBufPitch.process(s,~audio, features: ~pitch_feature); var chunkLen = (~pitch_feature.numFrames / 10).asInteger; var stats = FluidBufStats.kr( source: ~pitch_feature, startFrame: count * chunkLen, - numFrames: chunkLen, stats: ~stats, trig: trig + numFrames: chunkLen, stats: ~stats, trig: (trig * (count <=9)), blocking:1 ); var rd = BufRd.kr(2, ~stats, DC.kr(0), 0, 1);// pick only mean pitch and confidence var wr1 = BufWr.kr(rd[0], buf, DC.kr(0)); var wr2 = BufWr.kr(rd[1], buf, DC.kr(1)); - var dsWr = FluidDataSetWr.kr(\norm_help_raw, buf: buf, trig: Done.kr(stats)); + var dsWr = FluidDataSetWr.kr(~raw, buf: buf, trig: Done.kr(stats)); LocalOut.kr( Done.kr(dsWr)); + Poll.kr(trig,count,\count); FreeSelf.kr(count - 9); }.play; ) - // Normalize and load to language-side array ( ~rawarray = Array.new(10); @@ -130,42 +130,28 @@ FluidBufPitch.process(s,~audio, features: ~pitch_feature); ~inbuf = Buffer.loadCollection(s,0.5.dup); ~outbuf = Buffer.new(s); ~normalizer.transformPoint(~inbuf,~outbuf,{|x|x.postln;x.getn(0,2,{|y|y.postln;};)}); - +OSCFunc.trace(false,true) //Server side queries -//Setup -( -~tempPoint = Buffer.alloc(s,2); -~predictPoint = Buffer.alloc(s,2); -~avgBuf = Buffer.alloc(s,100,2); -~pitchingBus = Bus.control; -~catchingBus = Bus.control; -) ( -~normalizer.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~tempPoint).outBuffer_(~predictPoint); -//Pitching (no pun intended): read frames out of buffer and pass to standardize { var audio = BufRd.ar(1,~audio,LFSaw.ar(BufDur.ir(~audio).reciprocal).range(0, BufFrames.ir(~audio))); var counter = Stepper.ar(Impulse.ar(ControlRate.ir),max:99); - var trig = HPZ1.ar(counter) < 0; + var trig = A2K.kr(HPZ1.ar(counter) < 0); //average 100 frames: one could use the MovingAverage extension here var avg; - BufWr.kr(FluidPitch.kr(audio),~avgBuf,phase:counter); - avg = Mix.new(BufRd.kr(2, ~avgBuf, phase:100.collect{|x|x})) * 0.01; + var inputPoint = LocalBuf(2); + var outputPoint = LocalBuf(2); + var avgBuf = LocalBuf(100,2); + //running average of pitch features + BufWr.kr(FluidPitch.kr(audio),avgBuf,phase:counter); + avg = Mix.new(BufRd.kr(2, avgBuf, phase:100.collect{|x|x})) * 0.01; //assemble data point - BufWr.kr(avg[0],~tempPoint,0); - BufWr.kr(avg[1],~tempPoint,1); - Poll.kr(T2K.kr(trig),BufRd.kr(1,~tempPoint,[0,1]),["pitch (raw)", "confidence (raw)"]); - Out.kr(~pitchingBus.index,[T2K.kr(trig)]); -}.play(~normalizer.synth,addAction:\addBefore); - -{ - Poll.kr(In.kr(~catchingBus.index),BufRd.kr(1,~predictPoint,[0,1]),["pitch (normalized)", "confidence (normalized)"]) -}.play(~normalizer.synth,addAction:\addAfter); - + BufWr.kr(avg[0],inputPoint,0); + BufWr.kr(avg[1],inputPoint,1); + ~normalizer.kr(trig,inputPoint,outputPoint); + Poll.kr(trig,BufRd.kr(1,inputPoint,[0,1]),["pitch (raw)", "confidence (raw)"]); + Poll.kr(trig,BufRd.kr(1,outputPoint,[0,1]),["pitch (normalized)", "confidence (normalized)"]) +}.play; ) - - - - :: diff --git a/release-packaging/HelpSource/Classes/FluidPCA.schelp b/release-packaging/HelpSource/Classes/FluidPCA.schelp index 3036bc7..3b897fb 100644 --- a/release-packaging/HelpSource/Classes/FluidPCA.schelp +++ b/release-packaging/HelpSource/Classes/FluidPCA.schelp @@ -63,9 +63,9 @@ s.boot; //Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers, a FluidStandardize and a FluidPCA ( ~audiofile = File.realpath(FluidBufPitch.class.filenameSymbol).dirname +/+ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav"; -~raw = FluidDataSet(s,\pca_help_12D); -~standardized = FluidDataSet(s,\pca_help_12Ds); -~reduced = FluidDataSet(s,\pca_help_2D); +~raw = FluidDataSet(s); +~standardized = FluidDataSet(s); +~reduced = FluidDataSet(s); ~audio = Buffer.read(s,~audiofile); ~mfcc_feature = Buffer.new(s); ~stats = Buffer.alloc(s, 7, 12); @@ -78,7 +78,7 @@ s.boot; // Load audio and run an mfcc analysis, which gives us 13 points (we'll throw the 0th away) ( ~audio = Buffer.read(s,~audiofile); -FluidBufMFCC.process(s,~audio, features: ~mfcc_feature); +FluidBufMFCC.process(s,~audio, features: ~mfcc_feature,action:{"Done MFCCs".postln}); ) // Divide the time series in 100, and take the mean of each segment and add this as a point to @@ -90,15 +90,16 @@ FluidBufMFCC.process(s,~audio, features: ~mfcc_feature); var count = PulseCount.kr(trig) - 1; var chunkLen = (~mfcc_feature.numFrames / 100).asInteger; var stats = FluidBufStats.kr( - source: ~mfcc_feature, startFrame: count * chunkLen, - startChan:1, numFrames: chunkLen, stats: ~stats, trig: trig + source: ~mfcc_feature, startFrame: count * chunkLen, + startChan:1, numFrames: chunkLen, stats: ~stats, + trig: trig * (count < 100), blocking: 1 ); var rd = BufRd.kr(12, ~stats, DC.kr(0), 0, 1); var bufWr, dsWr; 12.do{|i| bufWr = BufWr.kr(rd[i], buf, DC.kr(i)); }; - dsWr = FluidDataSetWr.kr(\pca_help_12D, buf: buf, trig: Done.kr(stats)); + dsWr = FluidDataSetWr.kr(~raw, buf: buf, trig: Done.kr(stats)); LocalOut.kr( Done.kr(dsWr)); FreeSelf.kr(count - 99); Poll.kr(trig,count); @@ -150,29 +151,19 @@ subsection:: Server Side Queries Let's map our learned PCA dimensions to the controls of a processor code:: - -( -~inputPoint = Buffer.alloc(s,12); -~predictPoint = Buffer.alloc(s,2); -~pitchingBus = Bus.control; -~catchingBus = Bus.control; -) - ( -~pca.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~inputPoint).outBuffer_(~predictPoint); - { var mapped; var audio = BufRd.ar(1,~audio,LFSaw.ar(BufDur.ir(~audio).reciprocal).range(0, BufFrames.ir(~audio))); var mfcc = FluidMFCC.kr(audio)[1..12]; var smoothed = LagUD.kr(mfcc,1*ControlDur.ir,500*ControlDur.ir); var trig = Impulse.kr(ControlRate.ir / 2); - smoothed.collect{|coeff,i| BufWr.kr([coeff],~inputPoint,i)}; - Out.kr(~pitchingBus,[trig]); - mapped = Latch.kr(BufRd.kr(1,~predictPoint, phase:[0,1]).linlin(-3,3,0,3),In.kr(~catchingBus)).scope; + var inputPoint = LocalBuf(12); + var outputPoint = LocalBuf(2); + smoothed.collect{|coeff,i| BufWr.kr([coeff],inputPoint,i)}; + ~pca.kr(trig, inputPoint, outputPoint, 2); + mapped = BufRd.kr(1,outputPoint, phase:[0,1]).linlin(-3,3,0,3); CombC.ar(audio,3,mapped[0],mapped[1]*3) -}.play(~pca.synth,addAction:\addBefore); - +}.play; ) - :: diff --git a/release-packaging/HelpSource/Classes/FluidStandardize.schelp b/release-packaging/HelpSource/Classes/FluidStandardize.schelp index 4047cb4..02493c3 100644 --- a/release-packaging/HelpSource/Classes/FluidStandardize.schelp +++ b/release-packaging/HelpSource/Classes/FluidStandardize.schelp @@ -59,8 +59,8 @@ s.boot; //Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers and a FluidStandardize ( ~audiofile = File.realpath(FluidBufPitch.class.filenameSymbol).dirname +/+ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav"; -~raw = FluidDataSet(s,\stand_help_raw); -~stand = FluidDataSet(s,\stand_help_standd); +~raw = FluidDataSet(s); +~stand = FluidDataSet(s); ~audio = Buffer.read(s,~audiofile); ~pitch_feature = Buffer.new(s); ~stats = Buffer.alloc(s, 7, 2); @@ -71,10 +71,10 @@ s.boot; // Load audio and run a pitch analysis, which gives us pitch and pitch confidence (so a 2D datum) ( ~audio = Buffer.read(s,~audiofile); -FluidBufPitch.process(s,~audio, features: ~pitch_feature); +FluidBufPitch.process(s,~audio, features: ~pitch_feature,action:{"Analysed Pitch".postln}); ) -// Divide the time series in to 10, and take the mean of each segment and add this as a point to +// Divide the time series in to 10, and take the mean of each s"egment and add this as a point to // the 'raw' FluidDataSet ( { @@ -83,19 +83,20 @@ FluidBufPitch.process(s,~audio, features: ~pitch_feature); var count = PulseCount.kr(trig) - 1; var chunkLen = (~pitch_feature.numFrames / 10).asInteger; var stats = FluidBufStats.kr( - source: ~pitch_feature, startFrame: count * chunkLen, - numFrames: chunkLen, stats: ~stats, trig: trig + source: ~pitch_feature, startFrame: count * chunkLen, + numFrames: chunkLen, stats: ~stats, + trig: trig * (count < 10), blocking: 1 ); var rd = BufRd.kr(2, ~stats, DC.kr(0), 0, 1);// pick only mean pitch and confidence var wr1 = BufWr.kr(rd[0], buf, DC.kr(0)); var wr2 = BufWr.kr(rd[1], buf, DC.kr(1)); - var dsWr = FluidDataSetWr.kr(\stand_help_raw, buf: buf, trig: Done.kr(stats)); + var dsWr = FluidDataSetWr.kr(~raw, buf: buf, trig: Done.kr(stats)); LocalOut.kr( Done.kr(dsWr)); FreeSelf.kr(count - 9); + Poll.kr(trig,count, \count); }.play; ) - // Standardize and load to language-side array ( ~rawarray = Array.new(10); @@ -123,41 +124,27 @@ FluidBufPitch.process(s,~audio, features: ~pitch_feature); :: subsection::Server Side Querying - -Because FluidStandardize runs in its own link::Classes/Synth:: on the server, communication is done via control-rate link::Classes/Bus:: objects for triggering and link::Classes/Buffer:: objects for passing and retreiving data. - code:: -//Setup -( -~tempPoint = Buffer.alloc(s,2); -~predictPoint = Buffer.alloc(s,2); -~avgBuf = Buffer.alloc(s,100,2); -~pitchingBus = Bus.control; -~catchingBus = Bus.control; -) - ( -~standardizer.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~tempPoint).outBuffer_(~predictPoint); -//Pitching (no pun intended): read frames out of buffer and pass to standardize +// read frames out of buffer and pass to standardize { var audio = BufRd.ar(1,~audio,LFSaw.ar(BufDur.ir(~audio).reciprocal).range(0, BufFrames.ir(~audio))); var counter = Stepper.ar(Impulse.ar(ControlRate.ir),max:99); - var trig = HPZ1.ar(counter) < 0; + var trig = A2K.kr(HPZ1.ar(counter) < 0); //average 10 frames: one could use the MovingAverage extension here var avg; - BufWr.kr(FluidPitch.kr(audio),~avgBuf,phase:counter); - avg = Mix.new(BufRd.kr(2, ~avgBuf, phase:100.collect{|x|x})) * 0.01; + var inputPoint= LocalBuf(2); + var outputPoint = LocalBuf(2); + var avgBuf = LocalBuf(100,2); + //average of pitch features + BufWr.kr(FluidPitch.kr(audio),avgBuf,phase:counter); + avg = Mix.new(BufRd.kr(2, avgBuf, phase:100.collect{|x|x})) * 0.01; //assemble data point - BufWr.kr(avg[0],~tempPoint,0); - BufWr.kr(avg[1],~tempPoint,1); - Poll.kr(T2K.kr(trig),BufRd.kr(1,~tempPoint,[0,1]),["pitch (raw)", "confidence (raw)"]); - Out.kr(~pitchingBus.index,[T2K.kr(trig)]); -}.play(~standardizer.synth,addAction:\addBefore); - -//catching -{ - Poll.kr(In.kr(~catchingBus.index),BufRd.kr(1,~predictPoint,[0,1]),["pitch (standardized)", "confidence (standardized)"]) -}.play(~standardizer.synth,addAction:\addAfter); - + BufWr.kr(avg[0],inputPoint,0); + BufWr.kr(avg[1],inputPoint,1); + Poll.kr(trig,BufRd.kr(1,inputPoint,[0,1]),["pitch (raw)", "confidence (raw)"]); + ~standardizer.kr(trig,inputPoint,outputPoint); + Poll.kr(trig,BufRd.kr(1,outputPoint,[0,1]),["pitch (standardized)", "confidence (standardized)"]); +}.play; ) :: diff --git a/release-packaging/HelpSource/Classes/FluidUMAP.schelp b/release-packaging/HelpSource/Classes/FluidUMAP.schelp index cef03d3..381dea1 100644 --- a/release-packaging/HelpSource/Classes/FluidUMAP.schelp +++ b/release-packaging/HelpSource/Classes/FluidUMAP.schelp @@ -47,13 +47,13 @@ code:: //Preliminaries: we want some points, a couple of FluidDataSets, a FluidStandardize and a FluidUMAP ( -~raw = FluidDataSet(s,\umap_help_3D); -~standardized = FluidDataSet(s,\umap_help_3Ds); -~reduced = FluidDataSet(s,\umap_help_2D); -~normalized = FluidDataSet(s,\umap_help_2Dn); +~raw = FluidDataSet(s); +~standardized = FluidDataSet(s); +~reduced = FluidDataSet(s); +~normalized = FluidDataSet(s); ~standardizer = FluidStandardize(s); ~normalizer = FluidNormalize(s); -~umap = FluidUMAP(s, numDimensions: 2, numNeighbours: 5, minDist: 0.2,iterations: 50, learnRate: 0.2,batchSize: 50); +~umap = FluidUMAP(s).numDimensions_(2).numNeighbours_(5).minDist_(0.2).iterations_(50). learnRate_(0.2).batchSize_(50); ) @@ -65,11 +65,11 @@ code:: ~raw.print; //First standardize our DataSet, then apply the UMAP to get 2 dimensions, then normalise these 2 for drawing in the full window size - -~standardizer.fitTransform(~raw,~standardized) -~umap.fitTransform(~standardized,~reduced) -~normalizer.fitTransform(~reduced,~normalized) - +( +~standardizer.fitTransform(~raw,~standardized,action:{"Standardized".postln}); +~umap.fitTransform(~standardized,~reduced,action:{"Finished UMAP".postln}); +~normalizer.fitTransform(~reduced,~normalized,action:{"Normalized Output".postln}); +) //we recover the reduced dataset ~normalized.dump{|x| ~normalizedDict = x["data"]};