diff --git a/release-packaging/Classes/FluidBufToKr.sc b/release-packaging/Classes/FluidBufToKr.sc index 1661519..84a0cb6 100644 --- a/release-packaging/Classes/FluidBufToKr.sc +++ b/release-packaging/Classes/FluidBufToKr.sc @@ -1,45 +1,75 @@ FluidKrToBuf { *kr { - arg krStream, buffer; + arg krStream, buffer, krStartChan = 0, krNumChans = -1, destStartFrame = 0; + var endChan; + + // fix -1 default + if(krNumChans == -1,{krNumChans = krStream.numChannels - krStartChan}); + + // what is the last channel that will be used + endChan = (krStartChan + krNumChans) - 1; if(buffer.isKindOf(Buffer).or(buffer.isKindOf(LocalBuf)),{ - if(buffer.numFrames == 0) {"FluidKrToBuf:kr Buffer has 0 frames".warn}; - if(buffer.numFrames > 1000) { - Error("FluidKrToBuf:kr Buffer is % frames. This is probably not the buffer you intended.".format(buffer.numFrames)).throw; + + // sanity check + if(buffer.numFrames == 0){"% Buffer has 0 frames".format(this.class).warn}; + + // oopsie check + if(buffer.numFrames > 1000){ + Error("% Buffer is % frames. This is probably not the buffer you intended.".format(this.class,buffer.numFrames)).throw; }; + + // out of bounds check + if((destStartFrame + krNumChans) > buffer.numFrames,{ + Error("% (destStartFrame + krNumChans) > buffer.numFrames".format(this.class)).throw; + }); + }); - ^krStream.numChannels.do{ - arg i; - BufWr.kr(krStream[i], buffer, i); + ^(krStartChan..endChan).do{ + arg kr_i, i; + BufWr.kr(krStream[kr_i], buffer, destStartFrame + i); } } } FluidBufToKr { *kr { - arg buffer, numFrames = -1; + arg buffer, startFrame = 0, numFrames = -1; - if((buffer.isKindOf(Buffer).or(buffer.isKindOf(LocalBuf))).not.and(numFrames.isNil),{ - Error("FluidBufToKr:kr needs to be passed either an existing buffer or an OutputProxy and a number of frames for the buffer that will be supplied").throw; - }); + // out of bounds check + if(startFrame < 0,{Error("% startFrame must be >= 0".format(this.class)).throw;}); + + if(buffer.isKindOf(Buffer) or: {buffer.isKindOf(LocalBuf)},{ + + // fix default -1 + if(numFrames == -1,{numFrames = buffer.numFrames - startFrame}); - if(numFrames == -1,{ - numFrames = buffer.numFrames; + // dummy check + if(numFrames < 1,{Error("% numFrames must be >= 1".format(this.class)).throw}); + + // out of bounds check + if((startFrame+numFrames) > buffer.numFrames,{Error("% (startFrame + numFrames) > buffer.numFrames".format(this.class)).throw;}); + + },{ + // make sure the numFrames give is a positive integer + if((numFrames < 1) || (numFrames.isInteger.not),{ + Error("% if no buffer is specified, numFrames must be a value >= 1.".format(this.class)).throw; + }); }); - if(numFrames == 0) {"FluidKrToBuf:kr indicated numFrames is zero.".warn}; + // oopsie check if(numFrames > 1000) { - Error("FluidKrToBuf: Buffer is indicated to have % frames. This is probably not the buffer you intended.".format(numFrames)).throw; + Error("%: numframes is % frames. This is probably not what you intended.".format(this.class, numFrames)).throw; }; if(numFrames > 1,{ ^numFrames.collect{ arg i; - BufRd.kr(1,buffer,i,0,0); + BufRd.kr(1,buffer,i+startFrame,0,0); } },{ - ^BufRd.kr(1,buffer,0,0,0); + ^BufRd.kr(1,buffer,startFrame,0,0); }); } } diff --git a/release-packaging/HelpSource/Classes/FluidBufToKr.schelp b/release-packaging/HelpSource/Classes/FluidBufToKr.schelp index ea85655..71f4c7a 100644 --- a/release-packaging/HelpSource/Classes/FluidBufToKr.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufToKr.schelp @@ -4,7 +4,7 @@ categories:: Libraries>FluidCorpusManipulation related:: Classes/FluidKrToBuf DESCRIPTION:: -Helper pseudo UGen for reading data out of a buffer to a Kr stream. It only reads one-channel buffers, converting them to a Kr stream with as many channels as the number of frames that the buffer is long. +Helper pseudo UGen for reading data out of a buffer to a Kr stream. It only reads one-channel buffers, converting them to a Kr stream. CLASSMETHODS:: @@ -12,10 +12,13 @@ METHOD:: kr Initialize an instance of this pseudo UGen ARGUMENT:: buffer -The link::Classes/Buffer:: that this pseudo UGen will read out of. Must be a one-channel buffer. +Either a link::Classes/Buffer:: object or an index opointing to a buffer that this pseudo UGen will read out of. Must be a one-channel buffer. + +ARGUMENT:: startFrame +Offset of reading position in the buffer. The default is 0. ARGUMENT:: numFrames -How many frames the buffer is that will evenutally passed. If providing a buffer directly (instead of as an argument to a SynthDef), the default of -1 will get the number of frames from the buffer passed. +Number of frames to read from the buffer. Needs to be set, if buffer is not a code::Buffer:: object but a buffer index. If code::-1::, read whole buffer starting at code::startFrame::. The default is -1. returns:: a Kr stream that has the same number of channels as frames in the link::Classes/Buffer::. @@ -24,37 +27,51 @@ INSTANCEMETHODS:: EXAMPLES:: code:: -// make a buffer with some data in it -~buf = Buffer.loadCollection(s,[0,1,2,3,4,7]); -// play it on the server and read out of this buffer! +// fill a 1-channel buffer with 7 numbers +~buf = Buffer.loadCollection(s,{exprand(100,4000)} ! 7); + +// in a synth, read those numbers out of the buffer and get them as a control stream ( -{ - var sig = FluidBufToKr.kr(~buf); +~synth = { + arg buf; + var freqs = FluidBufToKr.kr(buf,numFrames:7); + var sig = SinOsc.ar(freqs.lag(0.03)) * 0.1; sig.poll; -}.play; + Splay.ar(sig); +}.play(args:[\buf,~buf]); ) -// =============== passing a buffer as an argument ====================== +// then you can change what's in the buffer and it will get read out by the synth +~buf.setn(0,{exprand(100,4000)} ! 7); + +:: +Use with other FluCoMa objects: +code:: + +// create an neural network for classification +~mlp = FluidMLPClassifier(s); + +// load a model that has been pre-trained to classify between a tone and noise, simple, i know, but... +~mlp.read(FluidFilesPath("../Resources/bufToKrExample.json")); -// create a synth that both writes into a buffer (with FluidKrToBuf) and reads -// out of the same buffer (with FluidBufToKr) +// can be used to demonstrate that... ( -~synth = { - arg buf = 999; - FluidKrToBuf.kr(SinOsc.kr(Array.fill(5,{rrand(0.0,1.0)})),buf); +{ + var input_buf = LocalBuf(7); + var out_buf = LocalBuf(1); + var sig = Select.ar(ToggleFF.kr(Dust.kr(1)),[SinOsc.ar(440),PinkNoise.ar]); + var analysis = FluidSpectralShape.kr(sig); + FluidKrToBuf.kr(analysis,input_buf); - // you need to specify the 5 so the synth here will know how many channels to make - // the output proxy - FluidBufToKr.kr(buf,5).poll; + // the output prediction is written into a buffer + ~mlp.kr(Impulse.kr(30),input_buf,out_buf); + + // and FluidBufToKr can be used to read the prediction out into a control rate stream + FluidBufToKr.kr(out_buf).poll; + + sig.dup * -30.dbamp }.play; -// you should see all zeros! (unless your buffer #999 has something in it already!) ) -// ...then after it is running, instantiate the buffer -~buffer = Buffer.alloc(s,5); - -// ...then send it to the buffer -~synth.set(\buf,~buffer); -// you should be able to see the sine oscillators now! :: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidKrToBuf.schelp b/release-packaging/HelpSource/Classes/FluidKrToBuf.schelp index 87b7f5b..21ab55f 100644 --- a/release-packaging/HelpSource/Classes/FluidKrToBuf.schelp +++ b/release-packaging/HelpSource/Classes/FluidKrToBuf.schelp @@ -4,7 +4,7 @@ categories:: Libraries>FluidCorpusManipulation related:: Classes/FluidBufToKr DESCRIPTION:: -Helper pseudo UGen for writing data into a buffer from a Kr stream. It only works with one-channel buffers. The number of frames in the link::Classes/Buffer:: must be the same as the number of channels in the Kr stream. +Helper pseudo UGen for writing data into a buffer from a Kr stream. It only works with one-channel buffers. CLASSMETHODS:: @@ -15,34 +15,65 @@ ARGUMENT:: krStream The Kr stream to write into the buffer. ARGUMENT:: buffer -The link::Classes/Buffer:: to write the Kr stream into. +The buffer to write the Kr stream into. Can be either a link::Classes/Buffer:: object, or an index poiting to a buffer. -returns:: Nothing. +ARGUMENT:: krStartChan +The channel in the code::krStream:: to begin the reading from. The default is 0. +ARGUMENT:: krNumChans +The number of channels in the code::krStream:: to read from starting at code::krStartChan:: The default of -1 will read from code::krStartChan:: to the max number of channels in the code::krStream::. + +ARGUMENT:: destStartFrame +The frame in the code::buffer:: to begin writing into. The default is 0. + +returns:: This class. INSTANCEMETHODS:: EXAMPLES:: - code:: + ( -// FluidKrToBuf test -s.waitForBoot{ - Routine{ - var buf = Buffer.alloc(s,5); +~synth = { + var buf = LocalBuf(512).clear; + var sig = SinOsc.ar([440,441]); + var lfos = Array.fill(512,{arg i; SinOsc.ar(i.linlin(0,511,0.01,0.2))}); + FluidKrToBuf.kr(lfos,buf); + sig = Shaper.ar(buf,sig); + sig.dup * -40.dbamp; +}.scope; +) + +:: +Use with other FluCoMa objects: +code:: - s.sync; +// make a new dataset +~ds = FluidDataSet(s); - { - var sig = SinOsc.kr(rrand(1.0.dup(buf.numFrames),4.0)); - FluidKrToBuf.kr(sig,buf); - }.play; +// run a synth with varying sounds and an mfcc analysis +( +~synth = { + arg t_trig; + var buf = LocalBuf(13); + var n = 7; + var sig = BPF.ar(PinkNoise.ar.dup(n),LFDNoise1.kr(2.dup(n)).exprange(100,4000)).sum * -20.dbamp; + var mfccs = FluidMFCC.kr(sig,buf.numFrames,startCoeff:1,maxNumCoeffs:buf.numFrames); - 1.wait; + // write the real-time mfcc analysis into this buffer so that... + FluidKrToBuf.kr(mfccs,buf); - defer{buf.plot}; - }.play; -} + // it can be added to the dataset from that buffer by sending a trig to the synth + FluidDataSetWr.kr(~ds,"point-",PulseCount.kr(t_trig),buf:buf,trig:t_trig); + sig.dup; +}.play; ) + +// send a bunch of triggers and... +~synth.set(\t_trig,1); + +// see how your dataset grows +~ds.print; + :: diff --git a/release-packaging/Resources/bufToKrExample.json b/release-packaging/Resources/bufToKrExample.json new file mode 100644 index 0000000..8349ab7 --- /dev/null +++ b/release-packaging/Resources/bufToKrExample.json @@ -0,0 +1,107 @@ +{ + "labels": { + "labels": [ + "tone", + "noise" + ], + "rows": 2 + }, + "mlp": { + "layers": [ + { + "activation": 1, + "biases": [ + 9.475189836135217e-245, + -7.312978302687739e-296, + 0.0, + 0.0, + 0.0 + ], + "cols": 5, + "rows": 7, + "weights": [ + [ + -0.7070957130289968, + 0.2529529084252004, + -0.6315006118399392, + 0.2641365029014226, + -0.11879484878940468 + ], + [ + -0.5210842572299229, + 0.25356340881888756, + 0.04200241621968997, + 0.12583197506255497, + 0.2845264672854667 + ], + [ + 0.36148051326296066, + 0.614748588906755, + 0.24204178015383798, + 0.6087291285844215, + 0.5802812834026664 + ], + [ + -0.05847754423619632, + -0.16475294200213061, + -0.6962199018410693, + 0.48955391140174614, + 0.3708040237508281 + ], + [ + 0.04633987154978883, + 0.02745889671058346, + -0.16487516786286913, + 0.03808304248049002, + -0.33594219806125813 + ], + [ + -0.3974517302751846, + 0.4680556811476128, + -0.6125775823484346, + -0.5770487854150471, + -0.6399818221697359 + ], + [ + -0.6405756468993841, + -0.65821443359875, + -0.1166924540077695, + 0.21767428398943345, + 0.3338702030112145 + ] + ] + }, + { + "activation": 1, + "biases": [ + -0.3628999139757816, + -0.42250374321781475 + ], + "cols": 2, + "rows": 5, + "weights": [ + [ + -0.31804841180355814, + -0.46839296834399485 + ], + [ + -4.2805091183479895, + 4.474227050298208 + ], + [ + 0.4747799633725462, + 0.4122869489228666 + ], + [ + 0.5463246485585964, + 0.04662010562772569 + ], + [ + 3.913863778074305, + -3.7226634012685995 + ] + ] + } + ] + } +} diff --git a/test/FluidBufToKr test.scd b/test/FluidBufToKr test.scd index b3c070c..3d2954c 100644 --- a/test/FluidBufToKr test.scd +++ b/test/FluidBufToKr test.scd @@ -93,7 +93,7 @@ s.waitForBoot{ ) ( -// FluidBufToKr +// FluidBufToKr should throw error s.waitForBoot{ Routine{ @@ -154,7 +154,7 @@ s.waitForBoot{ Routine{ ~synth = { arg buf; - FluidBufToKr.kr(buf,5).poll; + FluidBufToKr.kr(buf,numFrames:5).poll; }.play; 2.wait; @@ -197,7 +197,7 @@ s.waitForBoot{ arg buf = 999; FluidKrToBuf.kr(SinOsc.kr(Array.fill(5,{rrand(0.0,1.0)})),buf); - FluidBufToKr.kr(buf,5).poll; ////////// this will work becaues it knows how many frames the buffer will be + FluidBufToKr.kr(buf,numFrames:5).poll; ////////// this will work becaues it knows how many frames the buffer will be }.play; 2.wait; @@ -208,3 +208,155 @@ s.waitForBoot{ }.play; }; ) + +// test start frame: + + +( +// should skip the 0 +s.waitForBoot{ + Routine{ + var buf = Buffer.loadCollection(s,[0,1,2,3,4,7]); + + s.sync; + + { + var sig = FluidBufToKr.kr(buf,1); + sig.poll; + }.play; + }.play; +} +) + +( +// should be 2,3,4 +s.waitForBoot{ + Routine{ + var buf = Buffer.loadCollection(s,[0,1,2,3,4,7]); + + s.sync; + + { + var sig = FluidBufToKr.kr(buf,2,3); + sig.poll; + }.play; + }.play; +} +) + +( +// last four slots should be 0 +s.waitForBoot{ + Routine{ + var buf = Buffer.loadCollection(s,0.dup(10)); + + s.sync; + + { + FluidKrToBuf.kr(LFDNoise3.kr(1.dup(6)),buf); + }.play; + + 1.wait; + + // defer{buf.plot}; + buf.loadToFloatArray(action:{ + arg vals; + vals.postln; + }); + }.play; +} +) + +( +// middle slots should be not zero +s.waitForBoot{ + Routine{ + var buf = Buffer.loadCollection(s,0.dup(4)); + + s.sync; + + { + FluidKrToBuf.kr(LFDNoise3.kr(1.dup(2)),buf,destStartFrame:1); + }.play; + + 1.wait; + + // defer{buf.plot}; + buf.loadToFloatArray(action:{ + arg vals; + vals.postln; + }); + }.play; +} +) + +( +// should throw error +s.waitForBoot{ + Routine{ + var buf = Buffer.loadCollection(s,0.dup(4)); + + s.sync; + + { + FluidKrToBuf.kr(LFDNoise3.kr(1.dup(2)),buf,destStartFrame:3); + }.play; + + 1.wait; + + // defer{buf.plot}; + buf.loadToFloatArray(action:{ + arg vals; + vals.postln; + }); + }.play; +} +) + +( +// should be 0,0,200,3000,0,0 +s.waitForBoot{ + Routine{ + var buf = Buffer.alloc(s,7); + + s.sync; + + { + var sig = 3.collect{arg i; DC.kr((i+1)*100)}; + // sig.poll; + FluidKrToBuf.kr(sig,buf,1,2,2); + }.play; + + 1.wait; + + buf.loadToFloatArray(action:{ + arg vals; + vals.postln; + }); + }.play; +} +) + +( +// should be 100,200,300,400 +s.waitForBoot{ + Routine{ + var buf = Buffer.alloc(s,4); + + s.sync; + + { + var sig = 4.collect{arg i; DC.kr((i+1)*100)}; + // sig.poll; + FluidKrToBuf.kr(sig,buf); + }.play; + + 1.wait; + + buf.loadToFloatArray(action:{ + arg vals; + vals.postln; + }); + }.play; +} +) diff --git a/test/foo b/test/foo new file mode 100644 index 0000000..e69de29