Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
audio_spectrum.hpp File Reference

Buffer-level offline spectrum analyzers: magnitude/frequency response and harmonic distortion (THD / THD+N) over already-rendered audio buffers. More...

#include "audio_metrics.hpp"
#include <pulp/audio/buffer.hpp>
#include <cstdint>
#include <span>
#include <string>
#include <vector>

Go to the source code of this file.

Classes

struct  pulp::test::audio::ResponsePoint
 One point on a response curve: frequency and magnitude in dB. More...
struct  pulp::test::audio::ResponseCurve
 Magnitude/frequency-response curve. More...
struct  pulp::test::audio::ResponseOptions
 Options for the response analyzer. Lengths must be powers of two. More...
struct  pulp::test::audio::PhasePoint
 One point on a phase/group-delay curve. More...
struct  pulp::test::audio::PhaseCurve
 Phase / group-delay curve of a transfer function. More...
struct  pulp::test::audio::GroupDelayOptions
 Options for the phase/group-delay analyzer. Lengths must be powers of two. More...
struct  pulp::test::audio::Harmonic
 One harmonic's contribution to a distortion measurement. More...
struct  pulp::test::audio::ThdResult
 Total harmonic distortion result. More...
struct  pulp::test::audio::ThdOptions
 Options for the THD analyzer. More...
struct  pulp::test::audio::ToneFit
 Least-squares fit of a single sinusoid at a known frequency. More...
struct  pulp::test::audio::AliasComponent
 One expected component of the ideal series, and what was measured where it landed. More...
struct  pulp::test::audio::AliasOptions
 Options for measure_aliasing. More...
struct  pulp::test::audio::AliasReport
 Three-way classification of an oscillator's output into harmonic / alias / noise, with the worst in-band alias called out. More...
struct  pulp::test::audio::ModulationSidebandOptions
 Options for measure_modulation_sidebands. More...
struct  pulp::test::audio::ModulationSidebandReport
 Where a steady tone's energy went after a periodic modulation was applied to it. More...

Namespaces

namespace  pulp
namespace  pulp::test
namespace  pulp::test::audio

Enumerations

enum class  pulp::test::audio::Window {
  pulp::test::audio::rectangular , pulp::test::audio::hann , pulp::test::audio::hamming , pulp::test::audio::blackman ,
  pulp::test::audio::flat_top , pulp::test::audio::kaiser
}
 Analysis window. More...
enum class  pulp::test::audio::ComponentClass { pulp::test::audio::harmonic , pulp::test::audio::alias }
 How measure_aliasing classified one expected component of the ideal harmonic series h · f0. More...

Functions

std::string pulp::test::audio::window_name (Window w)
 Human-readable window name for artifacts/messages ("rectangular", "hann", "hamming", "blackman", "flat_top", "kaiser").
double pulp::test::audio::window_coherent_gain (Window w, int n, double kaiser_beta=kDefaultKaiserBeta)
 Coherent gain (a.k.a.
std::vector< double > pulp::test::audio::window_coefficients (Window w, int n, double kaiser_beta=kDefaultKaiserBeta)
 Coefficients of w for an n-sample analysis segment.
ResponseCurve pulp::test::audio::response_relative_to_input (const pulp::audio::BufferView< const float > &input, const pulp::audio::BufferView< const float > &output, double sample_rate, std::span< const double > checkpoints_hz, const ResponseOptions &options={})
 Compute the magnitude response of output relative to input, one channel.
ResponseCurve pulp::test::audio::magnitude_spectrum_curve (const pulp::audio::BufferView< const float > &signal, double sample_rate, std::span< const double > checkpoints_hz, const ResponseOptions &options={})
 The signal's OWN magnitude spectrum, one channel, in dB relative to its own peak bin (loudest frequency = 0 dB, everything else negative).
PhaseCurve pulp::test::audio::measure_group_delay (const pulp::audio::BufferView< const float > &input, const pulp::audio::BufferView< const float > &output, double sample_rate, std::span< const double > checkpoints_hz, const GroupDelayOptions &options={})
 Phase and group delay of output relative to input, one channel.
ThdResult pulp::test::audio::measure_thd (const pulp::audio::BufferView< const float > &signal, double fundamental_hz, double sample_rate, const ThdOptions &options={})
 Measure THD / THD+N of signal (one channel of an already-rendered buffer) at fundamental_hz.
ToneFit pulp::test::audio::fit_tone (std::span< const double > samples, double cycles_per_sample)
 Fit one sinusoid at cycles_per_sample (frequency ÷ sample rate; 0.5 is Nyquist) to samples by least squares.
double pulp::test::audio::tone_residual_db (std::span< const double > samples, double cycles_per_sample)
 Energy of samples NOT explained by a tone at cycles_per_sample, in dB relative to the fitted tone's own energy: 10·log10(residual / fitted).
double pulp::test::audio::tone_gain_db (std::span< const double > samples, double cycles_per_sample, double input_amplitude)
 Gain of a tone at cycles_per_sample relative to input_amplitude, in dB: 20·log10(fitted_amplitude / input_amplitude).
double pulp::test::audio::fold_frequency (double frequency_hz, double sample_rate)
 Where a component at frequency_hz LANDS once sampled at sample_rate.
AliasReport pulp::test::audio::measure_aliasing (const pulp::audio::BufferView< const float > &signal, double fundamental_hz, double sample_rate, const AliasOptions &options={})
 Classify signal (one channel of an already-rendered buffer) against the ideal harmonic series of fundamental_hz, and report the worst in-band alias.
ModulationSidebandReport pulp::test::audio::measure_modulation_sidebands (const pulp::audio::BufferView< const float > &signal, double carrier_hz, double modulation_hz, double sample_rate, const ModulationSidebandOptions &options={})
 Classify signal (one channel of an already-rendered buffer) as a tone at carrier_hz modulated at modulation_hz, and report the energy at the modulation's own sidebands against the energy anywhere else in the band.

Variables

constexpr double pulp::test::audio::kDefaultKaiserBeta = 14.0
 Default Kaiser β.

Detailed Description

Buffer-level offline spectrum analyzers: magnitude/frequency response and harmonic distortion (THD / THD+N) over already-rendered audio buffers.

This is the FFT-bearing core of the offline "Audio Doctor". It takes plain BufferViews — buffers that have already left the audio thread, whether rendered through a RenderScenario (test side) or decoded from a WAV file (the pulp audio validate doctor CLI) — and returns the curve/THD data structures the artifact serializers (audio_doctor_artifacts) write to JSON.

Target-boundary rule: heavy FFT/analyzer code lives ONLY in test/tool targets. This lib (pulp-audio-analysis) depends on core/signal for the FFT and is never linked into a runtime/plugin build — a CMake target boundary makes a violation a link error. The scenario-driven entry points that drive a Processor stay in test/support/audio_doctor.hpp (test-only); they delegate to the buffer-level functions here so there is a single analysis implementation.

── Analyzer Determinism Contract (shared by both analyzers) ─────────────── Every spectral fact below is deterministic for identical input on a given platform. The per-analyzer specifics (window, length, stimulus, coherence) are stated on each entry point and echoed verbatim into the curve artifact. Both analyzers:

  • remove DC by subtracting the segment mean before windowing (the response analyzer skips this for an impulse stimulus — see below);
  • window a single contiguous analysis segment (no overlap-averaging);
  • report dB relative to a stated reference, so the FFT backend's absolute normalization (vDSP vs the radix-2 fallback differ by a constant scale) cancels — results are backend-stable to the tolerance the tests state;
  • take all lengths as powers of two (the radix-2 FFT requirement);
  • require the analysis segment to lie entirely within the supplied buffer(s) — throws std::invalid_argument otherwise. Zero-padding a short capture would window a truncated signal, whose edge leaks like any discontinuity and reads as distortion; shrink fft_length to fit the capture instead.

Test/tool layer only — analysis happens entirely off the audio thread.