Replies: 1 comment
|
This has been mostly implemented (with some changes) in #116 |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Spec: AudioProcessorBase / DomainProcessor / SpectralProcessor Hierarchy
Overview
The
AudioProcessorclass has been refactored into a three-tier hierarchy:SpectralBridgeis itself anAudioProcessor, so it plugs directly intoAudioGraphProcessorwithout any graph-level changes. No separate spectral graph module orSpectralGraphProcessorexists in this branch.Tier 1 — AudioProcessorBase
File:
modules/yup_audio_processors/processors/yup_AudioProcessorBase.hImplementation:
modules/yup_audio_processors/processors/yup_AudioProcessorBase.cppExtracted from the former monolithic
AudioProcessor. Owns all domain-agnostic infrastructure:Members
processorName,getName()parameters,parameterMap,parameterHostIDMap,addParameter(),getParameters(),getParameterByID(),getParameterByHostID(),getParameterIndexByHostID()sampleRate,samplesPerBlock,getSampleRate(),getSamplesPerBlock(),setPlaybackConfiguration()(virtual, non-pure)latencySamples(atomic),getLatencySamples(),setLatencySamples()(notifies listeners on change)processLock(CriticalSection),processIsSuspended(atomic),getProcessLock(),isSuspended(),suspendProcessing(),ScopedProcessSuspensionofflineProcessing(atomic),isOfflineProcessing(),setOfflineProcessing()Listener(withaudioProcessorChanged(AudioProcessorBase*, ChangeDetails&)),addListener(),removeListener(),updateHostDisplay()latencyChanged,tailChanged,parameterValuesChanged,parameterInfoChanged,nonParameterStateChanged; builder methodswithLatencyChanged(),withTailChanged(), etc.getTailSamples()(virtual, default 0)getCurrentPreset(),setCurrentPreset(),getNumPresets(),getPresetName(),setPresetName()(all pure virtual)supportsDataTreeState(),loadStateFromDataTree(),saveStateIntoDataTree(),loadStateFromMemory(),saveStateIntoMemory()(all virtual with default implementations)Tier 2 — DomainProcessor (header-only template)
File:
modules/yup_audio_processors/processors/yup_DomainProcessor.hDesign rationale
The first template parameter is a template template (
DomainProcessContext<FloatType>) so each domain can define its ownContext<float>/Context<double>pair. The second parameter is a plainclass(DomainSpecification) because specs are not typed on float precision — they describe static configuration (sample rate, block size, FFT size, etc.).Members
processBlock(DomainProcessContext<float>&)— pure virtual, single-precisionprocessBlock(DomainProcessContext<double>&)— virtual, default no-op;supportsDoublePrecisionProcessing(),setProcessingPrecision(),getProcessingPrecision(),isUsingDoublePrecision()processBlockBypassed(DomainProcessContext<float>&),processBlockBypassed(DomainProcessContext<double>&)— virtual, default no-opsprepareToPlay(const DomainSpecification&)— pure virtual;releaseResources()— pure virtualflush()— virtual, default no-opsinglePrecision,doublePrecisionTier 3 — AudioProcessor
File:
modules/yup_audio_processors/processors/yup_AudioProcessor.hImplementation:
modules/yup_audio_processors/processors/yup_AudioProcessor.cppAudioSpec (the
DomainSpecification)File:
modules/yup_audio_processors/processors/yup_AudioSpec.hAudioProcessContext (the
DomainProcessContext)File:
modules/yup_audio_processors/processors/yup_AudioProcessContext.hAudioProcessor additional members (beyond DomainProcessor)
busLayout(AudioBusLayout)getBusLayout(),getNumAudioInputs(),getNumAudioOutputs()acceptsMidi(),producesMidi()hasEditor()createEditor()nullptrby defaultgetNumVoices()setPlaybackConfiguration(sampleRate, samplesPerBlock)releaseResources(), thenAudioProcessorBase::setPlaybackConfiguration(), thenprepareToPlay(AudioSpec(...))Backward compatibility
Existing
AudioProcessorsubclasses that overrideprepareToPlay(float, int)must be updated toprepareToPlay(const AudioSpec&). TheprocessBlocksignature changed from(AudioBuffer<float>&, MidiBuffer&)to(AudioProcessContext<float>&). All existing code in the repo (examples, tests, graph) has been migrated.Tier 3 — SpectralProcessor
File:
modules/yup_audio_processors/spectral/yup_SpectralProcessor.hImplementation:
modules/yup_audio_processors/spectral/yup_SpectralProcessor.cppSpectralSpec (the
DomainSpecification)File:
modules/yup_audio_processors/spectral/yup_SpectralSpec.hSpectralProcessContext (the
DomainProcessContext)File:
modules/yup_audio_processors/spectral/yup_SpectralProcessContext.hSpectralProcessor additional members
busLayout(AudioBusLayout)getBusLayout(),getNumAudioInputs(),getNumAudioOutputs()acceptsMidi(),producesMidi()setPlaybackConfiguration(sampleRate, samplesPerBlock)releaseResources(), thenAudioProcessorBase::setPlaybackConfiguration(), thenprepareToPlay(SpectralSpec(...))Note:
SpectralProcessordoes NOT havehasEditor()orcreateEditor()— these are audio-domain concerns.SpectralBuffer
File:
modules/yup_audio_basics/buffers/yup_AudioSpectralBuffer.hDesign
AudioBuffer<Type>internally (2 × numBins samples per channel)[real, imag, real, imag, ...]matchingFFTProcessor::performRealFFTForwardoutput conventionKey API
SpectralBuffer(int numChannels, int numBins)setSize(numChannels, numBins, ...)clear()getNumChannels(),getNumBins()getWritePointer(channel)getWritePointer(channel, bin)getReadPointer(channel),getReadPointer(channel, bin)getBinRef(channel, bin)BinRef— lightweight mutable reference to a single bin's real/imagcopyFrom(destCh, src, srcCh)copyFrom(destCh, destStart, src, srcCh, srcStart, numBins)makeCopyOf(other, avoidReallocating)fill(channel, real, imag)fill(real, imag)hasBeenCleared(),setNotClear()operator==,operator!=BinRef (inner class)
real(),imag()set(real, imag)magnitude()std::sqrt(real² + imag²)phase()std::atan2(imag, real)operator std::complex<Type>()SpectralBridge — Audio-to-Spectral STFT Bridge
File:
modules/yup_audio_processors/spectral/yup_SpectralBridge.hImplementation:
modules/yup_audio_processors/spectral/yup_SpectralBridge.cppDesign
SpectralBridgeis anAudioProcessorthat internally performs:SpectralProcessContext<float>→ callspectralProcessor->processBlock()→ inverse FFT → COLA overlap-add into output ring bufferThis means
SpectralBridgeplugs intoAudioGraphProcessoras a regular audio node — no graph changes needed. It is NOT paired with a separate spectral graph.Key API
SpectralBridge(name, busLayout)allocateResources(), sets latencysetSpectralProcessor(shared_ptr<SpectralProcessor>)getSpectralProcessor()setFFTSize(int)getFFTSize()setOverlapFactor(int)getOverlapFactor()setWindowType(WindowType)getWindowType()setWindowParameter(float)getWindowParameter()Lifecycle
prepareToPlay(AudioSpec)AudioProcessorBase::setPlaybackConfiguration(), callsallocateResources()(sizes rings, FFT, windows), callsspectralProcessor->prepareToPlay(SpectralSpec(...))releaseResources()spectralProcessor->releaseResources()flush()spectralProcessor->flush()processBlock(AudioProcessContext<float>&)Internal: processAvailableFrames()
spectralParamsbuffernextFrameStart + fftSize ≤ samplesWritten:fft.performRealFFTForward()→ copy numBins×2 floats intospectralBufferspectralProcessoris set: buildSpectralProcessContext<float>{spectralBuffer, spectralParams}→spectralProcessor->processBlock()fft.performRealFFTInverse()→ overlap-add withcolaCompensationinto output ringnextFrameStartbyhopSizeCOLA compensation
Analysis/synthesis windows are computed via
WindowFunctions<float>::generate(). The COLA compensation array ensures perfect reconstruction for the given overlap factor:colaCompensation[n] = window[n] / sum(window[i]² for i in overlap positions at n).Thread safety
processBlockacquiresScopedTryLockonprocessLock; clears output if lock failssetFFTSize,setOverlapFactor,setWindowType,setWindowParameter) useScopedProcessSuspensionand callreconfigureIfPrepared()All reactions