From 65f4efbfb8662aba8582c85b400e14ef00b940ed Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Thu, 20 Sep 2018 09:41:59 +0100 Subject: [PATCH] all files finished and with working relative directory --- .../HelpSource/Classes/FluidBufCompose.schelp | 4 +- .../HelpSource/Classes/FluidBufHPSS.schelp | 2 +- .../HelpSource/Classes/FluidBufNMF.schelp | 4 +- .../Classes/FluidBufNoveltySlice.schelp | 2 +- .../HelpSource/Classes/FluidBufSines.schelp | 4 +- .../Classes/FluidBufTransientSlice.schelp | 44 +++++++++++++++++- .../Classes/FluidBufTransients.schelp | 39 +++++++++++++++- .../HelpSource/Classes/FluidGain.schelp | 4 +- .../HelpSource/Classes/FluidHPSS.schelp | 2 +- .../HelpSource/Classes/FluidSTFTPass.schelp | 4 +- .../HelpSource/Classes/FluidSines.schelp | 4 +- .../Classes/FluidTransientSlice.schelp | 46 +++++++++++++------ .../HelpSource/Classes/FluidTransients.schelp | 41 +++++++++++------ src/FluidTransients/test.scd | 4 +- 14 files changed, 159 insertions(+), 45 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufCompose.schelp b/release-packaging/HelpSource/Classes/FluidBufCompose.schelp index d899e81..7def2b3 100644 --- a/release-packaging/HelpSource/Classes/FluidBufCompose.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufCompose.schelp @@ -85,8 +85,8 @@ EXAMPLES:: code:: // load some buffers ( -b = Buffer.read(s,"../../AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav".resolveRelative); -c = Buffer.read(s,"../../AudioFiles/Tremblay-SA-UprightPianoPedalWide.wav".resolveRelative); +b = Buffer.read(s,File.realpath(FluidBufCompose.class.filenameSymbol).dirname.replace("Classes","AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav")); +c = Buffer.read(s,File.realpath(FluidBufCompose.class.filenameSymbol).dirname.replace("Classes","AudioFiles/Tremblay-SA-UprightPianoPedalWide.wav")); d = Buffer.new(s); ) diff --git a/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp b/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp index 0b3afd1..7529e30 100644 --- a/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp @@ -119,7 +119,7 @@ EXAMPLES:: code:: //load buffers ( - b = Buffer.read(s,"../../AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav".resolveRelative); + b = Buffer.read(s,File.realpath(FluidBufHPSS.class.filenameSymbol).dirname.replace("Classes","AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav")); c = Buffer.new(s); d = Buffer.new(s); e = Buffer.new(s); diff --git a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp index 9716c8c..7997b76 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp @@ -117,7 +117,7 @@ EXAMPLES:: code:: // set some buffers and parameters ( -b = Buffer.read(s,"../../AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav".resolveRelative); +b = Buffer.read(s,File.realpath(FluidBufNMF.class.filenameSymbol).dirname.replace("Classes","AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav")); c = Buffer.new(s); x = Buffer.new(s); y = Buffer.new(s); @@ -192,7 +192,7 @@ c.plot;x.plot; y.plot; //set some buffers ( -b = Buffer.read(s,"../../AudioFiles/Tremblay-AaS-AcousticStrums-M.wav".resolveRelative); +b = Buffer.read(s,File.realpath(FluidBufNMF.class.filenameSymbol).dirname.replace("Classes","AudioFiles/Tremblay-AaS-AcousticStrums-M.wav")); c = Buffer.new(s); x = Buffer.new(s); e = Buffer.alloc(s,1,1); diff --git a/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp b/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp index 57ac83a..ba1962e 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp @@ -60,7 +60,7 @@ EXAMPLES:: code:: // load some buffers ( -b = Buffer.read(s,"../../AudioFiles/Tremblay-AaS-AcousticStrums-M.wav".resolveRelative); +b = Buffer.read(s,File.realpath(FluidBufNoveltySlice.class.filenameSymbol).dirname.replace("Classes","AudioFiles/Tremblay-AaS-AcousticStrums-M.wav")); c = Buffer.new(s); ) diff --git a/release-packaging/HelpSource/Classes/FluidBufSines.schelp b/release-packaging/HelpSource/Classes/FluidBufSines.schelp index d25506b..c1d58db 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSines.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSines.schelp @@ -9,7 +9,7 @@ This class triggers a Sinusoidal Modelling process on buffers on the non-real-ti The algorithm will take a buffer in, and will divide it in two parts: LIST:: ## a reconstruction of what its detects as sinusoidal; - ## a residual derived from the previous buffert to allow null-summing:: + ## a residual derived from the previous buffer to allow null-summing:: The whole process is based on the assumption that signal is made of pitched steady components that have a long-enough duration and are periodic enough to be perceived as such, that can be tracked, resynthesised and removed from the original, leaving behind what is considered as non-pitched, noisy, and/or transient. It first tracks the peaks, then checks if they are the continuation of a peak in previous spectral frames, by assigning them a track. More information on this model, and on how it links to musicianly thinking, are availabe in LINK::Guides/FluCoMa:: overview file. @@ -74,7 +74,7 @@ EXAMPLES:: code:: // create some buffers ( -b = Buffer.read(s,"../../AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav".resolveRelative); +b = Buffer.read(s,File.realpath(FluidBufSines.class.filenameSymbol).dirname.replace("Classes","AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav")); c = Buffer.new(s); d = Buffer.new(s); ) diff --git a/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp index e620352..c3c12b2 100644 --- a/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp @@ -67,5 +67,47 @@ RETURNS:: EXAMPLES:: code:: -(some example code) +// load some buffers +( +b = Buffer.read(s,File.realpath(FluidBufTransientSlice.class.filenameSymbol).dirname.replace("Classes","AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav")); +c = Buffer.new(s); +) + +// with basic parameters +( + Routine{ + t = Main.elapsedTime; + FluidBufTransientSlice.process(s,b.bufnum, transBufNum:c.bufnum, order:80, debounce:4410); + s.sync; + (Main.elapsedTime - t).postln; +}.play +); + +//check the number of slices +c.query; + +//loops over a splice +( +{ + BufRd.ar(1, b.bufnum, + Phasor.ar(0,1, + BufRd.kr(1, c.bufnum, + MouseX.kr(0, BufFrames.kr(c.bufnum) - 1), 0, 1), + BufRd.kr(1, c.bufnum, + MouseX.kr(1, BufFrames.kr(c.bufnum)), 0, 1), + BufRd.kr(1,c.bufnum, + MouseX.kr(0, BufFrames.kr(c.bufnum) - 1), 0, 1)), 0, 1); + }.play; +) + + +// with everything changed to make it much faster +( +Routine{ + t = Main.elapsedTime; + FluidBufTransients.process(s,b.bufnum, 44100, 44100, 0, 0, c.bufnum, d.bufnum, 100, 512,256,1,2,1,12,20); + s.sync; + (Main.elapsedTime - t).postln; +}.play +) :: diff --git a/release-packaging/HelpSource/Classes/FluidBufTransients.schelp b/release-packaging/HelpSource/Classes/FluidBufTransients.schelp index ca09715..fb793ee 100644 --- a/release-packaging/HelpSource/Classes/FluidBufTransients.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufTransients.schelp @@ -75,6 +75,41 @@ RETURNS:: EXAMPLES:: code:: -(some example code) -:: +( +b = Buffer.read(s,File.realpath(FluidBufTransients.class.filenameSymbol).dirname.replace("Classes","AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav")); +c = Buffer.new(s); +d = Buffer.new(s); +) + +// with basic params +( +Routine{ + t = Main.elapsedTime; + FluidBufTransients.process(s,b.bufnum, transBufNum:c.bufnum, resBufNum:d.bufnum); + s.sync; + (Main.elapsedTime - t).postln; +}.play +); + +c.query; +c.play; +d.query; +d.play; + +//nullsumming tests +{(PlayBuf.ar(1,c.bufnum))+(PlayBuf.ar(1,d.bufnum))+(-1*PlayBuf.ar(1,b.bufnum,doneAction:2))}.play + + +// with everything changed to make it much faster +( +Routine{ + t = Main.elapsedTime; + FluidBufTransients.process(s,b.bufnum, 0, 88200, 0, 0, c.bufnum, d.bufnum, 100, 512,256,1,2,1,12,20); + s.sync; + (Main.elapsedTime - t).postln; +}.play +) + :: + + \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidGain.schelp b/release-packaging/HelpSource/Classes/FluidGain.schelp index a20f9e8..eb54159 100644 --- a/release-packaging/HelpSource/Classes/FluidGain.schelp +++ b/release-packaging/HelpSource/Classes/FluidGain.schelp @@ -1,7 +1,7 @@ TITLE:: FluidGain SUMMARY:: Real-Time Buffered Gain Changer -CATEGORIES:: UGens>Algebraic -RELATED::Classes/UnaryOpFunction +CATEGORIES:: UGens>Algebraic, Libraries>FluidDecomposition, UGens>Buffer +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition,Classes/UnaryOpFunction DESCRIPTION:: diff --git a/release-packaging/HelpSource/Classes/FluidHPSS.schelp b/release-packaging/HelpSource/Classes/FluidHPSS.schelp index 24b70e1..a094850 100644 --- a/release-packaging/HelpSource/Classes/FluidHPSS.schelp +++ b/release-packaging/HelpSource/Classes/FluidHPSS.schelp @@ -92,7 +92,7 @@ EXAMPLES:: CODE:: //load a soundfile to play - b = Buffer.read(s,"../../AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav".resolveRelative); + b = Buffer.read(s,File.realpath(FluidHPSS.class.filenameSymbol).dirname.replace("Classes","AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav")); // run with basic parameters (left is harmonic, right is percussive) {FluidHPSS.ar(PlayBuf.ar(1,b.bufnum,loop:1))}.play diff --git a/release-packaging/HelpSource/Classes/FluidSTFTPass.schelp b/release-packaging/HelpSource/Classes/FluidSTFTPass.schelp index 2252f44..67c6806 100644 --- a/release-packaging/HelpSource/Classes/FluidSTFTPass.schelp +++ b/release-packaging/HelpSource/Classes/FluidSTFTPass.schelp @@ -1,7 +1,7 @@ TITLE:: FluidSTFTPass SUMMARY:: Real-Time FFT/IFFT return trip. -CATEGORIES:: UGens>Algebraic -RELATED:: Classes/UnaryOpFunction +CATEGORIES:: UGens>Algebraic, Libraries>FluidDecomposition, UGens>Buffer +RELATED:: Guides/FluCoMa, Guides/FluidDecomposition,Classes/UnaryOpFunction DESCRIPTION:: diff --git a/release-packaging/HelpSource/Classes/FluidSines.schelp b/release-packaging/HelpSource/Classes/FluidSines.schelp index 966586f..b37b21c 100644 --- a/release-packaging/HelpSource/Classes/FluidSines.schelp +++ b/release-packaging/HelpSource/Classes/FluidSines.schelp @@ -8,7 +8,7 @@ This class applies a Sinusoidal Modelling process on its audio input. It impleme The algorithm will take an audio in, and will divide it in two parts: LIST:: ## a reconstruction of what its detects as sinusoidal; - ## a residual derived from the previous buffert to allow null-summing:: + ## a residual derived from the previous signal to allow null-summing:: The whole process is based on the assumption that signal is made of pitched steady components that have a long-enough duration and are periodic enough to be perceived as such, that can be tracked, resynthesised and removed from the original, leaving behind what is considered as non-pitched, noisy, and/or transient. It first tracks the peaks, then checks if they are the continuation of a peak in previous spectral frames, by assigning them a track. More information on this model, and on how it links to musicianly thinking, are availabe in LINK::Guides/FluCoMa:: overview file. @@ -54,7 +54,7 @@ EXAMPLES:: CODE:: // load some audio to play -b = Buffer.read(s,"../../AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav".resolveRelative); +b = Buffer.read(s,File.realpath(FluidSines.class.filenameSymbol).dirname.replace("Classes","AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav")); // run with basic parameters - left is sinusoidal model, right is residual {FluidSines.ar(PlayBuf.ar(1,b.bufnum,loop:1))}.play diff --git a/release-packaging/HelpSource/Classes/FluidTransientSlice.schelp b/release-packaging/HelpSource/Classes/FluidTransientSlice.schelp index 4578e18..e810548 100644 --- a/release-packaging/HelpSource/Classes/FluidTransientSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidTransientSlice.schelp @@ -4,9 +4,9 @@ CATEGORIES:: Libraries>FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition DESCRIPTION:: -It is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote:: -This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: +This class implements a real-time transient-based slice extractor relying on the same algorithm than Classes/FluidBufTransients using clicks/transients/derivation/anomaly in the signal to estimate the slicing points. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: + The process will return an audio steam with sample-long impulses at estimated starting points of the different slices. CLASSMETHODS:: @@ -14,31 +14,31 @@ METHOD:: ar The audio rate version of the object. ARGUMENT:: in -(describe argument here) + The audio to be processed. ARGUMENT:: order -(describe argument here) + The order in samples of the impulse response filter used to model the estimated continuous signal. It is how many previous samples are used by the algorythm to predict the next one as reference for the model. The higher the order, the more accurate is its spectral definition, not unlike fft, improving low frequency resolution, but it differs in that it is not conected to its temporal resolution. ARGUMENT:: blockSize -(describe argument here) + The size in samples of frame on which it the algorithm is operating. High values are more cpu intensive, and also determines the maximum transient size, which will not be allowed to be more than half that lenght in size. ARGUMENT:: padSize -(describe argument here) + The size of the handles on each sides of the block simply used for analysis purpose and avoid boundary issues. ARGUMENT:: skew -(describe argument here) + The nervousness of the bespoke detection function with values from -10 to 10. It allows to decide how peaks are amplified or smoothed before the thresholding. High values increase the sensitivity to small variations. ARGUMENT:: threshFwd -(describe argument here) + The threshold of the onset of the smoothed error function. It allows tight start of the identification of the anomaly as it proceeds forward. ARGUMENT:: threshBack -(describe argument here) + The threshold of the offset of the smoothed error function. As it proceeds backwards in time, it allows tight ending of the identification of the anomaly. ARGUMENT:: winSize -(describe argument here) + The averaging window of the error detection function. It needs smoothing as it is very jittery. The longer the window, the less precise, but the less false positive. ARGUMENT:: debounce -(describe argument here) + The window size in sample within with positive detections will be clumped together to avoid overdetecting in time. No slice will be shorter than this duration. RETURNS:: An audio stream with impulses at detected transients. The latency between the input and the output is (blockSize + padSize - order) samples. @@ -47,5 +47,25 @@ RETURNS:: EXAMPLES:: CODE:: -(some example code) -:: \ No newline at end of file +//load some sounds +b = Buffer.read(s,File.realpath(FluidTransientSlice.class.filenameSymbol).dirname.replace("Classes","AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav")); + +// basic param (the process add a latency of (blockSize + padding - order) samples +{var sig = PlayBuf.ar(1,b.bufnum,loop:1); [FluidTransientSlice.ar(sig)*0.5, DelayN.ar(sig, 1, ((256 + 128 - 20)/ s.sampleRate))]}.play + +// other parameters +{var sig = PlayBuf.ar(1,b.bufnum,loop:1); [FluidTransientSlice.ar(sig,order:80,debounce:2205)*0.5, DelayN.ar(sig, 1, ((256 + 128 - 80)/ s.sampleRate))]}.play + +// more musical trans-trigged autopan +( +{ + var sig, trig, syncd, pan; + sig = PlayBuf.ar(1,b.bufnum,loop:1); + trig = FluidTransientSlice.ar(sig,order:10,debounce:2205); + syncd = DelayN.ar(sig, 1, ((256 + 128 - 10)/ s.sampleRate)); + pan = TRand.ar(-1,1,trig); + Pan2.ar(syncd,pan); +}.play +) +:: + diff --git a/release-packaging/HelpSource/Classes/FluidTransients.schelp b/release-packaging/HelpSource/Classes/FluidTransients.schelp index d7c3a3b..8552691 100644 --- a/release-packaging/HelpSource/Classes/FluidTransients.schelp +++ b/release-packaging/HelpSource/Classes/FluidTransients.schelp @@ -4,9 +4,14 @@ CATEGORIES:: Libraries>FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition DESCRIPTION:: -It is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote:: +This class applies a real-time transient extractor on its input. It implements declicking algorithm from chapter 5 of the classic Digital Audio Restoration by Godsill, Simon J., Rayner, Peter J.W. with some bespoke improvements on the detection function tracking. It is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).:: + The algorithm will take an audio in, and will divide it in two outputs: LIST:: + ## the transients, estimated from the signal and extracted from it; + ## the remainder of the material, as estimated by the algorithm.:: + + The whole process is based on the assumption that a transient is an element that is deviating from the surrounding material, as sort of click or anomaly. The algorythm then estimates what should have happened if the signal had followed its normal path, and resynthesises this estimate, removing the anomaly which is considered as the transient. More information on signal estimation, and on its musicianly usage, are availabe in LINK::Guides/FluCoMa:: overview file. CLASSMETHODS:: @@ -14,31 +19,31 @@ METHOD:: ar The audio rate version of the object. ARGUMENT:: in -(describe argument here) + The audio to be processed. ARGUMENT:: order -(describe argument here) + The order in samples of the impulse response filter used to model the estimated continuous signal. It is how many previous samples are used by the algorythm to predict the next one as reference for the model. The higher the order, the more accurate is its spectral definition, not unlike fft, improving low frequency resolution, but it differs in that it is not conected to its temporal resolution. ARGUMENT:: blockSize -(describe argument here) + The size in samples of frame on which it the algorithm is operating. High values are more cpu intensive, and also determines the maximum transient size, which will not be allowed to be more than half that lenght in size. ARGUMENT:: padSize -(describe argument here) + The size of the handles on each sides of the block simply used for analysis purpose and avoid boundary issues. ARGUMENT:: skew -(describe argument here) + The nervousness of the bespoke detection function with values from -10 to 10. It allows to decide how peaks are amplified or smoothed before the thresholding. High values increase the sensitivity to small variations. ARGUMENT:: threshFwd -(describe argument here) + The threshold of the onset of the smoothed error function. It allows tight start of the identification of the anomaly as it proceeds forward. ARGUMENT:: threshBack -(describe argument here) + The threshold of the offset of the smoothed error function. As it proceeds backwards in time, it allows tight ending of the identification of the anomaly. ARGUMENT:: winSize -(describe argument here) + The averaging window of the error detection function. It needs smoothing as it is very jittery. The longer the window, the less precise, but the less false positive. ARGUMENT:: debounce -(describe argument here) + The window size in sample within with positive detections will be clumped together to avoid overdetecting in time. RETURNS:: An array of two audio streams: [0] is the transient extracted, [1] is the rest. The latency between the input and the output is (blockSize + padSize - order) samples. @@ -46,6 +51,16 @@ RETURNS:: EXAMPLES:: -CODE:: -(some example code) -:: \ No newline at end of file +CODE: +//load some buffer +b = Buffer.read(s,File.realpath(FluidTransients.class.filenameSymbol).dirname.replace("Classes","AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav")); + +// basic parameters +{FluidTransients.ar(PlayBuf.ar(1,b.bufnum,loop:1))}.play + +// tweaked parameterss +{FluidTransients.ar(PlayBuf.ar(1,b.bufnum,loop:1),80,threshFwd:MouseX.kr(0,5),threshBack:MouseY.kr(0,2))}.play + +// null test (the process add a latency of (blockSize + padding - order) samples +{var sig = PlayBuf.ar(1,b.bufnum,loop:1); [FluidTransients.ar(sig).sum - DelayN.ar(sig, 1, ((256 + 128 - 20)/ s.sampleRate))]}.play +:: diff --git a/src/FluidTransients/test.scd b/src/FluidTransients/test.scd index 3180b64..363a719 100644 --- a/src/FluidTransients/test.scd +++ b/src/FluidTransients/test.scd @@ -1,4 +1,5 @@ s.reboot; +s.quit b = Buffer.read(s,"../../release-packaging/AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav".resolveRelative); @@ -9,4 +10,5 @@ b = Buffer.read(s,"../../release-packaging/AudioFiles/Tremblay-AaS-SynthTwoVoice {FluidTransients.ar(PlayBuf.ar(1,b.bufnum,loop:1),80,threshFwd:MouseX.kr(0,5),threshBack:MouseY.kr(0,2))}.play // null test (the process add a latency of (blockSize + padding - order) samples -{var sig = PlayBuf.ar(1,b.bufnum,loop:1); [FluidTransients.ar(sig).sum - DelayN.ar(sig, 1, ((256 + 128 - 20)/ s.sampleRate))]}.play \ No newline at end of file +{var sig = PlayBuf.ar(1,b.bufnum,loop:1); [FluidTransients.ar(sig).sum - DelayN.ar(sig, 1, ((256 + 128 - 20)/ s.sampleRate))]}.play +