#include <loudness_compensation.hpp>
Public Types | |
| enum class | Import { prior , warm } |
| How import_bands() treats a saved estimate. More... | |
Public Member Functions | |
| void | prepare (double sample_rate, int channels, const LoudnessCompensationConfig &config, std::span< const double > prior={}) |
| Control thread; allocates. | |
| void | reset () noexcept |
| Cold start: back to the prior, and the frame grid restarts at the next sample. | |
| void | restart_grid () noexcept |
| A transport jump or host reset: the frame grid restarts at the next sample (so how the stream is chopped still cannot matter), and the estimate – what the material sounds like – is kept. | |
| void | begin_candidate () noexcept |
| A detector run began: collect the would-be new material's own energy average, so a confirmed change starts from what has been heard since, not from zero. | |
| void | cancel_candidate () noexcept |
| The run broke: that was a passing moment, not new material. | |
| void | restart () noexcept |
| The material changed. | |
| void | merge_with_previous () noexcept |
| The material went back to what it was before the last change: it alternates (a kick-only bar and a full one, a hit and its tail). | |
| void | settle_alternation () noexcept |
| The material came back to what the LAST restart went to, after something else in between that never confirmed a change (it alternates, and the slow estimate already holds both halves): stop chasing it – keep the estimate, average over alternating_time_constant_seconds, and remember both halves. | |
| bool | has_arrived () const noexcept |
| LegSpectra | arrived_shape_legs () const noexcept |
| bool | has_previous () const noexcept |
| bool | alternating () const noexcept |
| LegSpectra | previous_shape_legs () const noexcept |
| LegSpectra | alternate_shape_legs (int which) const noexcept |
| The two remembered halves of an alternation (0: the earlier, 1: the later). | |
| std::uint64_t | merges () const noexcept |
| bool | prepared_for (double sample_rate, int channels, const LoudnessCompensationConfig &config) const noexcept |
| The estimator has the geometry prepare() would give these arguments. | |
| std::int64_t | frames_since_restart () const noexcept |
| Audible frames averaged since the last restart (or cold start). | |
| void | wet_becomes_dry () noexcept |
| Freeze released: the wet leg becomes the live input again, whose spectrum the dry leg kept warm through the hold. | |
| void | legs_uncorrelated () noexcept |
| Freeze engaged: the wet leg is now a held past, unrelated to the live input from here on. | |
| template<typename OnFrame> | |
| void | push (const float *const *wet, const float *const *dry, int channels, int num_samples, OnFrame &&on_frame) noexcept |
| Feed planar samples of the wet leg and, when tracking it, the dry leg (nullptr: the dry leg is the wet one). | |
| template<typename OnFrame> | |
| void | push (const float *const *x, int channels, int num_samples, OnFrame &&on_frame) noexcept |
| void | push (const float *const *x, int channels, int num_samples) noexcept |
| void | export_bands (SpectrumBands &out) const noexcept |
| The slow estimate, band-compressed (allocation-free). | |
| void | import_bands (const SpectrumBands &in, Import how=Import::warm) noexcept |
| A band's energy is spread evenly over the bins whose centres fall in it (or given to the bin nearest its centre). | |
| bool | prepared () const noexcept |
| int | fft_size () const noexcept |
| int | hop () const noexcept |
| int | samples_to_next_frame () const noexcept |
| Samples until the next frame boundary of the grid. | |
| int | bins () const noexcept |
| double | bin_hz () const noexcept |
| double | sample_rate () const noexcept |
| double | hop_seconds () const noexcept |
| const LoudnessCompensationConfig & | config () const noexcept |
| std::uint64_t | observed_frames () const noexcept |
| std::uint64_t | gated_frames () const noexcept |
| Frames under the absolute gate (silence). | |
| std::uint64_t | level_drops () const noexcept |
| std::uint64_t | restarts () const noexcept |
| double | last_frame_lufs () const noexcept |
| K-weighted loudness of the last frame's wet leg (LUFS-equivalent). | |
| double | prior_weight () const noexcept |
| The prior's remaining share of the estimate (1 until material is heard). | |
| LegSpectra | prior_legs () const noexcept |
| The prior's per-bin share (sums to one), as legs. | |
| void | set_prior_fade_scale (double scale) noexcept |
| How fast the prior fades from the next frame on, as a multiple of prior_seconds' rate (1: as configured). | |
| bool | last_frame_audible () const noexcept |
| Whether the last frame was audible (above the absolute gate). | |
| std::span< const double > | spectrum () const noexcept |
| The slow (wet) estimate per bin, K-weighted, normalised to sum 1. | |
| LegSpectra | legs () const noexcept |
| All the legs' slow estimates on the wet leg's scale. | |
| LegSpectra | fast_legs () const noexcept |
| The same of the fast estimate. | |
| LegSpectra | fast_shape_legs () const noexcept |
| The change detector's pair: fast and slow averages of each audible frame's wet SHAPE (level-independent), as coherent single-leg spectra. | |
| LegSpectra | slow_shape_legs () const noexcept |
| std::int64_t | shape_frames () const noexcept |
| double | weight (double lo_hz, double hi_hz) const noexcept |
| The slow wet estimate's energy between two frequencies; the whole weighs 1. | |
| const SpectrumCdf & | cdf () const noexcept |
Static Public Member Functions | |
| static int | fft_size_for (double sample_rate, const LoudnessCompensationConfig &config) noexcept |
| The FFT size prepare() picks for a rate and config. | |
| static int | bins_for (double sample_rate, const LoudnessCompensationConfig &config) noexcept |
|
strong |
How import_bands() treats a saved estimate.
|
inlinenodiscardnoexcept |
The two remembered halves of an alternation (0: the earlier, 1: the later).
|
inlinenodiscardnoexcept |
|
inlinenodiscardnoexcept |
|
inlinenoexcept |
A detector run began: collect the would-be new material's own energy average, so a confirmed change starts from what has been heard since, not from zero.
The frames whose windows still reach back across the change (one window from the frame that began the run) are left out: energy-weighted, a little of a louder past would bias it for seconds.
|
inlinenodiscardnoexcept |
|
inlinenodiscardnoexcept |
|
inlinestaticnodiscardnoexcept |
|
inlinenoexcept |
The run broke: that was a passing moment, not new material.
|
inlinenodiscardnoexcept |
|
inlinenodiscardnoexcept |
|
inlinenoexcept |
The slow estimate, band-compressed (allocation-free).
|
inlinenodiscardnoexcept |
The same of the fast estimate.
|
inlinenodiscardnoexcept |
The change detector's pair: fast and slow averages of each audible frame's wet SHAPE (level-independent), as coherent single-leg spectra.
|
inlinenodiscardnoexcept |
|
inlinestaticnodiscardnoexcept |
The FFT size prepare() picks for a rate and config.
|
inlinenodiscardnoexcept |
Audible frames averaged since the last restart (or cold start).
|
inlinenodiscardnoexcept |
Frames under the absolute gate (silence).
|
inlinenodiscardnoexcept |
|
inlinenodiscardnoexcept |
|
inlinenodiscardnoexcept |
|
inlinenodiscardnoexcept |
|
inlinenoexcept |
A band's energy is spread evenly over the bins whose centres fall in it (or given to the bin nearest its centre).
The frame grid restarts.
|
inlinenodiscardnoexcept |
Whether the last frame was audible (above the absolute gate).
|
inlinenodiscardnoexcept |
K-weighted loudness of the last frame's wet leg (LUFS-equivalent).
|
inlinenodiscardnoexcept |
All the legs' slow estimates on the wet leg's scale.
|
inlinenoexcept |
Freeze engaged: the wet leg is now a held past, unrelated to the live input from here on.
|
inlinenodiscardnoexcept |
|
inlinenoexcept |
The material went back to what it was before the last change: it alternates (a kick-only bar and a full one, a hit and its tail).
The slow estimate becomes the mix of both and averages over alternating_time_constant_seconds from here, so the gain holds steady instead of chasing each half.
|
inlinenodiscardnoexcept |
|
inlinenodiscardnoexcept |
|
inline |
Control thread; allocates.
prior: per-bin power on this estimator's grid (see bins_for()), any scale; empty means flat. The estimate IS the prior until the first audible frame (both legs, coherent).
|
inlinenodiscardnoexcept |
|
inlinenodiscardnoexcept |
The estimator has the geometry prepare() would give these arguments.
|
inlinenodiscardnoexcept |
|
inlinenodiscardnoexcept |
The prior's per-bin share (sums to one), as legs.
|
inlinenodiscardnoexcept |
The prior's remaining share of the estimate (1 until material is heard).
|
inlinenoexcept |
Feed planar samples of the wet leg and, when tracking it, the dry leg (nullptr: the dry leg is the wet one).
on_frame(samples_consumed) runs after every frame the call completes (audible or gated), with the estimate already updated. Prepared channels beyond channels are fed silence for these samples, so a narrower call never re-reads older audio.
|
inlinenoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
Cold start: back to the prior, and the frame grid restarts at the next sample.
|
inlinenoexcept |
The material changed.
Both estimates start again from the candidate (the new material's energy average since the change was first seen), or empty when there is none, and go on as running means. Energy weighting is why: seeded from anything that still holds the old material, a louder past would outweigh quieter new material for seconds. The material before is remembered (merge_with_previous), and the detector's slow shape is re-armed on the new material.
|
inlinenoexcept |
A transport jump or host reset: the frame grid restarts at the next sample (so how the stream is chopped still cannot matter), and the estimate – what the material sounds like – is kept.
|
inlinenodiscardnoexcept |
|
inlinenodiscardnoexcept |
|
inlinenodiscardnoexcept |
Samples until the next frame boundary of the grid.
|
inlinenoexcept |
How fast the prior fades from the next frame on, as a multiple of prior_seconds' rate (1: as configured).
A caller that sees the material agree with a RESTORED prior slows the fade, so the few frames the material's own estimate starts from do not swing it.
|
inlinenoexcept |
The material came back to what the LAST restart went to, after something else in between that never confirmed a change (it alternates, and the slow estimate already holds both halves): stop chasing it – keep the estimate, average over alternating_time_constant_seconds, and remember both halves.
|
inlinenodiscardnoexcept |
|
inlinenodiscardnoexcept |
|
inlinenodiscardnoexcept |
The slow (wet) estimate per bin, K-weighted, normalised to sum 1.
|
inlinenodiscardnoexcept |
The slow wet estimate's energy between two frequencies; the whole weighs 1.
|
inlinenoexcept |
Freeze released: the wet leg becomes the live input again, whose spectrum the dry leg kept warm through the hold.
Both estimates take the FAST dry estimate (the live input as it is now, not as it was when the hold began), and the slow one goes on as a running mean.