|
DSPark 1.8.0
Header-only C++20 DSP for real-time and offline audio
|
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 |
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.
|
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.
|
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.
|
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.
|
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.
| out | Receives the band configuration if and only if true is returned. |
Definition at line 1049 of file Equalizer.h.
| std::atomic<bool> dspark::Equalizer< T, MaxBands >::StagedBand::enabled { BandConfig{}.enabled } |
Definition at line 978 of file Equalizer.h.
| std::atomic<T> dspark::Equalizer< T, MaxBands >::StagedBand::frequency { BandConfig{}.frequency } |
Definition at line 973 of file Equalizer.h.
| std::atomic<T> dspark::Equalizer< T, MaxBands >::StagedBand::gain { BandConfig{}.gain } |
Definition at line 974 of file Equalizer.h.
|
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.
| std::atomic<T> dspark::Equalizer< T, MaxBands >::StagedBand::q { BandConfig{}.q } |
Definition at line 975 of file Equalizer.h.
| std::atomic<unsigned> dspark::Equalizer< T, MaxBands >::StagedBand::seq { 0 } |
Definition at line 979 of file Equalizer.h.
| std::atomic<int> dspark::Equalizer< T, MaxBands >::StagedBand::slope { BandConfig{}.slope } |
Definition at line 977 of file Equalizer.h.
| std::atomic<int> dspark::Equalizer< T, MaxBands >::StagedBand::type { static_cast<int>(BandConfig{}.type) } |
Definition at line 976 of file Equalizer.h.