Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
spectral_mask_processor.hpp
Go to the documentation of this file.
1#pragma once
2
23
30
31#include <algorithm>
32#include <array>
33#include <cmath>
34#include <complex>
35#include <cstddef>
36#include <cstdint>
37#include <limits>
38#include <memory>
39#include <optional>
40#include <type_traits>
41#include <vector>
42
43namespace pulp::signal {
44
46 std::uint64_t{256} << 20u;
47
48template <typename SampleType>
50 static_assert(std::is_floating_point_v<SampleType>);
51
53 SampleType sample_rate = SampleType{48000};
54 SampleType initial_mix = SampleType{1};
57 std::uint64_t max_retained_bytes =
59};
60
65template <typename SampleType = float>
67public:
68 virtual ~SpectralPreMaskStageT() = default;
69 virtual void process_frames(std::complex<SampleType>* const* frames,
70 int channels, int num_bins) noexcept = 0;
71};
72
78template <typename SampleType = float>
80public:
81 virtual ~SpectralWetSourceStageT() = default;
82 virtual void process_block(const SampleType* const* input, SampleType* const* wet,
83 int channels, int num_samples) noexcept = 0;
84};
85
86template <typename SampleType = float>
88public:
89 static_assert(std::is_floating_point_v<SampleType>);
90
96
97 using EffectiveFrameObserver = void (*)(void*, const Table&, std::uint64_t) noexcept;
98
108 [[nodiscard]] bool set_effective_frame_observer(void* context,
109 EffectiveFrameObserver observer) noexcept {
110 if (!state_)
111 return false;
112 state_->observer_context = context;
113 state_->observer = observer;
114 return true;
115 }
116
120 [[nodiscard]] bool prepare(const Config& config) {
121 if (!std::isfinite(config.sample_rate)
122 || config.sample_rate <= SampleType{0}
123 || !std::isfinite(config.initial_mix)
124 || config.initial_mix < SampleType{0}
125 || config.initial_mix > SampleType{1}
126 || config.mix_ramp_samples < 0
127 || !valid_mix_curve_(config.mix_curve))
128 return false;
129
130 const auto frame_geometry =
132 if (!frame_geometry) return false;
133
134 const auto retained = checked_retained_bytes_(config, *frame_geometry);
135 if (!retained || *retained > config.max_retained_bytes) return false;
136
137 auto candidate = std::make_unique<PreparedState>();
138 candidate->config = config;
139 candidate->retained_bytes = *retained;
140 candidate->engine.prepare(config.frame);
141
142 Table identity{};
143 identity.fft_size = config.frame.fft_size;
144 identity.num_bins = frame_geometry->num_bins;
145 identity.sample_rate = config.sample_rate;
146 identity.effective_min_hz = SampleType{0};
147 identity.effective_max_hz = config.sample_rate * SampleType{0.5};
148 identity.active_bands = 1;
149 identity.band_edges_hz[0] = SampleType{0};
150 identity.band_edges_hz[1] = identity.effective_max_hz;
151 std::fill_n(identity.gain_linear.begin(), identity.num_bins,
152 SampleType{1});
153
154 candidate->frame_table = identity;
155 candidate->target_table = identity;
156 candidate->publication =
157 std::make_unique<runtime::TripleBuffer<PublishedTable>>(
158 PublishedTable{identity, 0});
159 candidate->mixer.set_mix(config.initial_mix);
160 candidate->mixer.set_curve(config.mix_curve);
161 candidate->mixer.set_ramp_samples(config.mix_ramp_samples);
162 candidate->mixer.set_wet_latency(candidate->engine.latency_samples());
163 candidate->mixer.prepare(config.frame.channels, config.frame.max_block);
164 const auto channel_count = static_cast<std::size_t>(config.frame.channels);
165 const auto block = static_cast<std::size_t>(config.frame.max_block);
166 candidate->wet_input.assign(channel_count * block, SampleType{});
167 candidate->wet_write.resize(channel_count);
168 candidate->wet_read.resize(channel_count);
169 for (std::size_t ch = 0; ch < channel_count; ++ch) {
170 candidate->wet_write[ch] = candidate->wet_input.data() + ch * block;
171 candidate->wet_read[ch] = candidate->wet_write[ch];
172 }
173
174 state_ = std::move(candidate);
175 next_generation_ = 1;
176 return true;
177 }
178
181 [[nodiscard]] bool publish_layout(const Layout& layout) {
182 if (!state_) return false;
183 Table table;
184 if (!build_spectral_mask(layout, state_->config.frame.fft_size,
185 state_->config.sample_rate, table))
186 return false;
187 return publish_table(table);
188 }
189
193 [[nodiscard]] bool publish_table(const Table& table) {
194 if (!state_ || !valid_table_(table)) return false;
195 auto generation = next_generation_++;
196 if (generation == 0) generation = next_generation_++;
197 state_->publication->write_with([&](PublishedTable& payload) {
198 payload.table = table;
199 payload.generation = generation;
200 });
201 state_->published_generation.write(generation);
202 return true;
203 }
204
210 [[nodiscard]] bool set_layout_rt(const Layout& layout) noexcept {
211 if (!state_ || !valid_layout_geometry_(layout)) return false;
212 state_->audio_layout = layout;
213 state_->audio_layout_pending = true;
214 return true;
215 }
216
220 [[nodiscard]] bool process(const SampleType* const* input,
221 SampleType* const* output,
222 int num_samples) noexcept {
223 if (!valid_audio_block_(input, output, num_samples)) {
224 zero_output_(output, num_samples);
225 return false;
226 }
227
228 state_->mixer.push_dry(input, state_->config.frame.channels, num_samples);
229 const SampleType* const* analysis_input = input;
230 if (wet_source_stage_ != nullptr) {
231 wet_source_stage_->process_block(input, state_->wet_write.data(),
232 state_->config.frame.channels, num_samples);
233 analysis_input = state_->wet_read.data();
234 }
235 bool frame_ok = true;
236 state_->engine.process(analysis_input, output, num_samples,
237 [this, &frame_ok](std::complex<SampleType>* const* frames,
238 int bins) noexcept {
239 if (pre_mask_stage_ != nullptr)
240 pre_mask_stage_->process_frames(
241 frames, state_->config.frame.channels, bins);
242 if (!process_frame(frames, bins)) frame_ok = false;
243 });
244 if (!frame_ok) {
245 zero_output_(output, num_samples);
246 return false;
247 }
248 state_->mixer.mix_wet(output, state_->config.frame.channels, num_samples);
249 return true;
250 }
251
256 [[nodiscard]] bool process_frame(std::complex<SampleType>* const* frames,
257 int num_bins) noexcept {
258 if (!state_ || num_bins != state_->frame_table.num_bins ||
259 state_->next_frame_ordinal == std::numeric_limits<std::uint64_t>::max()) {
260 zero_frames_(frames, num_bins);
261 return false;
262 }
263
264 adopt_latest_table_();
265 adopt_audio_layout_();
266 advance_transition_();
267 if (!apply_spectral_mask(frames, state_->config.frame.channels,
268 num_bins, state_->frame_table)) {
269 zero_frames_(frames, num_bins);
270 return false;
271 }
272 if (state_->observer)
273 state_->observer(state_->observer_context, state_->frame_table,
274 state_->next_frame_ordinal);
275 ++state_->next_frame_ordinal;
276 return true;
277 }
278
285 void set_pre_mask_stage(PreMaskStage* stage) noexcept { pre_mask_stage_ = stage; }
286 [[nodiscard]] PreMaskStage* pre_mask_stage() const noexcept { return pre_mask_stage_; }
287
291 void set_wet_source_stage(WetSourceStage* stage) noexcept { wet_source_stage_ = stage; }
292 [[nodiscard]] WetSourceStage* wet_source_stage() const noexcept {
293 return wet_source_stage_;
294 }
295
297 void set_mix(SampleType mix) noexcept {
298 if (state_ && std::isfinite(mix)) state_->mixer.set_mix(mix);
299 }
300 void set_mix_ramp_samples(int samples) noexcept {
301 if (state_) state_->mixer.set_ramp_samples(samples);
302 }
303 void set_mix_curve(MixCurve curve) noexcept {
304 if (state_ && valid_mix_curve_(curve)) state_->mixer.set_curve(curve);
305 }
306
309 void reset() noexcept {
310 if (!state_) return;
311 state_->next_frame_ordinal = 0;
312 state_->engine.reset();
313 state_->mixer.reset();
314 state_->frame_table = state_->target_table;
315 state_->transition_total = 0;
316 state_->transition_position = 0;
317 state_->target_table = state_->frame_table;
318 }
319
320 [[nodiscard]] bool prepared() const noexcept { return state_ != nullptr; }
321 [[nodiscard]] int latency_samples() const noexcept {
322 return state_ ? state_->engine.latency_samples() : 0;
323 }
324
325 [[nodiscard]] int maximum_tail_samples() const noexcept {
326 return state_ ? latency_samples() + state_->config.frame.fft_size : 0;
327 }
328 [[nodiscard]] int num_bins() const noexcept {
329 return state_ ? state_->frame_table.num_bins : 0;
330 }
331 [[nodiscard]] int channels() const noexcept {
332 return state_ ? state_->config.frame.channels : 0;
333 }
334 [[nodiscard]] std::uint64_t retained_bytes() const noexcept {
335 return state_ ? state_->retained_bytes : 0;
336 }
337 [[nodiscard]] std::uint64_t active_table_version() const noexcept {
338 return state_ ? state_->active_version.read() : 0;
339 }
340 [[nodiscard]] bool table_publication_pending() const noexcept {
341 return state_
342 && state_->published_generation.read()
343 != state_->active_generation.read();
344 }
345
346private:
347 struct PublishedTable {
348 Table table{};
349 std::uint64_t generation = 0;
350 };
351
352 struct PreparedState {
353 Config config{};
354 EffectiveFrameObserver observer = nullptr;
355 void* observer_context = nullptr;
356 std::uint64_t next_frame_ordinal = 0;
357 SpectralFrameEngineT<SampleType> engine;
358 DryWetMixerT<SampleType> mixer;
359 std::unique_ptr<runtime::TripleBuffer<PublishedTable>> publication;
360 Table frame_table{};
361 Table target_table{};
362 Layout audio_layout{};
363 Table audio_table{};
364 std::array<SampleType, kSpectralBandMaskMaximumBins> transition_start{};
365 runtime::SeqLock<std::uint64_t> published_generation;
366 runtime::SeqLock<std::uint64_t> active_generation;
367 runtime::SeqLock<std::uint64_t> active_version;
368 std::vector<SampleType> wet_input; // channels * max_block
369 std::vector<SampleType*> wet_write; // per-channel views
370 std::vector<const SampleType*> wet_read;
371 std::uint64_t consumed_generation = 0;
372 std::uint64_t retained_bytes = 0;
373 std::uint32_t transition_total = 0;
374 std::uint32_t transition_position = 0;
375 bool audio_layout_pending = false;
376 };
377
378 static std::optional<std::uint64_t> checked_retained_bytes_(
379 const Config& config,
380 const SpectralFrameEngineGeometry& frame) noexcept {
381 const auto channels = static_cast<std::uint64_t>(config.frame.channels);
382 const auto latency = static_cast<std::uint64_t>(config.frame.fft_size)
383 + static_cast<std::uint64_t>(config.frame.analysis_hop);
384 std::uint64_t samples_per_channel = 0;
385 std::uint64_t dry_samples = 0;
386 std::uint64_t wet_samples = 0;
387 if (!checked_capacity_sum(latency,
388 static_cast<std::uint64_t>(config.frame.max_block),
389 config.max_retained_bytes, samples_per_channel)
390 || !checked_capacity_product(samples_per_channel, channels,
391 config.max_retained_bytes, dry_samples)
392 || !checked_capacity_product(static_cast<std::uint64_t>(config.frame.max_block),
393 channels, config.max_retained_bytes, wet_samples))
394 return std::nullopt;
395
396 CheckedRetainedByteCharge charge(config.max_retained_bytes);
397 if (!charge.add_retained_bytes(frame.retained_bytes)
398 || !charge.add_retained_bytes(sizeof(PreparedState))
399 || !charge.add_retained_bytes(
400 sizeof(runtime::TripleBuffer<PublishedTable>))
401 || !charge.add<SampleType>(dry_samples)
402 || !charge.add<std::vector<SampleType>>(channels)
403 || !charge.add<SampleType>(wet_samples)
404 || !charge.add<SampleType*>(channels)
405 || !charge.add<const SampleType*>(channels)
406 || charge.total() > config.max_retained_bytes)
407 return std::nullopt;
408 return charge.total();
409 }
410
411 static bool valid_mix_curve_(MixCurve curve) noexcept {
412 switch (curve) {
413 case MixCurve::Linear:
416 case MixCurve::Sin3dB:
418 case MixCurve::Sin6dB:
421 return true;
422 }
423 return false;
424 }
425
426 bool valid_table_(const Table& table) const noexcept {
427 if (table.fft_size != state_->config.frame.fft_size
428 || table.num_bins != state_->frame_table.num_bins
429 || table.sample_rate != state_->config.sample_rate
430 || table.transition_frames
432 return false;
433 for (int bin = 0; bin < table.num_bins; ++bin) {
434 const auto gain = table.gain_linear[static_cast<std::size_t>(bin)];
435 if (!std::isfinite(gain) || gain < SampleType{0}) return false;
436 }
437 return true;
438 }
439
440 bool valid_layout_geometry_(const Layout& layout) const noexcept {
441 if (!std::isfinite(layout.min_hz) || !std::isfinite(layout.max_hz)
442 || layout.min_hz < SampleType{0}
443 || layout.max_hz <= layout.min_hz
444 || layout.active_bands == 0
445 || layout.active_bands > layout.bands.size())
446 return false;
447 for (std::size_t band = 0; band < layout.active_bands; ++band) {
448 if (!std::isfinite(layout.bands[band].gain_db)) return false;
449 }
450 return true;
451 }
452
453 bool valid_audio_block_(const SampleType* const* input,
454 SampleType* const* output,
455 int num_samples) const noexcept {
456 if (!state_ || !input || !output || num_samples <= 0
457 || num_samples > state_->config.frame.max_block)
458 return false;
459 for (int channel = 0; channel < state_->config.frame.channels; ++channel)
460 if (!input[channel] || !output[channel]) return false;
461 return true;
462 }
463
464 void adopt_latest_table_() noexcept {
465 const auto& publication = state_->publication->read();
466 if (publication.generation == state_->consumed_generation) return;
467
468 state_->consumed_generation = publication.generation;
469 state_->active_generation.write(publication.generation);
470 state_->active_version.write(publication.table.version);
471 std::copy_n(state_->frame_table.gain_linear.begin(),
472 state_->frame_table.num_bins,
473 state_->transition_start.begin());
474 state_->target_table = publication.table;
475 state_->transition_total = publication.table.transition_frames;
476 state_->transition_position = 0;
477 if (state_->transition_total == 0) state_->frame_table = publication.table;
478 }
479
480 void adopt_audio_layout_() noexcept {
481 if (!state_->audio_layout_pending) return;
482 state_->audio_layout_pending = false;
483 if (!build_spectral_mask(state_->audio_layout,
484 state_->config.frame.fft_size,
485 state_->config.sample_rate,
486 state_->audio_table)
487 || !valid_table_(state_->audio_table))
488 return;
489
490 std::copy_n(state_->frame_table.gain_linear.begin(),
491 state_->frame_table.num_bins,
492 state_->transition_start.begin());
493 state_->target_table = state_->audio_table;
494 state_->transition_total = state_->audio_table.transition_frames;
495 state_->transition_position = 0;
496 if (state_->transition_total == 0)
497 state_->frame_table = state_->audio_table;
498 }
499
500 void advance_transition_() noexcept {
501 if (state_->transition_position >= state_->transition_total) return;
502 ++state_->transition_position;
503 const auto mix = static_cast<SampleType>(state_->transition_position)
504 / static_cast<SampleType>(state_->transition_total);
505 state_->frame_table = state_->target_table;
506 for (int bin = 0; bin < state_->frame_table.num_bins; ++bin) {
507 const auto index = static_cast<std::size_t>(bin);
508 state_->frame_table.gain_linear[index] =
509 state_->transition_start[index]
510 + (state_->target_table.gain_linear[index]
511 - state_->transition_start[index]) * mix;
512 }
513 }
514
515 void zero_output_(SampleType* const* output, int num_samples) noexcept {
516 if (!state_ || !output || num_samples <= 0) return;
517 const auto samples = std::min(num_samples, state_->config.frame.max_block);
518 for (int channel = 0; channel < state_->config.frame.channels; ++channel)
519 if (output[channel]) std::fill_n(output[channel], samples, SampleType{});
520 }
521
522 void zero_frames_(std::complex<SampleType>* const* frames,
523 int num_bins) noexcept {
524 if (!state_ || !frames || num_bins <= 0) return;
525 const auto bins = std::min<std::size_t>(
526 static_cast<std::size_t>(num_bins), kSpectralBandMaskMaximumBins);
527 for (int channel = 0; channel < state_->config.frame.channels; ++channel)
528 if (frames[channel]) std::fill_n(frames[channel], bins,
529 std::complex<SampleType>{});
530 }
531
532 std::unique_ptr<PreparedState> state_;
533 PreMaskStage* pre_mask_stage_ = nullptr;
534 WetSourceStage* wet_source_stage_ = nullptr;
535 std::uint64_t next_generation_ = 1;
536};
537
546
547} // namespace pulp::signal
Definition spectral_mask_processor.hpp:87
int num_bins() const noexcept
Definition spectral_mask_processor.hpp:328
SpectralPreMaskStageT< SampleType > PreMaskStage
Definition spectral_mask_processor.hpp:94
void set_mix(SampleType mix) noexcept
Audio-thread-safe dry/wet controls. Mix defaults to fully wet.
Definition spectral_mask_processor.hpp:297
SpectralWetSourceStageT< SampleType > WetSourceStage
Definition spectral_mask_processor.hpp:95
WetSourceStage * wet_source_stage() const noexcept
Definition spectral_mask_processor.hpp:292
void set_pre_mask_stage(PreMaskStage *stage) noexcept
Install (or clear, with nullptr) the non-owning pre-mask frame stage.
Definition spectral_mask_processor.hpp:285
bool table_publication_pending() const noexcept
Definition spectral_mask_processor.hpp:340
bool publish_table(const Table &table)
Publish a complete immutable table through the shared latest-value three-slot handoff.
Definition spectral_mask_processor.hpp:193
int channels() const noexcept
Definition spectral_mask_processor.hpp:331
PreMaskStage * pre_mask_stage() const noexcept
Definition spectral_mask_processor.hpp:286
int maximum_tail_samples() const noexcept
Conservative flush bound for the fixed-latency WOLA response.
Definition spectral_mask_processor.hpp:325
void set_mix_curve(MixCurve curve) noexcept
Definition spectral_mask_processor.hpp:303
bool prepare(const Config &config)
Prepare a complete replacement state.
Definition spectral_mask_processor.hpp:120
bool process(const SampleType *const *input, SampleType *const *output, int num_samples) noexcept
Process one prepared planar block.
Definition spectral_mask_processor.hpp:220
std::uint64_t retained_bytes() const noexcept
Definition spectral_mask_processor.hpp:334
bool set_effective_frame_observer(void *context, EffectiveFrameObserver observer) noexcept
Install/remove only while the processing owner is stopped.
Definition spectral_mask_processor.hpp:108
SpectralMaskTableT< SampleType > Table
Definition spectral_mask_processor.hpp:93
bool prepared() const noexcept
Definition spectral_mask_processor.hpp:320
bool set_layout_rt(const Layout &layout) noexcept
Stage a layout from the single audio owner.
Definition spectral_mask_processor.hpp:210
int latency_samples() const noexcept
Definition spectral_mask_processor.hpp:321
void(*)(void *, const Table &, std::uint64_t) noexcept EffectiveFrameObserver
Definition spectral_mask_processor.hpp:97
SpectralBandLayoutT< SampleType > Layout
Definition spectral_mask_processor.hpp:92
void reset() noexcept
Clear analysis/synthesis, dry-delay, and in-flight interpolation state while preserving the latest ad...
Definition spectral_mask_processor.hpp:309
bool publish_layout(const Layout &layout)
Compile and publish one layout from the prepared geometry.
Definition spectral_mask_processor.hpp:181
void set_wet_source_stage(WetSourceStage *stage) noexcept
Install (or clear, with nullptr) the non-owning time-domain wet-source stage.
Definition spectral_mask_processor.hpp:291
void set_mix_ramp_samples(int samples) noexcept
Definition spectral_mask_processor.hpp:300
SpectralMaskProcessorConfigT< SampleType > Config
Definition spectral_mask_processor.hpp:91
bool process_frame(std::complex< SampleType > *const *frames, int num_bins) noexcept
Apply the current publication/transition state to one coherent complex frame group.
Definition spectral_mask_processor.hpp:256
std::uint64_t active_table_version() const noexcept
Definition spectral_mask_processor.hpp:337
A frame-domain stage that SpectralMaskProcessorT::process() runs on each analysis frame group before ...
Definition spectral_mask_processor.hpp:66
virtual void process_frames(std::complex< SampleType > *const *frames, int channels, int num_bins) noexcept=0
virtual ~SpectralPreMaskStageT()=default
A time-domain stage that SpectralMaskProcessorT::process() runs on each block before the wet path's a...
Definition spectral_mask_processor.hpp:79
virtual ~SpectralWetSourceStageT()=default
virtual void process_block(const SampleType *const *input, SampleType *const *wet, int channels, int num_samples) noexcept=0
@ config
Per-user settings root (an XDG distinction; equals app_data off Linux).
Definition special_dir.hpp:65
@ index
Definition gpu_pass.hpp:140
Definition additive_bank.hpp:218
SpectralPreMaskStageT< double > SpectralPreMaskStage64
Definition spectral_mask_processor.hpp:543
constexpr std::uint32_t kSpectralBandMaskMaximumTransitionFrames
Definition spectral_band_mask.hpp:30
std::optional< SpectralFrameEngineGeometry > checked_spectral_frame_engine_geometry(const SpectralFrameEngineConfig &config, std::uint64_t target_max_bytes=kTargetAddressMaximumBytes) noexcept
Derive every integer and typed backing-store capacity used by prepare().
Definition spectral_frame_engine.hpp:151
SpectralMaskProcessorT< float > SpectralMaskProcessor
Definition spectral_mask_processor.hpp:540
bool apply_spectral_mask(std::complex< SampleType > *const *frames, int channels, int num_bins, const SpectralMaskTableT< SampleType > &table) noexcept
Multiply one coherent positive-frequency frame group by a prepared mask.
Definition spectral_band_mask.hpp:387
SpectralMaskProcessorT< double > SpectralMaskProcessor64
Definition spectral_mask_processor.hpp:541
SpectralMaskProcessorConfigT< double > SpectralMaskProcessorConfig64
Definition spectral_mask_processor.hpp:539
constexpr std::size_t kSpectralBandMaskMaximumBins
Definition spectral_band_mask.hpp:28
SpectralPreMaskStageT< float > SpectralPreMaskStage
Definition spectral_mask_processor.hpp:542
bool checked_capacity_product(std::uint64_t lhs, std::uint64_t rhs, std::uint64_t maximum, std::uint64_t &product) noexcept
Definition checked_allocation.hpp:12
SpectralWetSourceStageT< double > SpectralWetSourceStage64
Definition spectral_mask_processor.hpp:545
bool checked_capacity_sum(std::uint64_t lhs, std::uint64_t rhs, std::uint64_t maximum, std::uint64_t &sum) noexcept
Definition checked_allocation.hpp:21
bool build_spectral_mask(const SpectralBandLayoutT< SampleType > &layout, int fft_size, SampleType sample_rate, SpectralMaskTableT< SampleType > &out_table) noexcept
Compile a complete positive-frequency gain table.
Definition spectral_band_mask.hpp:279
SpectralWetSourceStageT< float > SpectralWetSourceStage
Definition spectral_mask_processor.hpp:544
constexpr std::uint64_t kSpectralMaskProcessorDefaultMaximumRetainedBytes
Definition spectral_mask_processor.hpp:45
SpectralMaskProcessorConfigT< float > SpectralMaskProcessorConfig
Definition spectral_mask_processor.hpp:538
MixCurve
Crossfade curve between dry (mix=0) and wet (mix=1).
Definition dry_wet_mixer.hpp:29
@ Sqrt3dB
Definition dry_wet_mixer.hpp:36
@ Sqrt4_5dB
Definition dry_wet_mixer.hpp:37
@ Linear
Definition dry_wet_mixer.hpp:30
@ EqualPower
Definition dry_wet_mixer.hpp:31
@ Sin4_5dB
Definition dry_wet_mixer.hpp:34
@ Sin6dB
Definition dry_wet_mixer.hpp:35
@ Sin3dB
Definition dry_wet_mixer.hpp:33
@ Balanced
Definition dry_wet_mixer.hpp:32
Lock-free sequence-lock for coherent multi-field snapshots.
Fixed-capacity control-side compilation and frame-side application of a zoomable spectral band layout...
Streaming STFT analysis + weighted overlap-add (WOLA) synthesis for DSP-grade spectral processing — d...
Definition spectral_band_mask.hpp:55
Configuration for a SpectralFrameEngine.
Definition spectral_frame_engine.hpp:56
Definition spectral_mask_processor.hpp:49
float initial_mix
Definition spectral_mask_processor.hpp:54
int mix_ramp_samples
Definition spectral_mask_processor.hpp:55
MixCurve mix_curve
Definition spectral_mask_processor.hpp:56
SpectralFrameEngineConfig frame
Definition spectral_mask_processor.hpp:52
float sample_rate
Definition spectral_mask_processor.hpp:53
std::uint64_t max_retained_bytes
Definition spectral_mask_processor.hpp:57
Definition spectral_band_mask.hpp:104
std::array< SampleType, kSpectralBandMaskMaximumBins > gain_linear
Definition spectral_band_mask.hpp:116
SampleType sample_rate
Definition spectral_band_mask.hpp:109
std::array< SampleType, kSpectralBandMaskMaximumBands+1 > band_edges_hz
Definition spectral_band_mask.hpp:115
SampleType effective_max_hz
Definition spectral_band_mask.hpp:111
std::uint32_t active_bands
Definition spectral_band_mask.hpp:112
int num_bins
Definition spectral_band_mask.hpp:108
SampleType effective_min_hz
Definition spectral_band_mask.hpp:110
int fft_size
Definition spectral_band_mask.hpp:107
Lock-free latest-value publication between two threads.