Test Audio Quality#
Overview#
Objective electroacoustic measurement is essential to verify that audio processing components (such as equalizers, dynamic range compressors, sample rate converters, and beamformers) meet rigorous acoustic and mathematical specifications.
The Sound Open Firmware (SOF) repository includes an automated, objective audio quality test
suite located in tools/test/audio/. Built around the AES17 standard (the Audio
Engineering Society standard method for digital audio equipment measurement), this test
harness executes simulated audio pipelines via sof-testbench4 or xt-run, captures the
output PCM waveforms, and performs mathematical analysis of gain accuracy, frequency response,
dynamic range, and harmonic distortion.
AES17 Objective Measurement Metrics#
The audio quality harness evaluates the following core parameters:
Gain Accuracy (dB): Measures the difference between the nominal and measured output signal level at 997 Hz. Verifies that processing algorithms maintain linear scaling across all word lengths and internal bit depths.
Frequency Response (FR): Evaluates the magnitude transfer function across the audible audio band (20 Hz to 20 kHz or Nyquist frequency limit). The measured response is verified against theoretical tolerance masks (such as ±0.5 dB passband ripple).
Dynamic Range (DR): Measured in dB CCIR-RMS in accordance with AES17 Section 8. Evaluated by stimulating the component with a low-level -60 dBFS test tone, removing the fundamental frequency with a notch filter, and measuring residual noise floor with CCIR weighting.
Total Harmonic Distortion plus Noise (THD+N): Quantifies non-linear distortion artifacts and harmonic spurs across a swept sine signal. Calculated as the ratio of the RMS sum of all harmonic components plus noise to the RMS amplitude of the fundamental tone.
Chirp Spectrogram Analysis: A logarithmic frequency sweep (chirp) detects anti-aliasing filter leakage, digital clipping, asymmetric saturation, and non-linear phase distortion.
Prerequisites and Environment Setup#
The test harness runs under either GNU Octave (recommended for open-source workflows) or MathWorks MATLAB.
Install GNU Octave and Required Toolboxes#
On Ubuntu or Debian workstations:
sudo apt install octave octave-signal octave-control octave-io
On Fedora workstations:
sudo dnf install octave octave-signal octave-control octave-io
Directory Layout#
The test harness resides in tools/test/audio/:
process_test.m: Main test runner and AES17 metric calculator.comp_run.sh: Underlying shell wrapper invokingsof-testbench4orxt-run.test_utils/: Octave/MATLAB helper routines for signal generation and filtering.std_utils/: Standard mathematical and DSP utility routines.plots/: Exported PNG graphs and spectrograms.reports/: Exported CSV performance summaries.
Running Audio Quality Tests with process_test.m#
Function Invocation Syntax#
The primary test entry point is process_test():
[n_fail, n_pass, n_na] = process_test(comp, bits_in_list, bits_out_list, fs, fulltest, show_plots, xtrun)
Parameters:
comp: Component identifier string (e.g.'eqiir','eqfir','volume','drc','src','tdfb').bits_in_list: Array of input PCM bit depths to test (e.g.[16, 24, 32]or32).bits_out_list: Array of output PCM bit depths to test (e.g.[16, 24, 32]or32).fs: Sampling frequency in Hertz (default:48000).fulltest:0for rapid single-frequency smoke test;1for complete AES17 swept test.show_plots:0for headless/batch execution;1to display interactive plot windows;2to display plots and preserve temporary audio PCM files.xtrun: Optional simulator string (e.g.''for native host testbench, or'xt-run --turbo'for cycle-accurate Xtensa simulation).
Interactive Execution in GNU Octave#
To run interactively from the Octave prompt:
cd tools/test/audio
octave --gui
Inside Octave, execute a full evaluation for the IIR equalizer:
pkg load signal io;
process_test('eqiir', [16 24 32], [16 24 32], 48000, 1, 1);
Headless Execution via Command Line#
Run headlessly directly from bash:
cd tools/test/audio
octave -q --eval "pkg load signal io; [n_fail]=process_test('eqiir', 32, 32, 48000, 1, 0); exit(n_fail)"
Cycle-Accurate Xtensa Simulation Testing#
Run objective audio quality verification directly through the Cadence Xtensa simulator:
process_test('eqiir', 32, 32, 48000, 1, 0, 'xt-run --turbo');
Interpreting Test Results#
Upon completion, process_test.m outputs formatted CSV matrix tables detailing performance
across word lengths:
eqiir test result: Gain (dB)
in \ out, 16, 24, 32
16, -7.33, x, x
24, x, -7.33, x
32, x, x, -7.33
eqiir test result: Dynamic range (dB CCIR-RMS)
in \ out, 16, 24, 32
16, 82.43, x, x
24, x, 130.40, x
32, x, x, 149.12
eqiir test result: Worst-case THD+N vs. frequency
in \ out, 16, 24, 32
16, -54.93, x, x
24, x, -98.01, x
32, x, x, -99.55
eqiir test result: Fails chirp/gain/DR/THD+N/FR
in \ out, 16, 24, 32
16, 0/0/0/0/0, x, x
24, x, 0/0/0/0/0, x
32, x, x, 0/0/0/0/0
Number of passed tests = 15
Number of failed tests = 0
Number of non-applicable tests = 0
Number of skipped tests = 0
Understanding Pass/Fail Criteria#
Gain: Verified against expected filter insertion loss within ±0.1 dB.
Dynamic Range: Must exceed defined acoustic noise floor minimums (≥ 80 dB for 16-bit, ≥ 120 dB for 24-bit, ≥ 140 dB for 32-bit).
THD+N: Worst-case distortion across 20 Hz to 20 kHz must remain below component tolerance thresholds (e.g. < -90 dBFS for 24/32-bit processing).
Frequency Response: Measured curve must fit within upper and lower tolerance envelopes across all audible octaves.
Visualizing Plots and Spectrograms#
When show_plots is set to 1 or 2, the harness generates three diagnostic plots:
Figure 326 Figure 321: Test results for EQ IIR component: Chirp spectrogram (top), THD+N frequency sweep (bottom left), and measured frequency response against tolerance mask (bottom right).#
Logarithmic Chirp Spectrogram (Top): Displays time-frequency distribution of the swept sine stimulus. Harmonic spurs, aliasing ghosts, or truncation noise appear as spurious diagonal or horizontal artifacts.
THD+N vs. Frequency (Bottom Left): Plots total harmonic distortion plus noise from 20 Hz to Nyquist limit against the maximum allowable distortion ceiling.
Frequency Response vs. Theoretical Envelope (Bottom Right): Compares the actual simulated response against the mathematical filter response calculated from coefficients.
Inspecting Waveforms with Audio Editors#
When debugging audio dropouts, initial state clicks, or DC bias, inspect the exported RAW PCM
files using waveform visualizers such as mhWaveEdit or Audacity:
sudo apt install mhwaveedit
mhwaveedit out.raw
Figure 327 Figure 322: Inspecting processed audio waveforms and transient responses in mhWaveEdit.#
Extending Audio Quality Tests for New Components#
To integrate a new component (e.g. newcomp) into the objective audio test harness:
Configure Execution Wrapper: Add a component configuration block to
tools/test/audio/comp_run.shor create a dedicatednewcomp_run.shscript specifying the benchmark topology:COMP=newcomp DIRECTION=playback TESTBENCH=sof-testbench4
Register in process_test.m: Add the component identifier to the configuration handler in
process_test.m:switch t.comp case {'eqiir', 'eqfir', 'dcblock', 'volume', 'tdfb', 'newcomp'} % Standard linear processing components case 'newcomp' % Optional: inject runtime controls via amixer or sof-ctl test.extra_opts = '-s tools/ctl/ipc4/newcomp/default.sh'; end
Define Pass/Fail Thresholds: Specify customized tolerance masks and distortion ceilings:
test.g_db_tol = 0.05; % Stricter 0.05 dB gain tolerance test.thdnf_max = -92; % Minimum -92 dB THD+N test.dr_db_min = 125; % Minimum 125 dB Dynamic Range
Automated CI and Regression Testing#
The objective quality harness is fully scriptable for automated Continuous Integration (CI). A simple shell script executes regression sweeps and halts with non-zero exit codes if any audio quality regressions are detected:
#!/bin/bash
set -e
cd tools/test/audio
octave -q --eval "
pkg load signal io;
components = {'volume', 'eqiir', 'eqfir', 'dcblock'};
total_fail = 0;
for i = 1:length(components)
fprintf('Testing component: %s\n', components{i});
[n_fail] = process_test(components{i}, 32, 32, 48000, 1, 0);
total_fail = total_fail + n_fail;
end
exit(total_fail);
"
All generated diagnostic plots and CSV reports are written to tools/test/audio/plots/ and
tools/test/audio/reports/ for automated CI artifact archiving and trend analysis.