From 0d9236e845d84c22977a6fbe167e3c248df78d54 Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Tue, 14 Jul 2026 10:28:20 -0500 Subject: [PATCH 1/2] refactor to put all items under flexplugins namespace --- README.md | 3 + src/filters/FIR.schelp | 12 +- src/filters/filters.sc | 1 + src/filters/fir.cpp | 10 +- src/filters/fir.hpp | 4 +- src/generators/arrayheap.cpp | 6 +- src/generators/arrayheap.hpp | 32 ++-- src/pv/pvCFreeze.cpp | 18 +-- src/pv/pvStretch.cpp | 268 +++++++++++++++++----------------- src/rubberband/ringbuffer.hpp | 76 +++++----- 10 files changed, 224 insertions(+), 206 deletions(-) diff --git a/README.md b/README.md index 45c662c..0d17d69 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,9 @@ A phase vocoder pitch shifter and time stretcher using the [RubberBand library]( ### RubberBandStretcherBuf A phase vocoder pitch shifter and time stretcher using the [RubberBand library](https://breakfastquay.com/rubberband/). This version writes the stretched audio to a buffer rather than outputting it directly. +### FIR +A flexible FIR filter that allows you to provide an arbitrary number of coefficients directly (as opposed to loading them in a buffer with Convolution3). + ### ImpulseDropout A modified version of Impulse that randomly drops a percentage of impulses, producing a stuttering effect. diff --git a/src/filters/FIR.schelp b/src/filters/FIR.schelp index 0e25110..0d53dde 100644 --- a/src/filters/FIR.schelp +++ b/src/filters/FIR.schelp @@ -4,18 +4,26 @@ related:: Classes/Convolution3, Classes/FOS, Classes/SOS, Classes/OneZero categories:: Libraries>FlexUGens, UGens>Filters>Linear Description:: -A no-nonsense FIR filter where you provide your own coefficients. FIR duplicates the functionality +A flexible time-domain FIR filter where you provide your own coefficients. FIR duplicates the functionality of link::Classes/Convolution3::. The difference is that FIR does not require you to load your filter coefficients into a link::Classes/Buffer::. Instead, you can provide them directly as a link::Classes/Ref:: in the same way as is done in link::Classes/DynKlank::. +FIR internally limits the number of coefficients to 32. This is because time-domain convolution only makes sense for +very short impulse responses--Frank Wefers writes that "the +theoretical break-even point where FFT-based convolution becomes +faster is between 16-32 samples."footnote::Frank Wefers, "Partitioned convolution algorithms +for real-time auralization," Logos Verlag Berlin (2015), 224.:: One potential use of this UGen is +to perform Savitsky-Golay filtering for control signal smoothing +(see link::https://en.wikipedia.org/wiki/Savitzky%E2%80%93Golay_filter:: for coefficient tables). + classmethods:: method::ar argument::coefs A link::Classes/Ref:: to a list of FIR filter coefficients. The number of coefficients can change -over time, but can never exceed the server block size (if it does, extra coefficients will be truncated). +over time, but can never exceed 32 (if it does, extra coefficients will be truncated). argument::in The input signal to filter diff --git a/src/filters/filters.sc b/src/filters/filters.sc index d0038bc..dcd0de5 100644 --- a/src/filters/filters.sc +++ b/src/filters/filters.sc @@ -20,6 +20,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// A flexible FIR filter FIR : UGen { *ar { arg coefs, in, mul=1, add=0; diff --git a/src/filters/fir.cpp b/src/filters/fir.cpp index 73f5a0c..a0cd847 100644 --- a/src/filters/fir.cpp +++ b/src/filters/fir.cpp @@ -3,7 +3,7 @@ File: fir.cpp Author: Jeff Martin Description: -A raw FIR filter +A flexible FIR filter Copyright © 2026 by Jeffrey Martin. All rights reserved. Website: https://www.jeffreymartincomposer.com @@ -26,8 +26,8 @@ along with this program. If not, see . extern InterfaceTable *ft; FlexPlugins::FIR::FIR() { - m_z = static_cast(RTAlloc(mWorld, fullBufferSize() * sizeof(float))); - for (size_t i = 0; i < fullBufferSize(); i++) { + m_z = static_cast(RTAlloc(mWorld, MAX_COEFS * sizeof(float))); + for (size_t i = 0; i < MAX_COEFS; i++) { m_z[i] = 0.f; } set_calc_function(); @@ -40,13 +40,13 @@ FlexPlugins::FIR::~FIR() { void FlexPlugins::FIR::next(int inNumSamples) { size_t numCoefs = static_cast(mNumInputs - 1); - numCoefs = sc_clip(numCoefs, 0, static_cast(fullBufferSize())); + numCoefs = sc_clip(numCoefs, 0, static_cast(MAX_COEFS)); const float *inBuf = in(0); float *outBuf = out(0); for (size_t i = 0; i < inNumSamples; i++) { float convResult = 0.f; // unit delay - for (size_t j = fullBufferSize() - 1; j > 0; j--) { + for (size_t j = MAX_COEFS - 1; j > 0; j--) { m_z[j] = m_z[j-1]; } m_z[0] = inBuf[i]; diff --git a/src/filters/fir.hpp b/src/filters/fir.hpp index 552caef..e32d5f6 100644 --- a/src/filters/fir.hpp +++ b/src/filters/fir.hpp @@ -3,7 +3,7 @@ File: fir.hpp Author: Jeff Martin Description: -A raw FIR filter +A flexible FIR filter Copyright © 2026 by Jeffrey Martin. All rights reserved. Website: https://www.jeffreymartincomposer.com @@ -26,6 +26,8 @@ along with this program. If not, see . #include "SC_PlugIn.hpp" +#define MAX_COEFS 32 + namespace FlexPlugins { class FIR : public SCUnit { public: diff --git a/src/generators/arrayheap.cpp b/src/generators/arrayheap.cpp index 4c337e0..67ed397 100644 --- a/src/generators/arrayheap.cpp +++ b/src/generators/arrayheap.cpp @@ -25,7 +25,7 @@ along with this program. If not, see . #include "arrayheap.hpp" // Inserts into the heap -int heapInsert(IntMinHeap* heap, int data) { +int FlexPlugins::heapInsert(IntMinHeap* heap, int data) { if (heap->size == heap->maxSize) { return 0; } else { @@ -54,7 +54,7 @@ int heapInsert(IntMinHeap* heap, int data) { } // Removes from the heap and returns the value popped. Returns 0 if the heap is empty. -int heapPop(IntMinHeap* heap) { +int FlexPlugins::heapPop(IntMinHeap* heap) { if (heap->size > 1) { int val = heap->heap[1]; heap->heap[1] = heap->heap[heap->size-1]; @@ -97,7 +97,7 @@ int heapPop(IntMinHeap* heap) { } // Safe peek at the top of the heap -int heapPeek(IntMinHeap* heap) { +int FlexPlugins::heapPeek(IntMinHeap* heap) { if (heap->size > 1) { return heap->heap[1]; } else { diff --git a/src/generators/arrayheap.hpp b/src/generators/arrayheap.hpp index c21d879..632d698 100644 --- a/src/generators/arrayheap.hpp +++ b/src/generators/arrayheap.hpp @@ -25,18 +25,20 @@ along with this program. If not, see . #pragma once #include -/// A min heap for ints. -typedef struct { - int* heap; - size_t size; - size_t maxSize; -} IntMinHeap; - -/// Inserts into the heap -int heapInsert(IntMinHeap* heap, int data); - -/// Removes from the heap and returns the value popped. Returns 0 if the heap is empty. -int heapPop(IntMinHeap* heap); - -/// Safe peek at the top of the heap -int heapPeek(IntMinHeap* heap); \ No newline at end of file +namespace FlexPlugins { + /// A min heap for ints. + typedef struct { + int* heap; + size_t size; + size_t maxSize; + } IntMinHeap; + + /// Inserts into the heap + int heapInsert(IntMinHeap* heap, int data); + + /// Removes from the heap and returns the value popped. Returns 0 if the heap is empty. + int heapPop(IntMinHeap* heap); + + /// Safe peek at the top of the heap + int heapPeek(IntMinHeap* heap); +} diff --git a/src/pv/pvCFreeze.cpp b/src/pv/pvCFreeze.cpp index 1458c15..be84c86 100644 --- a/src/pv/pvCFreeze.cpp +++ b/src/pv/pvCFreeze.cpp @@ -63,22 +63,22 @@ void FlexPlugins::PV_CFreeze::next(int inNumSamples) { // Pull random DC and nyquist magnitudes p->dc = mDc[rgen.irand(mNumFrames)]; p->nyq = mNyq[rgen.irand(mNumFrames)]; - for (int xxn = 0; xxn < mNumBins; xxn++) { + for (int k = 0; k < mNumBins; k++) { // For each bin, grab a random magnitude and phase diff pair int idx = rgen.irand(mNumFrames); - idx = idx * mNumBins + xxn; - p->bin[xxn].mag = mMags[idx]; - mPhase[xxn] = sc_wrap(mPhase[xxn] + mPhaseDiffs[idx], 0.f, static_cast(twopi)); - p->bin[xxn].phase = mPhase[xxn]; + idx = idx * mNumBins + k; + p->bin[k].mag = mMags[idx]; + mPhase[k] = sc_wrap(mPhase[k] + mPhaseDiffs[idx], 0.f, static_cast(twopi)); + p->bin[k].phase = mPhase[k]; } } else { // We're writing to a circular buffer, so pull the current magnitude and phase diff arrays float *currentMagArr = mMags + (mWritePtr * mNumBins); float *currentPhaseDiffArr = mPhaseDiffs + (mWritePtr * mNumBins); - for (int xxn = 0; xxn < numbins; xxn++) { - currentMagArr[xxn] = p->bin[xxn].mag; - currentPhaseDiffArr[xxn] = sc_wrap(p->bin[xxn].phase - mPhase[xxn], 0.f, static_cast(twopi)); - mPhase[xxn] = p->bin[xxn].phase; + for (int k = 0; k < numbins; k++) { + currentMagArr[k] = p->bin[k].mag; + currentPhaseDiffArr[k] = sc_wrap(p->bin[k].phase - mPhase[k], 0.f, static_cast(twopi)); + mPhase[k] = p->bin[k].phase; } mDc[mWritePtr] = p->dc; mNyq[mWritePtr] = p->nyq; diff --git a/src/pv/pvStretch.cpp b/src/pv/pvStretch.cpp index f994baa..74ea4e1 100644 --- a/src/pv/pvStretch.cpp +++ b/src/pv/pvStretch.cpp @@ -354,18 +354,18 @@ void FlexPlugins::PV_PlayBufStretch::Stretch2( size_t hopSize) { outFrame->dc = frame->dc; outFrame->nyq = frame->nyq; - for (size_t xxk = 0; xxk < fftSize/2-1; xxk++) { - outFrame->bin[xxk].mag = frame->bin[xxk].mag; + for (size_t k = 0; k < fftSize/2-1; k++) { + outFrame->bin[k].mag = frame->bin[k].mag; // Compute the instantaneous frequency - float omegaK = twopi * (xxk+1) / fftSize; - float phaseInc = frame->bin[xxk].phase - framePrev->bin[xxk].phase - hopSize * omegaK; + float omegaK = twopi * (k+1) / fftSize; + float phaseInc = frame->bin[k].phase - framePrev->bin[k].phase - hopSize * omegaK; phaseInc = std::fmod(phaseInc + pi, twopi) - pi; float instantaneousFreq = omegaK + phaseInc/hopSize; // Compute the new phase - outFrame->bin[xxk].phase = sc_wrap( - outFramePrev->bin[xxk].phase + static_cast(hopSize * instantaneousFreq), + outFrame->bin[k].phase = sc_wrap( + outFramePrev->bin[k].phase + static_cast(hopSize * instantaneousFreq), 0.f, static_cast(twopi)); } @@ -393,31 +393,31 @@ void FlexPlugins::PV_PlayBufStretch::Stretch2Puckette( size_t hopSize) { outFrame->dc = frame->dc; outFrame->nyq = frame->nyq; - for (size_t xxk = 0; xxk < fftSize/2-1; xxk++) { - outFrame->bin[xxk].mag = frame->bin[xxk].mag; + for (size_t k = 0; k < fftSize/2-1; k++) { + outFrame->bin[k].mag = frame->bin[k].mag; // Compute the instantaneous frequency - float omegaK = twopi * (xxk+1) / fftSize; - float phaseInc = frame->bin[xxk].phase - framePrev->bin[xxk].phase - hopSize * omegaK; + float omegaK = twopi * (k+1) / fftSize; + float phaseInc = frame->bin[k].phase - framePrev->bin[k].phase - hopSize * omegaK; phaseInc = std::fmod(phaseInc + pi, twopi) - pi; float instantaneousFreq = omegaK + phaseInc/hopSize; // In Puckette-style phase locking, we make a substitution for the previous phase, // in order to "lock" phases of adjacent bins together. float prevPhase = 0.0; - if (xxk == 0) { - prevPhase = outFramePrev->bin[xxk].phase; - } else if (xxk == fftSize/2-2) { - prevPhase = outFramePrev->bin[xxk].phase; + if (k == 0) { + prevPhase = outFramePrev->bin[k].phase; + } else if (k == fftSize/2-2) { + prevPhase = outFramePrev->bin[k].phase; } else { - std::complex prevBinKMinus1 = std::polar(outFramePrev->bin[xxk-1].mag, outFramePrev->bin[xxk-1].phase); - std::complex prevBinK = std::polar(outFramePrev->bin[xxk].mag, outFramePrev->bin[xxk].phase); - std::complex prevBinKPlus1 = std::polar(outFramePrev->bin[xxk+1].mag, outFramePrev->bin[xxk+1].phase); + std::complex prevBinKMinus1 = std::polar(outFramePrev->bin[k-1].mag, outFramePrev->bin[k-1].phase); + std::complex prevBinK = std::polar(outFramePrev->bin[k].mag, outFramePrev->bin[k].phase); + std::complex prevBinKPlus1 = std::polar(outFramePrev->bin[k+1].mag, outFramePrev->bin[k+1].phase); prevPhase = std::arg(prevBinK - prevBinKMinus1 - prevBinKPlus1); } // Compute the new phase - outFrame->bin[xxk].phase = sc_wrap( + outFrame->bin[k].phase = sc_wrap( prevPhase + static_cast(hopSize * instantaneousFreq), 0.f, static_cast(twopi)); @@ -453,60 +453,60 @@ void FlexPlugins::PV_PlayBufStretch::Stretch2LarocheDolson( if (peakFinder->size() == 0) { // No phase locking if no peaks were acquired - for (size_t xxk = 0; xxk < fftSize/2-1; xxk++) { - outFrame->bin[xxk].mag = frame->bin[xxk].mag; + for (size_t k = 0; k < fftSize/2-1; k++) { + outFrame->bin[k].mag = frame->bin[k].mag; // Compute the instantaneous frequency - double omegaK = twopi * (xxk+1) / fftSize; - double phaseInc = frame->bin[xxk].phase - framePrev->bin[xxk].phase - hopSize * omegaK; + double omegaK = twopi * (k+1) / fftSize; + double phaseInc = frame->bin[k].phase - framePrev->bin[k].phase - hopSize * omegaK; phaseInc = std::fmod(phaseInc + pi, twopi) - pi; double instantaneousFreq = omegaK + phaseInc/hopSize; // Compute the phase - outFrame->bin[xxk].phase = sc_wrap( - outFramePrev->bin[xxk].phase + static_cast(hopSize * instantaneousFreq), + outFrame->bin[k].phase = sc_wrap( + outFramePrev->bin[k].phase + static_cast(hopSize * instantaneousFreq), 0.f, static_cast(twopi)); } } else { // Update any bins that occur below the lowest peak's region of influence - for (size_t xxk = 0; xxk < peakFinder->peaks[0].leftValley; xxk++) { - outFrame->bin[xxk].mag = frame->bin[xxk].mag; + for (size_t k = 0; k < peakFinder->peaks[0].leftValley; k++) { + outFrame->bin[k].mag = frame->bin[k].mag; // Compute the instantaneous frequency - double omegaK = twopi * (xxk+1) / fftSize; - double phaseInc = frame->bin[xxk].phase - framePrev->bin[xxk].phase - hopSize * omegaK; + double omegaK = twopi * (k+1) / fftSize; + double phaseInc = frame->bin[k].phase - framePrev->bin[k].phase - hopSize * omegaK; phaseInc = std::fmod(phaseInc + pi, twopi) - pi; double instantaneousFreq = omegaK + phaseInc/hopSize; // Compute the phase - outFrame->bin[xxk].phase = sc_wrap( - outFramePrev->bin[xxk].phase + static_cast(hopSize * instantaneousFreq), + outFrame->bin[k].phase = sc_wrap( + outFramePrev->bin[k].phase + static_cast(hopSize * instantaneousFreq), 0.f, static_cast(twopi)); } // Update any bins that occur above the highest peak's region of influence - for (size_t xxk = peakFinder->peaks[peakFinder->size()-1].rightValley + 1; xxk < fftSize / 2 - 1; xxk++) { - outFrame->bin[xxk].mag = frame->bin[xxk].mag; + for (size_t k = peakFinder->peaks[peakFinder->size()-1].rightValley + 1; k < fftSize / 2 - 1; k++) { + outFrame->bin[k].mag = frame->bin[k].mag; // Compute the instantaneous frequency - double omegaK = twopi * (xxk+1) / fftSize; - double phaseInc = frame->bin[xxk].phase - framePrev->bin[xxk].phase - hopSize * omegaK; + double omegaK = twopi * (k+1) / fftSize; + double phaseInc = frame->bin[k].phase - framePrev->bin[k].phase - hopSize * omegaK; phaseInc = std::fmod(phaseInc + pi, twopi) - pi; double instantaneousFreq = omegaK + phaseInc/hopSize; // Compute the phase - outFrame->bin[xxk].phase = sc_wrap( - outFramePrev->bin[xxk].phase + static_cast(hopSize * instantaneousFreq), + outFrame->bin[k].phase = sc_wrap( + outFramePrev->bin[k].phase + static_cast(hopSize * instantaneousFreq), 0.f, static_cast(twopi)); } // Update all other peaks - for (size_t xxn = 0; xxn < peakFinder->size(); xxn++) { + for (size_t n = 0; n < peakFinder->size(); n++) { // First we compute the new phase for the peak bin as usual - Peak peak = peakFinder->peaks[xxn]; + Peak peak = peakFinder->peaks[n]; outFrame->bin[peak.peak].mag = frame->bin[peak.peak].mag; // Compute the instantaneous frequency @@ -522,17 +522,17 @@ void FlexPlugins::PV_PlayBufStretch::Stretch2LarocheDolson( static_cast(twopi)); // Then we update the phases of all other peaks - for (size_t xxo = peak.leftValley; xxo < peak.peak; xxo++) { - outFrame->bin[xxo].mag = frame->bin[xxo].mag; - outFrame->bin[xxo].phase = sc_wrap( - outFrame->bin[peak.peak].phase + frame->bin[xxo].phase - frame->bin[peak.peak].phase, + for (size_t o = peak.leftValley; o < peak.peak; o++) { + outFrame->bin[o].mag = frame->bin[o].mag; + outFrame->bin[o].phase = sc_wrap( + outFrame->bin[peak.peak].phase + frame->bin[o].phase - frame->bin[peak.peak].phase, 0.f, static_cast(twopi)); } - for (size_t xxo = peak.peak + 1; xxo <= peak.rightValley; xxo++) { - outFrame->bin[xxo].mag = frame->bin[xxo].mag; - outFrame->bin[xxo].phase = sc_wrap( - outFrame->bin[peak.peak].phase + frame->bin[xxo].phase - frame->bin[peak.peak].phase, + for (size_t o = peak.peak + 1; o <= peak.rightValley; o++) { + outFrame->bin[o].mag = frame->bin[o].mag; + outFrame->bin[o].phase = sc_wrap( + outFrame->bin[peak.peak].phase + frame->bin[o].phase - frame->bin[peak.peak].phase, 0.f, static_cast(twopi)); } @@ -564,18 +564,18 @@ void FlexPlugins::PV_PlayBufStretch::Stretch3( size_t hopSize) { outFrame->dc = INTERP(framePrev1->dc, frameNext->dc, pos); outFrame->nyq = INTERP(framePrev1->nyq, frameNext->nyq, pos); - for (size_t xxk = 0; xxk < fftSize/2-1; xxk++) { - outFrame->bin[xxk].mag = INTERP(framePrev1->bin[xxk].mag, frameNext->bin[xxk].mag, pos); + for (size_t k = 0; k < fftSize/2-1; k++) { + outFrame->bin[k].mag = INTERP(framePrev1->bin[k].mag, frameNext->bin[k].mag, pos); - float omegaK = twopi * (xxk+1) / fftSize; + float omegaK = twopi * (k+1) / fftSize; // Compute the next instantaneous frequency - float phaseInc = frameNext->bin[xxk].phase - framePrev1->bin[xxk].phase - hopSize * omegaK; + float phaseInc = frameNext->bin[k].phase - framePrev1->bin[k].phase - hopSize * omegaK; phaseInc = std::fmod(phaseInc + pi, twopi) - pi; float instantaneousFreqNext = omegaK + phaseInc/hopSize; // Compute the previous instantaneous frequency - phaseInc = framePrev1->bin[xxk].phase - framePrev2->bin[xxk].phase - hopSize * omegaK; + phaseInc = framePrev1->bin[k].phase - framePrev2->bin[k].phase - hopSize * omegaK; phaseInc = std::fmod(phaseInc + pi, twopi) - pi; float instantaneousFreqPrev = omegaK + phaseInc/hopSize; @@ -583,8 +583,8 @@ void FlexPlugins::PV_PlayBufStretch::Stretch3( float instantaneousFreq = INTERP(instantaneousFreqPrev, instantaneousFreqNext, pos); // Compute the new phase - outFrame->bin[xxk].phase = sc_wrap( - outFramePrev->bin[xxk].phase + static_cast(hopSize * instantaneousFreq), + outFrame->bin[k].phase = sc_wrap( + outFramePrev->bin[k].phase + static_cast(hopSize * instantaneousFreq), 0.f, static_cast(twopi)); } @@ -616,18 +616,18 @@ void FlexPlugins::PV_PlayBufStretch::Stretch3Puckette( size_t hopSize) { outFrame->dc = INTERP(framePrev1->dc, frameNext->dc, pos); outFrame->nyq = INTERP(framePrev1->nyq, frameNext->nyq, pos); - for (size_t xxk = 0; xxk < fftSize/2-1; xxk++) { - outFrame->bin[xxk].mag = INTERP(framePrev1->bin[xxk].mag, frameNext->bin[xxk].mag, pos); + for (size_t k = 0; k < fftSize/2-1; k++) { + outFrame->bin[k].mag = INTERP(framePrev1->bin[k].mag, frameNext->bin[k].mag, pos); - float omegaK = twopi * (xxk+1) / fftSize; + float omegaK = twopi * (k+1) / fftSize; // Compute the next instantaneous frequency - float phaseInc = frameNext->bin[xxk].phase - framePrev1->bin[xxk].phase - hopSize * omegaK; + float phaseInc = frameNext->bin[k].phase - framePrev1->bin[k].phase - hopSize * omegaK; phaseInc = std::fmod(phaseInc + pi, twopi) - pi; float instantaneousFreqNext = omegaK + phaseInc/hopSize; // Compute the previous instantaneous frequency - phaseInc = framePrev1->bin[xxk].phase - framePrev2->bin[xxk].phase - hopSize * omegaK; + phaseInc = framePrev1->bin[k].phase - framePrev2->bin[k].phase - hopSize * omegaK; phaseInc = std::fmod(phaseInc + pi, twopi) - pi; float instantaneousFreqPrev = omegaK + phaseInc/hopSize; @@ -637,19 +637,19 @@ void FlexPlugins::PV_PlayBufStretch::Stretch3Puckette( // In Puckette-style phase locking, we make a substitution for the previous phase, // in order to "lock" phases of adjacent bins together. float prevPhase = 0.0; - if (xxk == 0) { - prevPhase = outFramePrev->bin[xxk].phase; - } else if (xxk == fftSize/2-2) { - prevPhase = outFramePrev->bin[xxk].phase; + if (k == 0) { + prevPhase = outFramePrev->bin[k].phase; + } else if (k == fftSize/2-2) { + prevPhase = outFramePrev->bin[k].phase; } else { - std::complex prevBinKMinus1 = std::polar(outFramePrev->bin[xxk-1].mag, outFramePrev->bin[xxk-1].phase); - std::complex prevBinK = std::polar(outFramePrev->bin[xxk].mag, outFramePrev->bin[xxk].phase); - std::complex prevBinKPlus1 = std::polar(outFramePrev->bin[xxk+1].mag, outFramePrev->bin[xxk+1].phase); + std::complex prevBinKMinus1 = std::polar(outFramePrev->bin[k-1].mag, outFramePrev->bin[k-1].phase); + std::complex prevBinK = std::polar(outFramePrev->bin[k].mag, outFramePrev->bin[k].phase); + std::complex prevBinKPlus1 = std::polar(outFramePrev->bin[k+1].mag, outFramePrev->bin[k+1].phase); prevPhase = std::arg(prevBinK - prevBinKMinus1 - prevBinKPlus1); } // Compute the new phase - outFrame->bin[xxk].phase = sc_wrap( + outFrame->bin[k].phase = sc_wrap( prevPhase + static_cast(hopSize * instantaneousFreq), 0.f, static_cast(twopi)); @@ -697,18 +697,18 @@ void FlexPlugins::PV_PlayBufStretch::Stretch3LarocheDolson( if (peakFinder->size() == 0) { // No phase locking if no peaks were acquired - for (size_t xxk = 0; xxk < fftSize/2-1; xxk++) { - outFrame->bin[xxk].mag = INTERP(framePrev1->bin[xxk].mag, frameNext->bin[xxk].mag, pos); + for (size_t k = 0; k < fftSize/2-1; k++) { + outFrame->bin[k].mag = INTERP(framePrev1->bin[k].mag, frameNext->bin[k].mag, pos); - double omegaK = twopi * (xxk+1) / fftSize; + double omegaK = twopi * (k+1) / fftSize; // Compute the next instantaneous frequency - double phaseInc = frameNext->bin[xxk].phase - framePrev1->bin[xxk].phase - hopSize * omegaK; + double phaseInc = frameNext->bin[k].phase - framePrev1->bin[k].phase - hopSize * omegaK; phaseInc = std::fmod(phaseInc + pi, twopi) - pi; double instantaneousFreqNext = omegaK + phaseInc/hopSize; // Compute the previous instantaneous frequency - phaseInc = framePrev1->bin[xxk].phase - framePrev2->bin[xxk].phase - hopSize * omegaK; + phaseInc = framePrev1->bin[k].phase - framePrev2->bin[k].phase - hopSize * omegaK; phaseInc = std::fmod(phaseInc + pi, twopi) - pi; double instantaneousFreqPrev = omegaK + phaseInc/hopSize; @@ -716,25 +716,25 @@ void FlexPlugins::PV_PlayBufStretch::Stretch3LarocheDolson( double instantaneousFreq = INTERP(instantaneousFreqPrev, instantaneousFreqNext, pos); // Compute the new phase - outFrame->bin[xxk].phase = sc_wrap( - outFramePrev->bin[xxk].phase + static_cast(hopSize * instantaneousFreq), + outFrame->bin[k].phase = sc_wrap( + outFramePrev->bin[k].phase + static_cast(hopSize * instantaneousFreq), 0.f, static_cast(twopi)); } } else { // Update any bins that occur below the lowest peak's region of influence - for (size_t xxk = 0; xxk < peakFinder->peaks[0].leftValley; xxk++) { - outFrame->bin[xxk].mag = INTERP(framePrev1->bin[xxk].mag, frameNext->bin[xxk].mag, pos); + for (size_t k = 0; k < peakFinder->peaks[0].leftValley; k++) { + outFrame->bin[k].mag = INTERP(framePrev1->bin[k].mag, frameNext->bin[k].mag, pos); // Compute the instantaneous frequency - double omegaK = twopi * (xxk+1) / fftSize; + double omegaK = twopi * (k+1) / fftSize; - double phaseInc = frameNext->bin[xxk].phase - framePrev1->bin[xxk].phase - hopSize * omegaK; + double phaseInc = frameNext->bin[k].phase - framePrev1->bin[k].phase - hopSize * omegaK; phaseInc = std::fmod(phaseInc + pi, twopi) - pi; double instantaneousFreqNext = omegaK + phaseInc/hopSize; // Compute the previous instantaneous frequency - phaseInc = framePrev1->bin[xxk].phase - framePrev2->bin[xxk].phase - hopSize * omegaK; + phaseInc = framePrev1->bin[k].phase - framePrev2->bin[k].phase - hopSize * omegaK; phaseInc = std::fmod(phaseInc + pi, twopi) - pi; double instantaneousFreqPrev = omegaK + phaseInc/hopSize; @@ -742,25 +742,25 @@ void FlexPlugins::PV_PlayBufStretch::Stretch3LarocheDolson( double instantaneousFreq = INTERP(instantaneousFreqPrev, instantaneousFreqNext, pos); // Compute the phase - outFrame->bin[xxk].phase = sc_wrap( - outFramePrev->bin[xxk].phase + static_cast(hopSize * instantaneousFreq), + outFrame->bin[k].phase = sc_wrap( + outFramePrev->bin[k].phase + static_cast(hopSize * instantaneousFreq), 0.f, static_cast(twopi)); } // Update any bins that occur above the highest peak's region of influence - for (size_t xxk = peakFinder->peaks[peakFinder->size()-1].rightValley + 1; xxk < fftSize / 2 - 1; xxk++) { - outFrame->bin[xxk].mag = INTERP(framePrev1->bin[xxk].mag, frameNext->bin[xxk].mag, pos); + for (size_t k = peakFinder->peaks[peakFinder->size()-1].rightValley + 1; k < fftSize / 2 - 1; k++) { + outFrame->bin[k].mag = INTERP(framePrev1->bin[k].mag, frameNext->bin[k].mag, pos); // Compute the instantaneous frequency - double omegaK = twopi * (xxk+1) / fftSize; + double omegaK = twopi * (k+1) / fftSize; - double phaseInc = frameNext->bin[xxk].phase - framePrev1->bin[xxk].phase - hopSize * omegaK; + double phaseInc = frameNext->bin[k].phase - framePrev1->bin[k].phase - hopSize * omegaK; phaseInc = std::fmod(phaseInc + pi, twopi) - pi; double instantaneousFreqNext = omegaK + phaseInc/hopSize; // Compute the previous instantaneous frequency - phaseInc = framePrev1->bin[xxk].phase - framePrev2->bin[xxk].phase - hopSize * omegaK; + phaseInc = framePrev1->bin[k].phase - framePrev2->bin[k].phase - hopSize * omegaK; phaseInc = std::fmod(phaseInc + pi, twopi) - pi; double instantaneousFreqPrev = omegaK + phaseInc/hopSize; @@ -768,15 +768,15 @@ void FlexPlugins::PV_PlayBufStretch::Stretch3LarocheDolson( double instantaneousFreq = INTERP(instantaneousFreqPrev, instantaneousFreqNext, pos); // Compute the phase - outFrame->bin[xxk].phase = sc_wrap( - outFramePrev->bin[xxk].phase + static_cast(hopSize * instantaneousFreq), + outFrame->bin[k].phase = sc_wrap( + outFramePrev->bin[k].phase + static_cast(hopSize * instantaneousFreq), 0.f, static_cast(twopi)); } - for (size_t xxn = 0; xxn < peakFinder->size(); xxn++) { + for (size_t n = 0; n < peakFinder->size(); n++) { // First we compute the new phase for the peak bin as usual - Peak peak = peakFinder->peaks[xxn]; + Peak peak = peakFinder->peaks[n]; outFrame->bin[peak.peak].mag = INTERP(framePrev1->bin[peak.peak].mag, frameNext->bin[peak.peak].mag, pos); // Compute the instantaneous frequency @@ -801,17 +801,17 @@ void FlexPlugins::PV_PlayBufStretch::Stretch3LarocheDolson( static_cast(twopi)); // Then we update all bins in the region of influence - for (size_t xxo = peak.leftValley; xxo < peak.peak; xxo++) { - outFrame->bin[xxo].mag = INTERP(framePrev1->bin[xxo].mag, frameNext->bin[xxo].mag, pos); - outFrame->bin[xxo].phase = sc_wrap( - outFrame->bin[peak.peak].phase + cframe->bin[xxo].phase - cframe->bin[peak.peak].phase, + for (size_t o = peak.leftValley; o < peak.peak; o++) { + outFrame->bin[o].mag = INTERP(framePrev1->bin[o].mag, frameNext->bin[o].mag, pos); + outFrame->bin[o].phase = sc_wrap( + outFrame->bin[peak.peak].phase + cframe->bin[o].phase - cframe->bin[peak.peak].phase, 0.f, static_cast(twopi)); } - for (size_t xxo = peak.peak + 1; xxo <= peak.rightValley; xxo++) { - outFrame->bin[xxo].mag = INTERP(framePrev1->bin[xxo].mag, frameNext->bin[xxo].mag, pos); - outFrame->bin[xxo].phase = sc_wrap( - outFrame->bin[peak.peak].phase + cframe->bin[xxo].phase - cframe->bin[peak.peak].phase, + for (size_t o = peak.peak + 1; o <= peak.rightValley; o++) { + outFrame->bin[o].mag = INTERP(framePrev1->bin[o].mag, frameNext->bin[o].mag, pos); + outFrame->bin[o].phase = sc_wrap( + outFrame->bin[peak.peak].phase + cframe->bin[o].phase - cframe->bin[peak.peak].phase, 0.f, static_cast(twopi)); } @@ -827,11 +827,11 @@ void FlexPlugins::PV_PlayBufStretch::Stretch3LarocheDolson( void FlexPlugins::PV_PlayBufStretch::fillPolarBuf(const float *fftBuf, SCPolarBuf *polarBuf, size_t fftSize) { polarBuf->dc = fftBuf[0]; polarBuf->nyq = fftBuf[1]; - for (size_t xxn = 2, xxk = 0; xxn < fftSize; xxn+=2, xxk++) { + for (size_t n = 2, k = 0; n < fftSize; n+=2, k++) { // For some reason the phase is stored first, then the magnitude. // This prevents a direct cast to SCPolarBuf, unfortunately. - polarBuf->bin[xxk].phase = fftBuf[xxn]; - polarBuf->bin[xxk].mag = fftBuf[xxn+1]; + polarBuf->bin[k].phase = fftBuf[n]; + polarBuf->bin[k].mag = fftBuf[n+1]; } } @@ -843,9 +843,9 @@ void FlexPlugins::PV_PlayBufStretch::fillPolarBuf(const float *fftBuf, SCPolarBu void FlexPlugins::PV_PlayBufStretch::copyPolarBuf(const SCPolarBuf *sourceBuf, SCPolarBuf *destBuf, size_t numbins) { destBuf->dc = sourceBuf->dc; destBuf->nyq = sourceBuf->nyq; - for (size_t xxn = 0; xxn < numbins; xxn++) { - destBuf->bin[xxn].mag = sourceBuf->bin[xxn].mag; - destBuf->bin[xxn].phase = sourceBuf->bin[xxn].phase; + for (size_t n = 0; n < numbins; n++) { + destBuf->bin[n].mag = sourceBuf->bin[n].mag; + destBuf->bin[n].phase = sourceBuf->bin[n].phase; } } @@ -893,27 +893,27 @@ void FlexPlugins::PeakFinder::analyze(const SCPolarBuf *buf) { if (m_queueL && m_maxSize > m_radius * 2 + 1) { m_size = 0; // clear any existing data - size_t xxi = m_radius; - while (xxi < m_maxSize - m_radius) { + size_t i = m_radius; + while (i < m_maxSize - m_radius) { bool isMax = true; - for (size_t xxj = xxi - m_radius; xxj < xxi; xxj++) { - if (buf->bin[xxj].mag >= buf->bin[xxi].mag) { + for (size_t j = i - m_radius; j < i; j++) { + if (buf->bin[j].mag >= buf->bin[i].mag) { isMax = false; break; } } - for (size_t xxj = xxi + 1; xxj <= xxi + m_radius; xxj++) { - if (buf->bin[xxj].mag >= buf->bin[xxi].mag) { + for (size_t j = i + 1; j <= i + m_radius; j++) { + if (buf->bin[j].mag >= buf->bin[i].mag) { isMax = false; break; } } if (isMax) { - peaks[m_size] = Peak(xxi); + peaks[m_size] = Peak(i); m_size++; - xxi += m_radius + 1; + i += m_radius + 1; } else { - xxi++; + i++; } } @@ -922,21 +922,21 @@ void FlexPlugins::PeakFinder::analyze(const SCPolarBuf *buf) { if (m_size > 0) { float min = buf->bin[0].mag; size_t argmin = 0; - size_t xxk = 1; - for (; xxk < peaks[0].peak; xxk++) { - if (buf->bin[xxk].mag < min) { - min = buf->bin[xxk].mag; - argmin = xxk; + size_t k = 1; + for (; k < peaks[0].peak; k++) { + if (buf->bin[k].mag < min) { + min = buf->bin[k].mag; + argmin = k; } } peaks[0].leftValley = argmin; - xxk = peaks[m_size-1].peak + 1; - min = buf->bin[xxk].mag; - argmin = xxk; - for (xxk++; xxk < m_maxSize; xxk++) { - if (buf->bin[xxk].mag < min) { - min = buf->bin[xxk].mag; - argmin = xxk; + k = peaks[m_size-1].peak + 1; + min = buf->bin[k].mag; + argmin = k; + for (k++; k < m_maxSize; k++) { + if (buf->bin[k].mag < min) { + min = buf->bin[k].mag; + argmin = k; } } peaks[m_size-1].rightValley = argmin; @@ -944,18 +944,18 @@ void FlexPlugins::PeakFinder::analyze(const SCPolarBuf *buf) { // Find the remaining left and right valleys if (m_size > 1) { - for (size_t xxj = 0; xxj < m_size-1; xxj++) { - size_t xxk = peaks[xxj].peak + 1; - float min = buf->bin[xxk].mag; - size_t argmin = xxk; - for (xxk++; xxk < peaks[xxj+1].peak; xxk++) { - if (buf->bin[xxk].mag < min) { - min = buf->bin[xxk].mag; - argmin = xxk; + for (size_t j = 0; j < m_size-1; j++) { + size_t k = peaks[j].peak + 1; + float min = buf->bin[k].mag; + size_t argmin = k; + for (k++; k < peaks[j+1].peak; k++) { + if (buf->bin[k].mag < min) { + min = buf->bin[k].mag; + argmin = k; } } - peaks[xxj].rightValley = argmin - 1; - peaks[xxj+1].leftValley = argmin; + peaks[j].rightValley = argmin - 1; + peaks[j+1].leftValley = argmin; } } } diff --git a/src/rubberband/ringbuffer.hpp b/src/rubberband/ringbuffer.hpp index eefca00..ec5adb5 100644 --- a/src/rubberband/ringbuffer.hpp +++ b/src/rubberband/ringbuffer.hpp @@ -10,42 +10,44 @@ #pragma once #include -template -class TestRingBuffer; +namespace FlexPlugins { + template + class TestRingBuffer; -template -class RingBuffer { -public: - RingBuffer(); - /// Given an initialized buffer, associates it with the RingBuffer - void initialize(T* buffer, size_t size); - - /// Determines if the buffer is ready to read `length` samples - bool isReadReady(size_t length) const; + template + class RingBuffer { + public: + RingBuffer(); + /// Given an initialized buffer, associates it with the RingBuffer + void initialize(T* buffer, size_t size); + + /// Determines if the buffer is ready to read `length` samples + bool isReadReady(size_t length) const; - /// Reads a block of specified length - void readBlock(T* destination, size_t length); - - /// Retrieves the size - size_t size() const; + /// Reads a block of specified length + void readBlock(T* destination, size_t length); + + /// Retrieves the size + size_t size() const; - /// Writes a block of specified length - void writeBlock(const T* samples, size_t length); - - /// Zeros out the buffer - void zero(); - T* m_buffer; -private: - size_t m_size; // size of buffer overall - size_t m_inputPointer; // input write pointer - size_t m_outputPointer; // output read pointer - int m_newSamples; // number of new samples accumulated since last read - template - friend class TestRingBuffer; -}; + /// Writes a block of specified length + void writeBlock(const T* samples, size_t length); + + /// Zeros out the buffer + void zero(); + T* m_buffer; + private: + size_t m_size; // size of buffer overall + size_t m_inputPointer; // input write pointer + size_t m_outputPointer; // output read pointer + int m_newSamples; // number of new samples accumulated since last read + template + friend class TestRingBuffer; + }; +} template -RingBuffer::RingBuffer() { +FlexPlugins::RingBuffer::RingBuffer() { m_buffer = nullptr; m_size = 0; m_newSamples = 0; @@ -54,13 +56,13 @@ RingBuffer::RingBuffer() { } template -void RingBuffer::initialize(T* buffer, size_t size) { +void FlexPlugins::RingBuffer::initialize(T* buffer, size_t size) { m_buffer = buffer; m_size = size; } template -void RingBuffer::writeBlock(const T* samples, size_t length) { +void FlexPlugins::RingBuffer::writeBlock(const T* samples, size_t length) { if (m_buffer && m_size > 0) { size_t startPos = m_inputPointer; for (size_t i = 0; i < length; i++) { @@ -75,7 +77,7 @@ void RingBuffer::writeBlock(const T* samples, size_t length) { } template -void RingBuffer::readBlock(T* destination, size_t length) { +void FlexPlugins::RingBuffer::readBlock(T* destination, size_t length) { if (m_buffer && m_size > 0) { for (size_t i = 0; i < length; i++) { destination[i] = m_buffer[m_outputPointer]; @@ -90,7 +92,7 @@ void RingBuffer::readBlock(T* destination, size_t length) { } template -bool RingBuffer::isReadReady(size_t length) const { +bool FlexPlugins::RingBuffer::isReadReady(size_t length) const { if (m_newSamples >= length) { return true; } else { @@ -99,12 +101,12 @@ bool RingBuffer::isReadReady(size_t length) const { } template -size_t RingBuffer::size() const { +size_t FlexPlugins::RingBuffer::size() const { return m_size; } template -void RingBuffer::zero() { +void FlexPlugins::RingBuffer::zero() { for (size_t i = 0; i < m_size; i++) m_buffer[i] = 0; } \ No newline at end of file From 4dade646ccd9ac1cf5ce4b6be9f7b73d51774a94 Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Tue, 14 Jul 2026 10:47:29 -0500 Subject: [PATCH 2/2] updated help files --- src/filters/FIR.schelp | 2 +- src/generators/ImpulseJitter.schelp | 26 +++++++++++--------- src/rubberband/RubberBandPS.schelp | 1 - src/rubberband/RubberBandStretcher.schelp | 1 - src/rubberband/RubberBandStretcherBuf.schelp | 1 - 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/filters/FIR.schelp b/src/filters/FIR.schelp index 0d53dde..b794d16 100644 --- a/src/filters/FIR.schelp +++ b/src/filters/FIR.schelp @@ -19,7 +19,7 @@ to perform Savitsky-Golay filtering for control signal smoothing classmethods:: -method::ar +method::ar, kr argument::coefs A link::Classes/Ref:: to a list of FIR filter coefficients. The number of coefficients can change diff --git a/src/generators/ImpulseJitter.schelp b/src/generators/ImpulseJitter.schelp index e1ad49e..891b5d8 100644 --- a/src/generators/ImpulseJitter.schelp +++ b/src/generators/ImpulseJitter.schelp @@ -32,17 +32,19 @@ This value will be added to the output. Examples:: +Generating audio: code:: -( -SynthDef(\jitter, { - arg freq, jitterFrac, amp; - var sig; - sig = ImpulseJitter.ar(freq, 0.0, jitterFrac, amp); - sig = LPF.ar(sig, freq); - sig = LeakDC.ar(sig); - Out.ar(0, sig); -}).add; - -Synth(\jitter, [\freq, 440.0, \jitterFrac, 0.1, \amp, -12.dbamp]); -) +{ + var sig; + sig = ImpulseJitter.ar(440, 0, XLine.kr(1, 2, 5)-1); + Pan2.ar(sig); +}.play; +:: + +An increasingly noisy impulse train as a control signal: +code:: +{ + var sig; + sig = ImpulseJitter.kr(10, 0, XLine.kr(1, 2, 2) - 1); +}.plot(2); :: \ No newline at end of file diff --git a/src/rubberband/RubberBandPS.schelp b/src/rubberband/RubberBandPS.schelp index 037d5ff..82bc7db 100644 --- a/src/rubberband/RubberBandPS.schelp +++ b/src/rubberband/RubberBandPS.schelp @@ -21,7 +21,6 @@ For more information about the RubberBand library, visit link::https://breakfast note:: This UGen uses quite a bit of CPU resources, which could be a problem on a less powerful computer. -You should also consider increasing the strong::memSize:: option for the server from the default 128MB. :: classmethods:: diff --git a/src/rubberband/RubberBandStretcher.schelp b/src/rubberband/RubberBandStretcher.schelp index 5beb61e..91bf7fd 100644 --- a/src/rubberband/RubberBandStretcher.schelp +++ b/src/rubberband/RubberBandStretcher.schelp @@ -23,7 +23,6 @@ For more information about the RubberBand library, visit link::https://breakfast note:: This UGen uses quite a bit of CPU resources, which could be a problem on a less powerful computer. -You should also consider increasing the strong::memSize:: option for the server from the default 128MB. :: classmethods:: diff --git a/src/rubberband/RubberBandStretcherBuf.schelp b/src/rubberband/RubberBandStretcherBuf.schelp index d07350e..a04dd34 100644 --- a/src/rubberband/RubberBandStretcherBuf.schelp +++ b/src/rubberband/RubberBandStretcherBuf.schelp @@ -19,7 +19,6 @@ For more information about the RubberBand library, visit link::https://breakfast note:: This UGen uses quite a bit of CPU resources, which could be a problem on a less powerful computer. -You should also consider increasing the strong::memSize:: option for the server from the default 128MB. :: classmethods::