Signalsmith Audio's DSP Library  1.6.0
Useful C++ classes/templates for audio effects
Nested Classes | Types | Static Attributes | Methods | Attributes
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). More...
 
 STFT (int channels, int windowSize, int interval, int historyLength=0, int zeroPadding=0)
 Parameters passed straight to .resize() More...
 
void resize (int nChannels, int windowSize, int interval, int historyLength=0, int zeroPadding=0)
 Sets the channel-count, FFT size and interval. More...
 
int windowSize () const
 
int fftSize () const
 
int interval () const
 
decltype(fft.window()) window () const
 Returns the (analysis and synthesis) window. More...
 
std::vector< Sample > partialSumWindow (bool includeLatestBlock=true) const
 Calculates the effective window for the partially-summed future output (relative to the most recent block) More...
 
void reset ()
 Resets everything - since we clear the output sum, it will take windowSize samples to get proper output. More...
 
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. More...
 
template<class AnalysisFn >
void ensureValid (AnalysisFn fn)
 The same as above, assuming index 0. More...
 
int nextInvalid () const
 Returns the next invalid index (a.k.a. the index of the next block) More...
 
template<class Data >
void analyse (Data &&data)
 Analyse a multi-channel input, for any type where data[channel][index] returns samples. More...
 
template<class Data >
void analyse (int c, Data &&data)
 
template<class Data >
void analyseRaw (Data &&data)
 Analyse without windowing or zero-rotation. More...
 
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) More...
 
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