DSPark 1.8.0
Header-only C++20 DSP for real-time and offline audio
Loading...
Searching...
No Matches
dspark::Equalizer< T, MaxBands >::StagedBand Struct Reference

One band's published configuration: a seqlock over atomic words. More...

#include <Equalizer.h>

Public Member Functions

void publish (const BandConfig &c) noexcept
 Publishes a whole band configuration (control thread only).
 
void loadWordsRelaxed (BandConfig &c) const noexcept
 Relaxed copy of the six published words (no ordering of its own; the caller's fences and counter checks provide that).
 
bool tryRead (BandConfig &out) const noexcept
 The bounded seqlock read for the AUDIO thread.
 
BandConfig read () const noexcept
 Reads the band into a caller-private plain copy (readouts).
 

Public Attributes

std::atomic< T > frequency { BandConfig{}.frequency }
 
std::atomic< T > gain { BandConfig{}.gain }
 
std::atomic< T > q { BandConfig{}.q }
 
std::atomic< int > type { static_cast<int>(BandConfig{}.type) }
 
std::atomic< int > slope { BandConfig{}.slope }
 
std::atomic< bool > enabled { BandConfig{}.enabled }
 
std::atomic< unsigned > seq { 0 }
 

Static Public Attributes

static constexpr int kSeqlockMaxAttempts = 3
 

Detailed Description

template<FloatType T, int MaxBands = 16>
struct dspark::Equalizer< T, MaxBands >::StagedBand

One band's published configuration: a seqlock over atomic words.

A BandConfig is a multi-word set with an invariant – half of one publication combined with half of the next is not a configuration the caller ever asked for – so it uses the canonical seqlock of docs/threading.md, matching Core/Biquad.h and Core/FIRFilter.h frame for frame. Every word crossing threads is std::atomic; nothing here is a plain scalar, and no reader ever holds a reference into this storage.

Definition at line 967 of file Equalizer.h.

Member Function Documentation

◆ loadWordsRelaxed()

template<FloatType T, int MaxBands = 16>
void dspark::Equalizer< T, MaxBands >::StagedBand::loadWordsRelaxed ( BandConfig &  c) const
inlinenoexcept

Relaxed copy of the six published words (no ordering of its own; the caller's fences and counter checks provide that).

Shared by both readers below so the word list exists exactly once: a field added to the band cannot be picked up by one reader and missed by the other.

Definition at line 1019 of file Equalizer.h.

◆ publish()

template<FloatType T, int MaxBands = 16>
void dspark::Equalizer< T, MaxBands >::StagedBand::publish ( const BandConfig &  c)
inlinenoexcept

Publishes a whole band configuration (control thread only).

An odd sequence number marks a write in progress; a reader that observes one retries. The release fence is mandatory, not decoration: an acquire/release counter alone does not order the relaxed word stores against it, so without the fence a reader could see a mid-publish word while the counter still looked even ([atomics.fences]/2; Boehm, "Can Seqlocks Get Along With Programming Language Memory Models?", MSPC 2012). Control-thread only, so the audio path pays nothing for it.

Definition at line 993 of file Equalizer.h.

◆ read()

template<FloatType T, int MaxBands = 16>
BandConfig dspark::Equalizer< T, MaxBands >::StagedBand::read ( ) const
inlinenoexcept

Reads the band into a caller-private plain copy (readouts).

Control- and GUI-thread entry point, and NOT for the audio thread: this retry is unbounded on purpose. A readout has no previously adopted copy to fall back on, so "sorry, try again" would be a worse API than a microsecond of spinning; the audio path uses tryRead().

The acquire fence keeps the word copy from sinking below the re-read of the counter: an acquire LOAD orders only later accesses, so without the fence a torn copy could pass the s0 == s1 check on a weakly ordered target or after compiler reordering. It also pairs with the writer's release fence, so a copy that read any mid-publish word cannot validate. The writer's critical section is six word stores, so this converges in at most a couple of iterations.

Definition at line 1083 of file Equalizer.h.

◆ tryRead()

template<FloatType T, int MaxBands = 16>
bool dspark::Equalizer< T, MaxBands >::StagedBand::tryRead ( BandConfig &  out) const
inlinenoexcept

The bounded seqlock read for the AUDIO thread.

Makes at most kSeqlockMaxAttempts validation attempts and then gives up rather than spin until the control thread finishes publishing – an audio-thread loop whose exit depends on another thread's progress is a stall of that thread's scheduling quantum, inside the callback. The accept test is the same one read() uses, so bounding can never make a torn set adoptable; it can only defer an adoption. out is written only once a copy has validated, so a caller may pass storage it is still using and a give-up leaves it intact.

An odd counter means the writer is mid-publish: skip the copy entirely instead of copying six words that would be discarded.

Parameters
outReceives the band configuration if and only if true is returned.
Returns
true if a consistent configuration was adopted; false if the attempt bound was reached (the caller keeps what it has and should re-arm its dirty flag to retry on a later call).

Definition at line 1049 of file Equalizer.h.

Member Data Documentation

◆ enabled

template<FloatType T, int MaxBands = 16>
std::atomic<bool> dspark::Equalizer< T, MaxBands >::StagedBand::enabled { BandConfig{}.enabled }

Definition at line 978 of file Equalizer.h.

◆ frequency

template<FloatType T, int MaxBands = 16>
std::atomic<T> dspark::Equalizer< T, MaxBands >::StagedBand::frequency { BandConfig{}.frequency }

Definition at line 973 of file Equalizer.h.

◆ gain

template<FloatType T, int MaxBands = 16>
std::atomic<T> dspark::Equalizer< T, MaxBands >::StagedBand::gain { BandConfig{}.gain }

Definition at line 974 of file Equalizer.h.

◆ kSeqlockMaxAttempts

template<FloatType T, int MaxBands = 16>
constexpr int dspark::Equalizer< T, MaxBands >::StagedBand::kSeqlockMaxAttempts = 3
staticconstexpr

Attempt bound for the audio-thread read (tryRead). Three is enough that attempt 1 succeeds whenever the reader does not collide with a publish, with two spare attempts to absorb one unlucky collision; past that, deferring the update by one call costs less than spending more of the block budget on it.

Definition at line 1011 of file Equalizer.h.

◆ q

template<FloatType T, int MaxBands = 16>
std::atomic<T> dspark::Equalizer< T, MaxBands >::StagedBand::q { BandConfig{}.q }

Definition at line 975 of file Equalizer.h.

◆ seq

template<FloatType T, int MaxBands = 16>
std::atomic<unsigned> dspark::Equalizer< T, MaxBands >::StagedBand::seq { 0 }

Definition at line 979 of file Equalizer.h.

◆ slope

template<FloatType T, int MaxBands = 16>
std::atomic<int> dspark::Equalizer< T, MaxBands >::StagedBand::slope { BandConfig{}.slope }

Definition at line 977 of file Equalizer.h.

◆ type

template<FloatType T, int MaxBands = 16>
std::atomic<int> dspark::Equalizer< T, MaxBands >::StagedBand::type { static_cast<int>(BandConfig{}.type) }

Definition at line 976 of file Equalizer.h.


The documentation for this struct was generated from the following file: