From 6cd431622eb13d7940c961d044ce4851a2af5bb4 Mon Sep 17 00:00:00 2001 From: Ted Moore Date: Mon, 7 Feb 2022 15:05:48 -0500 Subject: [PATCH] BufAudioTransport now has A-B based Arguments --- .../Classes/FluidBufAudioTransport.sc | 36 +- .../Classes/FluidBufAudioTransport.schelp | 307 +++++++++++++++--- 2 files changed, 288 insertions(+), 55 deletions(-) diff --git a/release-packaging/Classes/FluidBufAudioTransport.sc b/release-packaging/Classes/FluidBufAudioTransport.sc index 5850a0c..973afa6 100644 --- a/release-packaging/Classes/FluidBufAudioTransport.sc +++ b/release-packaging/Classes/FluidBufAudioTransport.sc @@ -4,29 +4,29 @@ FluidBufAudioTransport : FluidBufProcessor { ^\FluidBufAudioTransp } - *kr { |source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation = 0.0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| + *kr { |sourceA, startFrameA = 0, numFramesA = -1, startChanA = 0, numChansA = -1, sourceB, startFrameB = 0, numFramesB = -1, startChanB = 0, numChansB = -1, destination, interpolation = 0.0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source1.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; - source2.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; - source1 = source1.asUGenInput; - source2 = source2.asUGenInput; + sourceA.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; + sourceB.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; + sourceA = sourceA.asUGenInput; + sourceB = sourceB.asUGenInput; destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw}; destination = destination.asUGenInput; - ^FluidProxyUgen.kr(this.objectClassName++\Trigger,-1, source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); + ^FluidProxyUgen.kr(this.objectClassName++\Trigger,-1, sourceA, startFrameA, numFramesA, startChanA, numChansA, sourceB, startFrameA, numFramesA, startChanB, numChansB, destination, interpolation, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); } - *process { |server, source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + *process { |server, sourceA, startFrameA = 0, numFramesA = -1, startChanA = 0, numChansA = -1, sourceB, startFrameB = 0, numFramesB = -1, startChanB = 0, numChansB = -1, destination, interpolation=0.0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source1.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; - source2.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; - source1 = source1.asUGenInput; - source2 = source2.asUGenInput; + sourceA.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; + sourceB.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; + sourceA = sourceA.asUGenInput; + sourceB = sourceB.asUGenInput; destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw}; destination = destination.asUGenInput; @@ -34,17 +34,17 @@ FluidBufAudioTransport : FluidBufProcessor { ^this.new( server, nil, [destination] ).processList( - [source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame2, numFrames2, startChan2, numChans2, destination, interpolation, windowSize, hopSize, fftSize,maxFFTSize,0], freeWhenDone, action + [sourceA, startFrameA, numFramesA, startChanA, numChansA, sourceB, startFrameB, numFramesB, startChanB, numChansB, destination, interpolation, windowSize, hopSize, fftSize,maxFFTSize,0], freeWhenDone, action ) } - *processBlocking { |server, source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + *processBlocking { |server, sourceA, startFrameA = 0, numFramesA = -1, startChanA = 0, numChansA = -1, sourceB, startFrameB = 0, numFramesB = -1, startChanB = 0, numChansB = -1, destination, interpolation=0.0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source1.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; - source2.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; - source1 = source1.asUGenInput; - source2 = source2.asUGenInput; + sourceA.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; + sourceB.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; + sourceA = sourceA.asUGenInput; + sourceB = sourceB.asUGenInput; destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw}; destination = destination.asUGenInput; @@ -52,7 +52,7 @@ FluidBufAudioTransport : FluidBufProcessor { ^this.new( server, nil, [destination] ).processList( - [source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame2, numFrames2, startChan2, numChans2, destination, interpolation, windowSize, hopSize, fftSize,maxFFTSize,1], freeWhenDone, action + [sourceA, startFrameA, numFramesA, startChanA, numChansA, sourceB, startFrameB, numFramesB, startChanB, numChansB, destination, interpolation, windowSize, hopSize, fftSize,maxFFTSize,1], freeWhenDone, action ) } } diff --git a/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp b/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp index ab503ed..50bd11a 100644 --- a/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp @@ -1,77 +1,310 @@ TITLE:: FluidBufAudioTransport -summary:: Interpolate between buffers -categories:: Libraries>FluidCorpusManipulation -related:: Classes/FluidAudioTransport - +SUMMARY:: Interpolate between buffers +CATEGORIES:: Libraries>FluidCorpusManipulation +RELATED:: Classes/FluidAudioTransport DESCRIPTION:: -Interpolates between the spectra of two sounds using the Optimal Transport algorithm -See -Henderson and Solomonm (2019) AUDIO TRANSPORT: A GENERALIZED PORTAMENTO VIA OPTIMAL TRANSPORT, DaFx + + Interpolates between the spectra of two sounds using the Optimal Transport algorithm + + See Henderson and Solomonm (2019) AUDIO TRANSPORT: A GENERALIZED PORTAMENTO VIA OPTIMAL TRANSPORT, DaFx + + + CLASSMETHODS:: METHOD:: process, processBlocking -Process two audio link::Classes/Buffer:: + Processs the source LINK::Classes/Buffer:: on the LINK::Classes/Server::. CODE::processBlocking:: will execute directly in the server command FIFO, whereas CODE::process:: will delegate to a separate worker thread. The latter is generally only worthwhile for longer-running jobs where you don't wish to tie up the server. ARGUMENT:: server -The server the process runs on + The LINK::Classes/Server:: on which the buffers to be processed are allocated. + +ARGUMENT:: sourceA + + + The first source buffer + + +ARGUMENT:: startFrameA + + + offset into the first source buffer (samples) + + STRONG::Constraints:: + + LIST:: + ## + Minimum: 0 + + :: + +ARGUMENT:: numFramesA + + + number of samples to use from first source buffer + + +ARGUMENT:: startChanA + + + starting channel of first source buffer + + STRONG::Constraints:: + + LIST:: + ## + Minimum: 0 + + :: + +ARGUMENT:: numChansA + + + number of channels to process in first source buffer + + +ARGUMENT:: sourceB + + + the second source buffer + + +ARGUMENT:: startFrameB + + + offset into the second source buffer (samples) -ARGUMENT:: source1 -The first source buffer + STRONG::Constraints:: -ARGUMENT:: startFrame1 -offset into the first source buffer (samples) + LIST:: + ## + Minimum: 0 -ARGUMENT:: numFrames1 -number of samples to use from first source buffer + :: -ARGUMENT:: startChan1 -starting channel of first source buffer +ARGUMENT:: numFramesB -ARGUMENT:: numChans1 -number of channels to process in first source buffer + + number of samples to process from second buffer -ARGUMENT:: source2 -the second source buffer -ARGUMENT:: startFrame2 -offset into the second source buffer (samples) +ARGUMENT:: startChanB -ARGUMENT:: numFrames2 -number of samples to process from second buffer + + starting channel for second buffer -ARGUMENT:: startChan2 -starting channel for second buffer + STRONG::Constraints:: + + LIST:: + ## + Minimum: 0 + + :: + +ARGUMENT:: numChansB + + + number of channels to process in second buffer -ARGUMENT:: numChans2 -number of channels to process in second buffer ARGUMENT:: destination -buffer for interpolated audio + + + buffer for interpolated audio + ARGUMENT:: interpolation -The amount to interpolate between A and B (0-1, 0 = A, 1 = B) + + + The amount to interpolate between A and B (0-1, 0 = A, 1 = B) + + STRONG::Constraints:: + + LIST:: + ## + Minimum: 0.0 + + ## + Maximum: 1.0 + + :: ARGUMENT:: windowSize - The window size. As spectral differencing relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty + + + The window size. As spectral differencing relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. LINK::http://www.subsurfwiki.org/wiki/Gabor_uncertainty:: + ARGUMENT:: hopSize - The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). + + + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). + ARGUMENT:: fftSize - The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2. + + The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2. + + + ARGUMENT:: freeWhenDone -Free the server instance when processing complete. Default true + Free the server instance when processing complete. Default CODE::true:: ARGUMENT:: action - A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [destination] as an argument. + A function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed CODE::[features]:: as an argument. + +RETURNS:: An instance of the processor + +METHOD:: kr + Trigger the equivalent behaviour to CODE::processBlocking / process:: from a LINK::Classes/Synth::. Can be useful for expressing a sequence of buffer and data processing jobs to execute. Note that the work still executes on the server command FIFO (not the audio thread), and it is the caller's responsibility to manage the sequencing, using the CODE::done:: status of the various UGens. +ARGUMENT:: sourceA + + + The first source buffer + + +ARGUMENT:: startFrameA + + + offset into the first source buffer (samples) + + STRONG::Constraints:: + + LIST:: + ## + Minimum: 0 + + :: + +ARGUMENT:: numFramesA + + + number of samples to use from first source buffer + + +ARGUMENT:: startChanA + + + starting channel of first source buffer + + STRONG::Constraints:: + + LIST:: + ## + Minimum: 0 + + :: + +ARGUMENT:: numChansA + + + number of channels to process in first source buffer + + +ARGUMENT:: sourceB + + + the second source buffer + + +ARGUMENT:: startFrameB + + + offset into the second source buffer (samples) + + STRONG::Constraints:: + + LIST:: + ## + Minimum: 0 + + :: + +ARGUMENT:: numFramesB + + + number of samples to process from second buffer + + +ARGUMENT:: startChanB + + + starting channel for second buffer + + STRONG::Constraints:: + + LIST:: + ## + Minimum: 0 + + :: + +ARGUMENT:: numChansB + + + number of channels to process in second buffer + + +ARGUMENT:: destination + + + buffer for interpolated audio + + +ARGUMENT:: interpolation + + + The amount to interpolate between A and B (0-1, 0 = A, 1 = B) + + STRONG::Constraints:: + + LIST:: + ## + Minimum: 0.0 + + ## + Maximum: 1.0 + + :: + +ARGUMENT:: windowSize + + + The window size. As spectral differencing relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. LINK::http://www.subsurfwiki.org/wiki/Gabor_uncertainty:: + + +ARGUMENT:: hopSize + + + The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2). + + +ARGUMENT:: fftSize + + + The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2. + + + +ARGUMENT:: trig + A CODE::kr:: signal that will trigger execution + +ARGUMENT:: blocking + Whether to execute this process directly on the server command FIFO or delegate to a worker thread. See CODE::processBlocking/process:: for caveats. + INSTANCEMETHODS:: +METHOD:: kr + Returns a UGen that reports the progress of the running task when executing in a worker thread. Calling code::scope:: with this can be used for a convinient progress monitor -private:: synth, server +METHOD:: cancel + Cancels non-blocking processing +METHOD:: wait + When called in the context of a LINK::Classes/Routine:: (it won't work otherwise), will block execution until the processor has finished. This can be convinient for writing sequences of processes more linearly than using lots of nested actions. + EXAMPLES:: code::