Signalsmith Audio's DSP Library  1.6.1
Useful C++ classes/templates for audio effects
Loading...
Searching...
No Matches
signalsmith::spectral::STFT< Sample > Class Template Reference

#include <spectral.h>

Detailed Description

template<typename Sample>
class signalsmith::spectral::STFT< Sample >

STFT synthesis, built on a MultiBuffer.

Any window length and block interval is supported, but the FFT size may be rounded up to a faster size (by zero-padding). It uses a heuristically-optimal Kaiser window modified for perfect-reconstruction.

Simulated bad-case aliasing (random phase-shift for each band) for overlapping ratios

There is a "latest valid index", and you can read the output up to one historyLength behind this (see .resize()). You can read up to one window-length ahead to get partially-summed future output.

You move the valid index along using .ensureValid(), passing in a functor which provides spectra (using .analyse() and/or direct modification through .spectrum[c]):

void processSample(...) {
stft.ensureValid([&](int) {
// Here, we introduce (1 - windowSize) of latency
stft.analyse(inputBuffer.view(1 - windowSize))
});
// read as a MultiBuffer
auto result = stft.at(0);
++stft; // also moves the latest valid index
}
void processBlock(...) {
// assuming `historyLength` == blockSize
stft.ensureValid(blockSize, [&](int blockStartIndex) {
int inputStart = blockStartIndex + (1 - windowSize);
stft.analyse(inputBuffer.view(inputStart));
});
auto earliestValid = stft.at(0);
auto latestValid = stft.at(blockSize);
stft += blockSize;
}

The index passed to this functor will be greater than the previous valid index, and <= the index you pass in. Therefore, if you call .ensureValid() every sample, it can only ever be 0.

Inherits signalsmith::delay::MultiBuffer< Sample >.

Inherited by signalsmith::spectral::ProcessSTFT< Sample >.

Types

enum class  Window { kaiser , acg }
 
using Spectrum = MultiSpectrum
 
- Types inherited from signalsmith::delay::MultiBuffer< Sample >
using ConstChannel = typename Buffer<Sample>::ConstView
 
using MutableChannel = typename Buffer<Sample>::MutableView
 
using MutableView = View<false>
 
using ConstView = View<true>
 

Static Attributes

static constexpr Window kaiser = Window::kaiser
 
static constexpr Window acg = Window::acg
 

Methods

void setWindow (Window shape, bool rotateToZero=false)
 Swaps between the default (Kaiser) shape and Approximate Confined Gaussian (ACG).
 
 STFT (int channels, int windowSize, int interval, int historyLength=0, int zeroPadding=0)
 Parameters passed straight to .resize()
 
void resize (int nChannels, int windowSize, int interval, int historyLength=0, int zeroPadding=0)
 Sets the channel-count, FFT size and interval.
 
int windowSize () const
 
int fftSize () const
 
int interval () const
 
decltype(fft.window()) window () const
 Returns the (analysis and synthesis) window.
 
std::vector< Sample > partialSumWindow (bool includeLatestBlock=true) const
 Calculates the effective window for the partially-summed future output (relative to the most recent block)
 
void reset ()
 Resets everything - since we clear the output sum, it will take windowSize samples to get proper output.
 
template<class AnalysisFn>
void ensureValid (int i, AnalysisFn fn)
 Generates valid output up to the specified index (or 0), using the callback as many times as needed.
 
template<class AnalysisFn>
void ensureValid (AnalysisFn fn)
 The same as above, assuming index 0.
 
int nextInvalid () const
 Returns the next invalid index (a.k.a. the index of the next block)
 
template<class Data>
void analyse (Data &&data)
 Analyse a multi-channel input, for any type where data[channel][index] returns samples.
 
template<class Data>
void analyse (int c, Data &&data)
 
template<class Data>
void analyseRaw (Data &&data)
 Analyse without windowing or zero-rotation.
 
template<class Data>
void analyseRaw (int c, Data &&data)
 
int bands () const
 
int latency ()
 Internal latency (between the block-index requested in .ensureValid() and its position in the output)
 
STFToperator++ ()
 
STFToperator+= (int i)
 
STFToperator-- ()
 
STFToperator-= (int i)
 
Super::MutableView operator++ (int postIncrement)
 
Super::MutableView operator-- (int postIncrement)
 
- Methods inherited from signalsmith::delay::MultiBuffer< Sample >
 MultiBuffer (int channels=0, int capacity=0)
 
void resize (int nChannels, int capacity, Sample value=Sample())
 
void reset (Sample value=Sample())
 
Stride< false > at (int offset)
 
Stride< true > at (int offset) const
 
MutableView view (int offset=0)
 
ConstView view (int offset=0) const
 
ConstView constView (int offset=0) const
 
MutableChannel operator[] (int channel)
 
ConstChannel operator[] (int channel) const
 
MultiBufferoperator++ ()
 
MultiBufferoperator+= (int i)
 
MutableView operator++ (int)
 
MutableView operator+ (int i)
 
ConstView operator+ (int i) const
 
MultiBufferoperator-- ()
 
MultiBufferoperator-= (int i)
 
MutableView operator-- (int)
 
MutableView operator- (int i)
 
ConstView operator- (int i) const
 

Attributes

Window windowShape = Window::kaiser
 
Spectrum spectrum
 
WindowedFFT< Sample > fft
 

Constructor & Destructor Documentation

◆ STFT()

template<typename Sample>
signalsmith::spectral::STFT< Sample >::STFT ( int channels,
int windowSize,
int interval,
int historyLength = 0,
int zeroPadding = 0 )
inline

Parameters passed straight to .resize()

Method Details

◆ analyse()

template<typename Sample>
template<class Data>
void signalsmith::spectral::STFT< Sample >::analyse ( Data && data)
inline

Analyse a multi-channel input, for any type where data[channel][index] returns samples.

Results can be read/edited using .spectrum.

◆ analyseRaw()

template<typename Sample>
template<class Data>
void signalsmith::spectral::STFT< Sample >::analyseRaw ( Data && data)
inline

Analyse without windowing or zero-rotation.

◆ ensureValid() [1/2]

template<typename Sample>
template<class AnalysisFn>
void signalsmith::spectral::STFT< Sample >::ensureValid ( AnalysisFn fn)
inline

The same as above, assuming index 0.

◆ ensureValid() [2/2]

template<typename Sample>
template<class AnalysisFn>
void signalsmith::spectral::STFT< Sample >::ensureValid ( int i,
AnalysisFn fn )
inline

Generates valid output up to the specified index (or 0), using the callback as many times as needed.

The callback should be a functor accepting a single integer argument, which is the index for which a spectrum is required.

The block created from these spectra will start at this index in the output, plus .latency().

◆ latency()

template<typename Sample>
int signalsmith::spectral::STFT< Sample >::latency ( )
inline

Internal latency (between the block-index requested in .ensureValid() and its position in the output)

Currently unused, but it's in here to allow for a future implementation which spreads the FFT calculations out across each interval.

◆ nextInvalid()

template<typename Sample>
int signalsmith::spectral::STFT< Sample >::nextInvalid ( ) const
inline

Returns the next invalid index (a.k.a. the index of the next block)

◆ partialSumWindow()

template<typename Sample>
std::vector< Sample > signalsmith::spectral::STFT< Sample >::partialSumWindow ( bool includeLatestBlock = true) const
inline

Calculates the effective window for the partially-summed future output (relative to the most recent block)

◆ reset()

template<typename Sample>
void signalsmith::spectral::STFT< Sample >::reset ( )
inline

Resets everything - since we clear the output sum, it will take windowSize samples to get proper output.

◆ resize()

template<typename Sample>
void signalsmith::spectral::STFT< Sample >::resize ( int nChannels,
int windowSize,
int interval,
int historyLength = 0,
int zeroPadding = 0 )
inline

Sets the channel-count, FFT size and interval.

◆ setWindow()

template<typename Sample>
void signalsmith::spectral::STFT< Sample >::setWindow ( Window shape,
bool rotateToZero = false )
inline

Swaps between the default (Kaiser) shape and Approximate Confined Gaussian (ACG).

Default (Kaiser) windows and partial cumulative sum

The ACG has better rolloff since its edges go to 0:

ACG windows and partial cumulative sum

However, it generally has worse performance in terms of total sidelobe energy, affecting worst-case aliasing levels for (most) higher overlap ratios:

Simulated bad-case aliasing for ACG windows - compare with above

Roughly optimal Kaiser for STFT analysis (forced to perfect reconstruction)

◆ window()

template<typename Sample>
decltype(fft.window()) signalsmith::spectral::STFT< Sample >::window ( ) const
inline

Returns the (analysis and synthesis) window.

Member Data Documentation

◆ windowShape

template<typename Sample>
Window signalsmith::spectral::STFT< Sample >::windowShape = Window::kaiser
Deprecated
use .setWindow() which actually updates the window when you change it