diff --git a/release-packaging/Classes/FluidAmpSlice.sc b/release-packaging/Classes/FluidAmpSlice.sc index 8558d56..27640a9 100644 --- a/release-packaging/Classes/FluidAmpSlice.sc +++ b/release-packaging/Classes/FluidAmpSlice.sc @@ -1,6 +1,6 @@ FluidAmpSlice : UGen { - *ar { arg in = 0, absRampUp = 10, absRampDown = 10, absThreshOn = -90, absThreshOff = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = 144, relThreshOff = -144, highPassFreq = 85, maxSize = 88200, outputType = 0; - ^this.multiNew('audio', in.asAudioRateInput(this), absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq, maxSize, outputType) + *ar { arg in = 0, absRampUp = 10, absRampDown = 10, absThreshOn = -90, absThreshOff = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = 144, relThreshOff = -144, highPassFreq = 85, maxSize = 88200; + ^this.multiNew('audio', in.asAudioRateInput(this), absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq, maxSize, 0) } checkInputs { if(inputs.at(16).rate != 'scalar') { diff --git a/release-packaging/Classes/FluidBufAmpSlice.sc b/release-packaging/Classes/FluidBufAmpSlice.sc index 0c41b94..88edabb 100644 --- a/release-packaging/Classes/FluidBufAmpSlice.sc +++ b/release-packaging/Classes/FluidBufAmpSlice.sc @@ -1,5 +1,5 @@ FluidBufAmpSlice{ - *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, absRampUp = 10, absRampDown = 10, absThreshOn = -90, absThreshOff = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = 144, relThreshOff = -144, highPassFreq = 85, outputType = 0, action; + *process { arg server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, absRampUp = 10, absRampDown = 10, absThreshOn = -90, absThreshOff = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = 144, relThreshOff = -144, highPassFreq = 85, action; var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead)); @@ -12,7 +12,7 @@ FluidBufAmpSlice{ server = server ? Server.default; forkIfNeeded{ - server.sendMsg(\cmd, \BufAmpSlice, source, startFrame, numFrames, startChan, numChans, indices, absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq, maxSize, outputType); + server.sendMsg(\cmd, \BufAmpSlice, source, startFrame, numFrames, startChan, numChans, indices, absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq, maxSize, 0); server.sync; indices = server.cachedBufferAt(indices); indices.updateInfo; server.sync; action.value(indices); diff --git a/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp b/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp index 2fee617..d1789b7 100644 --- a/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp @@ -66,135 +66,118 @@ ARGUMENT:: highPassFreq ARGUMENT:: maxSize How large can the buffer be for time-critical conditions, by allocating memory at instantiation time. This cannot be modulated. -ARGUMENT:: outputType -(describe argument here) - RETURNS:: An audio stream with square envelopes around the slices. The latency between the input and the output is STRONG::max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead))::. EXAMPLES:: code:: -//basic tests: highPass sanity -( -{var env, source = SinOsc.ar(320,0,0.5); - env = FluidAmpSlice.ar(source,highPassFreq:250, outputType:1); - [source, env] -}.plot(0.03); -) -//basic tests: absRampUp-Down sanity -( - {var env, source = SinOsc.ar(320,0,0.5); - env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:1000, outputType:2); - [source.abs, env] - }.plot(0.03); -) -///////////////////////////// //basic tests: absThresh sanity ( - {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); +{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12); - [source, env] - }.plot(0.1); + [source, env] +}.plot(0.1); ) //basic tests: absThresh hysteresis ( - {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); +{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -16); - [source, env] - }.plot(0.1); + [source, env] +}.plot(0.1); ) //basic tests: absThresh min slice ( - {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); +{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minSliceLength:441); - [source, env] - }.plot(0.1); + [source, env] +}.plot(0.1); ) //basic tests: absThresh min silence ( - {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); +{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minSilenceLength:441); - [source, env] - }.plot(0.1); + [source, env] +}.plot(0.1); ) //mid tests: absThresh time hysteresis on ( - {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); - env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minLengthAbove:441, outputType:0); - [DelayN.ar(source,0.1,441/44100), env] - }.plot(0.1); +{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); + env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minLengthAbove:441); + [DelayN.ar(source,0.1,441/44100), env] +}.plot(0.1); ) //mid tests: absThresh time hysteresis off ( - {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); +{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12, minLengthBelow:441); - [DelayN.ar(source,0.1,441/44100), env] - }.plot(0.1); + [DelayN.ar(source,0.1,441/44100), env] +}.plot(0.1); ) //mid tests: absThresh with lookBack ( - {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); +{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12,lookBack:441); - [DelayN.ar(source,0.1,441/44100), env] - }.plot(0.1); + [DelayN.ar(source,0.1,441/44100), env] +}.plot(0.1); ) //mid tests: absThresh with lookAhead ( - {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); +{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12,lookAhead:441); - [DelayN.ar(source,0.1,441/44100), env] - }.plot(0.1); + [DelayN.ar(source,0.1,441/44100), env] +}.plot(0.1); ) //mid tests: absThresh with asymetrical lookBack and lookAhead ( - {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); +{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:100, absThreshOn:-12, absThreshOff: -12,lookBack:221, lookAhead:441); - [DelayN.ar(source,0.1,441/44100), env] - }.plot(0.1); + [DelayN.ar(source,0.1,441/44100), env] +}.plot(0.1); ) //advanced tests: absThresh hysteresis, long tail ( - {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); +{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:2000, absThreshOn:-12, absThreshOff: -16); - [source, env] - }.plot(0.1); + [source, env] +}.plot(0.1); ) //solution: have to recut with relThresh ( - {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); +{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:2000, absThreshOn:-12, absThreshOff: -16, relRampUp:5, relRampDown:200, relThreshOn:-1, relThreshOff:-12); - [source, env] - }.plot(0.1); + [source, env] +}.plot(0.1); ) //beware of double trig ( - {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); +{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:2000, absThreshOn:-12, absThreshOff: -16, relRampUp:5, relRampDown:200, relThreshOn:-1, relThreshOff:-1); - [source, env] - }.plot(0.05); + [source, env] +}.plot(0.05); ) //a solution: minSliceLength ( - {var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); +{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs); env = FluidAmpSlice.ar(source,absRampUp:10, absRampDown:2000, absThreshOn:-12, absThreshOff: -16, relRampUp:5, relRampDown:200, relThreshOn:-1, relThreshOff:-1, minSliceLength:441); - [source, env] - }.plot(0.05); + [source, env] +}.plot(0.05); ) //drum slicing, many ways //load a buffer b = Buffer.read(s,File.realpath(FluidAmpSlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); +//have fun with a gate (explore lookahead and lookback, but correct for latency) ( - {var env, source = PlayBuf.ar(1,b); - env = FluidAmpSlice.ar(source,absRampUp:2205, absRampDown:2205, absThreshOn:-70, absThreshOff: -80, relRampUp:10, relRampDown:441, relThreshOn:14, relThreshOff:12, minSliceLength:4410, outputType:0); - [source, env] - }.plot(2,maxval:[1,1],separately:true); +{var env, source = PlayBuf.ar(1,b); + env = FluidAmpSlice.ar(source,absRampUp:44, absRampDown:2205, absThreshOn:-20, absThreshOff: -23, minSilenceLength:1100, lookBack:441); + [DelayN.ar(source,delaytime:441/44100), env] +}.plot(2,separately:true); ) ( - {var env, source = PlayBuf.ar(1,b); - env = FluidAmpSlice.ar(source,highPassFreq:120, absRampUp:2205, absRampDown:2205, absThreshOn:-70, absThreshOff: -80, relRampUp:10, relRampDown:2205, relThreshOn:12, relThreshOff:8, minSliceLength:441); - [source, env] - }.play); +{var env, source = PlayBuf.ar(1,b); + env = FluidAmpSlice.ar(source,highPassFreq:120, absRampUp:4410, absRampDown:4410, absThreshOn:-60, absThreshOff: -60, relRampUp:10, relRampDown:2205, relThreshOn:13, relThreshOff:10, minSilenceLength:4410, highPassFreq:20); + [source, Trig.ar(env,0)] +}.play; ) :: diff --git a/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp index 7c7375b..35031d6 100644 --- a/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp @@ -81,9 +81,6 @@ ARGUMENT:: relThreshOff 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. -ARGUMENT:: outputType -(describe argument here) - 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. @@ -95,8 +92,8 @@ 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 @@ -160,24 +157,36 @@ c.getn(0,c.numFrames*2,{|item|item.postln;}) FluidBufAmpSlice.process(s,b,indices:c, absRampUp:2205, absRampDown:2205, absThreshOn:-60, absThreshOff: -60, relRampUp:5, relRampDown:220, relThreshOn:2, relThreshOff:1, minSliceLength:4410) c.query c.getn(0,c.numFrames*2,{|item|item.postln;}) +:: -///////////////////////// -//drum slicing, many ways +STRONG::A musical example.:: +CODE:: //load a buffer ( b = Buffer.read(s,File.realpath(FluidBufAmpSlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); c = Buffer.new(s); ) +// slice the samples FluidBufAmpSlice.process(s,b,indices:c, absRampUp:2205, absRampDown:2205, absThreshOn:-70, absThreshOff: -80, relRampUp:10, relRampDown:441, relThreshOn:14, relThreshOff:12, minSliceLength:4410) c.query c.getn(0,c.numFrames*2,{|item|item.postln;}) -//AGAIN STRANGE OFFSET ADDRESSES - +//loops over a splice with the MouseX +( +{ + BufRd.ar(1, b, + Phasor.ar(0,1, + BufRd.kr(1, c, + MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1), + BufRd.kr(1, c, + MouseX.kr(1, BufFrames.kr(c)), 0, 1), + BufRd.kr(1,c, + MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1)), 0, 1); + }.play; +) :: - /// //TO TROUBLESHOOT STRONG::A stereo buffer example.:: CODE:: // make a stereo buffer @@ -194,9 +203,9 @@ c = Buffer.new(s); ( // with basic params Routine{ - t = Main.elapsedTime; - FluidBufAmpSlice.process(s,b, indices: c, absRampUp:1, absRampDown:1, absThreshOn:-60, absThreshOff:-60); - (Main.elapsedTime - t).postln; + t = Main.elapsedTime; + FluidBufAmpSlice.process(s,b, indices: c, absRampUp:1, absRampDown:1, absThreshOn:-60, absThreshOff:-60); + (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp index 092d5c6..6f185f6 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp @@ -235,49 +235,77 @@ CODE:: //set some buffers ( b = Buffer.read(s,File.realpath(FluidBufNMF.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-AcousticStrums-M.wav"); -c = Buffer.new(s); -x = Buffer.new(s); -e = Buffer.new(s); -y = Buffer.new(s); + +~originalNMF = Buffer.new(s); +~bases = Buffer.new(s); +~trainedBases = Buffer.new(s); +~activations = Buffer.new(s); +~final = Buffer.new(s); +~spectralshapes = Buffer.new(s); +~stats = Buffer.new(s); +~sortedNMF = Buffer.new(s); ) -// train only 2 seconds +b.play + +// train using the first 2 seconds of the sound file ( Routine { - FluidBufNMF.process(s,b,0,88200,0,1, c, x, components:10); - c.query; + FluidBufNMF.process(s,b,0,44100*5,0,1, ~originalNMF, ~bases, components:10); + ~originalNMF.query; }.play; ) -// find the component that has the picking sound by changing which channel to listen to +// listen to the 10 components across the stereo image +{Splay.ar(PlayBuf.ar(10, ~originalNMF))}.play + +// plot the bases +~bases.plot + +// find the component that has the picking sound checking the median spectral centroid ( - ~element = 4; - {PlayBuf.ar(10,c)[~element]}.play +FluidBufSpectralShape.process(s, ~originalNMF, 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); + }) + }) + }) +}); ) -// copy all the other components on itself and the picking basis as the sole component of the 1st channel +//what is happening there? We run the spectralshapes on the buffer of 10 components from the nmf. See the structure of that buffer: +~originalNMF.query +//10 channel are therefore giving 70 channels: the 7 shapes of component0, then 7 shapes of compoenent1, etc +~spectralshapes.query +// we then run the bufstats on them. Each channel, which had a time series (an envelop) of each descriptor, is reduced to 7 frames +~stats.query +// we then need to retrieve the values that are where we want: the first of every 7 for the centroid, and the 6th frame of them as we want the median. Because we retrieve the values in an interleave format, the select function gets a bit tricky but we get the following values: +~centroids.postln + +// we then copy the basis with the highest median centroid to a channel, and all the other bases to the other channel, of a 2-channel bases for decomposition ( -Routine{ - z = (0..9); - FluidBufCompose.process(s, x, startChan: z.removeAt(~element), numChans: 1, destination: e); - z.do({|chan| FluidBufCompose.process(s, x, startChan:chan, numChans: 1, destStartChan: 1, destination: e, destGain:1)}); - e.query; -}.play; +z = (0..9); +[z.removeAt(~centroids.maxIndex)].do{|chan|FluidBufCompose.process(s, ~bases, startChan: chan, numChans: 1, destination: ~trainedBases, destGain:1)}; +z.postln; +z.do({|chan| FluidBufCompose.process(s, ~bases, startChan:chan, numChans: 1, destStartChan: 1, destination: ~trainedBases, destGain:1)}); ) +~trainedBases.plot //process the whole file, splitting it with the 2 trained bases ( Routine{ - FluidBufNMF.process(s, b, destination: c, bases: e, basesMode: 2, activations: y, components:2); - c.query; + FluidBufNMF.process(s, b, destination: ~sortedNMF, bases: ~trainedBases, basesMode: 2, components:2); + ~originalNMF.query; }.play; ) -// play the result: pick on the left, rest on the right. -c.play +// play the result: pick on the left, sustain on the right! +{PlayBuf.ar(2,~sortedNMF)}.play // it even null-sums -{(PlayBuf.ar(2,c,doneAction:2).sum)-(PlayBuf.ar(1,b,doneAction:2))}.play +{(PlayBuf.ar(2,~sortedNMF,doneAction:2).sum)-(PlayBuf.ar(1,b,doneAction:2))}.play :: STRONG::Updating Bases: The process can update bases provided as seed.:: diff --git a/release-packaging/HelpSource/Classes/FluidMFCC.schelp b/release-packaging/HelpSource/Classes/FluidMFCC.schelp index 0f877ee..8261515 100644 --- a/release-packaging/HelpSource/Classes/FluidMFCC.schelp +++ b/release-packaging/HelpSource/Classes/FluidMFCC.schelp @@ -61,7 +61,6 @@ a.reference_(Array.fill(13,{0.5})); //make a center line to show 0 //run the window updating routine. ( - ~winRange = 500; r = Routine { diff --git a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp index 36e181e..664ff8d 100644 --- a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp +++ b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp @@ -104,36 +104,44 @@ CODE:: ( b = Buffer.read(s,File.realpath(FluidNMFMatch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-AcousticStrums-M.wav"); c = Buffer.new(s); -x = Buffer.new(s); -e = Buffer.new(s); +~bases = Buffer.new(s); +~spectralshapes = Buffer.new(s); +~stats = Buffer.new(s); +~centroids = Buffer.new(s); +~trainedBases = Buffer.new(s); ) // train only 2 seconds ( Routine { - FluidBufNMF.process(s,b,0,88200,0,1, c, x, components:10,fftSize:2048); + FluidBufNMF.process(s,b,0,88200,0,1, c, ~bases, components:10,fftSize:2048); c.query; }.play; ) // wait for the query to print -// then find the component that has more sustain pitch than pick (TODO: use descriptors with stats) +// find the component that has the picking sound checking the median spectral centroid ( - ~element = 4; - {PlayBuf.ar(10,c)[~element]}.play; +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); + }) + }) + }) +}); ) -// copy all the other components on itself and the picking basis as the sole component of the 1st channel +// we then copy the basis with the lowest median centroid to a channel, and all the other bases to the other channel, of a 2-channel bases for decomposition ( -Routine{ - z = (0..9); - FluidBufCompose.process(s, x, startChan: z.removeAt(~element), numChans: 1, destination: e); - z.do({|chan| FluidBufCompose.process(s, x, startChan:chan, numChans: 1, destStartChan: 1, destination: e, destGain:1)}); - e.query; -}.play; +z = (0..9); +[z.removeAt(~centroids.minIndex)].do{|chan|FluidBufCompose.process(s, ~bases, startChan: chan, numChans: 1, destination: ~trainedBases, destGain:1)}; +z.postln; +z.do({|chan| FluidBufCompose.process(s, ~bases, startChan:chan, numChans: 1, destStartChan: 1, destination: ~trainedBases, destGain:1)}); ) -e.plot; +~trainedBases.plot; //we can then use the resynthesised signal to sent in a delay ( @@ -149,7 +157,7 @@ e.plot; mod4 = SinOsc.ar(((613 * 191) / (463 * 601)), 0, 0.001); // compress the signal to send to the delays - todelay = FluidNMFFilter.ar(source,e,2,fftSize:2048)[0]; //reading the channel of the activations on the pick basis + todelay = FluidNMFFilter.ar(source,~trainedBases,2,fftSize:2048)[0]; //reading the channel of the activations on the pick basis // delay network feedback = LocalIn.ar(3);// take the feedback in for the delays diff --git a/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp b/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp index d2c05c3..a8946ef 100644 --- a/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp +++ b/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp @@ -104,39 +104,47 @@ CODE:: ( b = Buffer.read(s,File.realpath(FluidNMFMatch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-AcousticStrums-M.wav"); c = Buffer.new(s); -x = Buffer.new(s); -e = Buffer.new(s); +~bases = Buffer.new(s); +~spectralshapes = Buffer.new(s); +~stats = Buffer.new(s); +~centroids = Buffer.new(s); +~trainedBases = Buffer.new(s); ) // train only 2 seconds ( Routine { - FluidBufNMF.process(s,b,0,88200,0,1, c, x, components:10,fftSize:2048); + FluidBufNMF.process(s,b,0,88200,0,1, c, ~bases, components:10,fftSize:2048); c.query; }.play; ) // wait for the query to print -// then find the component that has the picking sound by changing which channel to listen to +// find the component that has the picking sound checking the median spectral centroid ( - ~element = 6; - {PlayBuf.ar(10,c)[~element]}.play; +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); + }) + }) + }) +}); ) -// copy all the other components on itself and the picking basis as the sole component of the 1st channel +// we then copy the basis with the highest median centroid to a channel, and all the other bases to the other channel, of a 2-channel bases for decomposition ( -Routine{ - z = (0..9); - FluidBufCompose.process(s, x, startChan: z.removeAt(~element), numChans: 1, destination: e); - z.do({|chan| FluidBufCompose.process(s, x, startChan:chan, numChans: 1, destStartChan: 1, destination: e, destGain:1)}); - e.query; -}.play; +z = (0..9); +[z.removeAt(~centroids.maxIndex)].do{|chan|FluidBufCompose.process(s, ~bases, startChan: chan, numChans: 1, destination: ~trainedBases, destGain:1)}; +z.postln; +z.do({|chan| FluidBufCompose.process(s, ~bases, startChan:chan, numChans: 1, destStartChan: 1, destination: ~trainedBases, destGain:1)}); ) -e.plot; +~trainedBases.plot; //using this trained basis we can see the envelop (activations) of each component -{FluidNMFMatch.kr(PlayBuf.ar(1,b),e,2,fftSize:2048)}.plot(1); +{FluidNMFMatch.kr(PlayBuf.ar(1,b),~trainedBases,2,fftSize:2048)}.plot(1); // the left/top activations are before, the pick before the sustain. //we can then use the activation value to sidechain a compression patch that is sent in a delay @@ -154,7 +162,7 @@ e.plot; // compress the signal to send to the delays todelay = DelayN.ar(source,0.1, 800/44100, //delaying it to compensate for FluidNMFMatch's latency - LagUD.ar(K2A.ar(FluidNMFMatch.kr(source,e,2,fftSize:2048)[0]), //reading the channel of the activations on the pick basis + LagUD.ar(K2A.ar(FluidNMFMatch.kr(source,~trainedBases,2,fftSize:2048)[0]), //reading the channel of the activations on the pick basis 80/44100, // lag uptime (compressor's attack) 1000/44100, // lag downtime (compressor's decay) (1/(2.dbamp) // compressor's threshold inverted @@ -195,7 +203,7 @@ Routine { // wait for the query to print // then find a component for each item you want to find. You could also sum them. Try to find a component with a good object-to-rest ratio ( - ~dog =2; + ~dog =4; {PlayBuf.ar(10,c)[~dog]}.play )