SuperAPI AI Directory

matlab-evaluate-acoustic-metrics

Testing & quality

View repository

Updated 10-16 00:21

About this skill

Evaluate Acoustic Metrics

Select the correct Audio Toolbox function for acoustic measurement, apply proper calibration, and choose appropriate standards — instead of reimplementing algorithms or calling nonexistent functions.

When to Use

  • Computing psychoacoustic metrics (loudness, sharpness, roughness, fluctuation)
  • Evaluating speech quality or intelligibility (ViSQOL, STOI, STI, SII)
  • Assessing noise compliance (NC rating, RC rating, RNC, SIL)
  • Measuring SPL from recordings (offline, calibrated)
  • Computing broadcast loudness (EBU R 128 / LUFS)
  • Measuring reverberation time (RT60) from impulse responses
  • Detecting tonal content (tone-to-noise ratio, prominence ratio)
  • Converting microphone sensitivity to CalibrationFactor
  • User mentions PESQ or POLQA (redirect to correct alternatives)

When NOT to Use

  • Real-time streaming audio processing loops — use matlab-process-streaming-audio
  • Audio device I/O, recording, or live calibrateMicrophone — use matlab-play-record-audio
  • Audio plugin authoring (VST/AU) — use matlab-write-audio-plugin
  • General filter design unrelated to measurement — use matlab-design-digital-filter
  • Spatial audio / HRTF / SOFA processing

Workflow

Follow this decision tree to route the user's goal to the right function:

1. Identify the measurement goal

Goal Category Go to
"How loud is it?" (perceived) Psychoacoustic → Psychoacoustic Metrics
"Does it comply with broadcast loudness?" Broadcast → Broadcast Loudness
"Is the noise level acceptable for this space?" Noise compliance → Noise Compliance
"Can people understand speech in this room?" Speech intelligibility → STI/SII Measurement
"How good does processed speech sound?" Speech quality → Speech Quality
"What's the SPL?" Level measurement → SPL Measurement
"What's the reverberation time?" Room acoustics → RT60
"Is there a tonal component?" Tonality → Tonality

2. Ask before computing

Before writing code, confirm these with the user:

  • Calibration: Do they have a CalibrationFactor? Did they calibrate with calibrateMicrophone? (See Calibration section)
  • Standard: Which standard or method applies? (ISO 532-1 vs 532-2, NC vs RC vs RNC)
  • Sound field: Free field or diffuse field recording?
  • Application context: What space type? What's the concern? (Drives NC vs RC vs RNC selection)

3. Select function and apply

Use the Key Functions table and the category-specific patterns below.

Functions That Do NOT Exist

Function Status Use Instead
pesq Does NOT exist (proprietary ITU-T P.862 license) visqol(degraded, reference, fs, Mode="speech")
polqa Does NOT exist (proprietary ITU-T P.863 license) visqol(degraded, reference, fs)

Never suggest pesq or polqa. They are not in Audio Toolbox and cannot be added due to licensing.

Key Functions

Function Purpose Standard Available From
acousticLoudness Perceived loudness (sones) ISO 532-1/2 R2020a
acousticSharpness Perceived sharpness (acum) DIN 45692 R2020a
acousticRoughness Perceived roughness (asper) — R2021a
acousticFluctuation Fluctuation strength (vacil) — R2020b
integratedLoudness Broadcast loudness (LUFS) EBU R 128 / ITU-R BS.1770 R2016b
loudnessMeter Streaming broadcast loudness meter EBU R 128 / ITU-R BS.1770 R2018a
visqol Full-reference audio quality (MOS) — R2024a
stoi Short-time objective intelligibility — R2024a
mnru Modulated noise reference unit ITU-T P.810 R2024a
dBov Power level relative to overload (dB) ITU-T G.100.1 R2024a
speechTransmissionIndex STI from IR or test signals IEC 60268-16 R2026a
speechIntelligibilityIndex SII from IR, test signals, or levels ANSI/ASA S3.5-1997 R2026b
stipaExcitation Generate STI/STIPA test signals IEC 60268-16 R2026a
siiExcitation Generate SII test signals ANSI/ASA S3.5-1997 R2026b
noiseCriteria NC rating + SIL + spectrum imbalance + rattle risk ANSI/ASA S12.2 R2026b
roomNoiseCriteria RNC rating (detects surging/fluctuations) ANSI/ASA S12.2 R2026b
roomCriteria RC Mark II rating (HVAC systems) ANSI/ASA S12.2 R2026b
noiseCriteriaCurves Look up NC curve values ANSI/ASA S12.2 R2026b
noiseCriteriaRecommendations Recommended NC levels by room type ANSI/ASA S12.2 R2026b
speechInterferenceLevel SIL from recording ANSI/ASA S12.2 R2026b
rt60 Reverberation time from impulse response ISO 3382-1/2 R2025a
acousticToneToNoiseRatio Detect/quantify tones ECMA-418-1 R2023b
acousticProminenceRatio Tonality via critical band comparison ECMA-418-1 R2023b
splMeter SPL measurement (time-weighted, per-band) IEC 61672 R2018a
calibrateMicrophone Compute calibration factor from known tone — R2018b
weightingFilter A/C/Z/K frequency weighting IEC 61672-1 R2016b
octaveFilter Single octave-band filter ANSI S1.11-2004 R2017b
octaveFilterBank Multi-band octave filtering ANSI S1.11-2004 R2019a

See references/function-arguments.md for complete argument lists, defaults, and valid values for each function — consult when constructing calls with non-default parameters.

Calibration

CalibrationFactor from microphone sensitivity

Convert dB sensitivity (dBV/Pa) to the linear CalibrationFactor:

sensitivity_dBV = -26;  % Example: -26 dBV/Pa microphone
calibrationFactor = 1 / 10^(sensitivity_dBV/20);

The CalibrationFactor is the 3rd positional argument for acousticLoudness, acousticSharpness, acousticRoughness, and acousticFluctuation (default: sqrt(8)). For noise compliance functions, pass it as a name-value: CalibrationFactor=value.

System-level calibration advice

Always ask the user about their signal chain. If the recording path includes a preamp, audio interface, or ADC with unknown gain, the mic sensitivity spec alone is insufficient. Recommend:

  1. Play a 1 kHz tone through the system and record it through the full signal chain (mic → preamp → ADC → WAV). Use a pistonphone for a known 94 dB reference, or place a physical SPL meter next to the microphone to get an SPL reading.
  2. Compute the system calibration factor: calibrationFactor = calibrateMicrophone(calRecording, fs, SPLreading, FrequencyWeighting="Z-weighting")
  3. Pass that factor to the measurement function

calibrateMicrophone expects a 1 kHz tone recording as input. The SPLreading argument is the known SPL in dB (e.g., 94 from a pistonphone, or the value shown on a physical SPL meter).

Cross-reference: for the physical recording workflow with calibrateMicrophone, see the matlab-play-record-audio skill.

Patterns

Psychoacoustic Metrics

[audioIn, fs] = audioread("recording.wav");
calFactor = 1 / 10^(-26/20);

loudness = acousticLoudness(audioIn, fs, calFactor, ...
    Method="ISO 532-1", SoundField="diffuse", TimeVarying=true);

sharpness = acousticSharpness(audioIn, fs, calFactor, ...
    SoundField="diffuse", TimeVarying=true);

roughness = acousticRoughness(audioIn, fs, calFactor, SoundField="diffuse");

fluctuation = acousticFluctuation(audioIn, fs, calFactor, SoundField="diffuse");

Return types: acousticLoudness returns a scalar (sones) for stationary analysis. With TimeVarying=true, it returns a column vector (time-varying loudness in sones). The second output (specificLoudness) is a matrix (time x critical bands). It does NOT return a struct or timetable.

Ask the user:

  • ISO 532-1 (Zwicker, stationary/time-varying) or ISO 532-2 (Moore-Glasberg)?
  • Free-field or diffuse-field recording?
  • Time-varying analysis needed?

See references/psychoacoustic-metrics.md for method selection guidance (ISO 532-1 vs 532-2), interpretation tables (sone values, sharpness/roughness/fluctuation scales), tonality metric selection, and combined sound quality assessment workflows.

Unit Conversions

Use built-in functions — do NOT reimplement conversion formulas manually:

Conversion Function Method selection
phon → sone phon2sone(phon, standard) "ISO 532-1" (default), "ISO 532-2"
sone → phon sone2phon(sone, standard) "ISO 532-1" (default), "ISO 532-2"
Hz → ERB hz2erb(freq) Single formula (no variants)
ERB → Hz erb2hz(erb) Single formula (no variants)
Hz → bark hz2bark(freq) Single formula (no variants)
bark → Hz bark2hz(bark) Single formula (no variants)
Hz → mel hz2mel(freq, MelStyle=s) "oshaughnessy" (default), "slaney"
mel → Hz mel2hz(mel, MelStyle=s) "oshaughnessy" (default), "slaney"

When the user specifies a standard (e.g., "ISO 532-2"), pass it as the second positional argument for phon2sone/sone2phon. For hz2mel/mel2hz, use the MelStyle name-value pair.

Noise Compliance

Choose the function based on the application:

Situation Function Why
General background noise rating noiseCriteria Standard NC curves (ANSI/ASA S12.2)
HVAC system noise specifically roomCriteria RC Mark II designed for HVAC evaluation
Noise with audible surging/fluctuation roomNoiseCriteria RNC penalizes time-varying noise (requires ≥15 s recording)
Quick speech interference check speechInterferenceLevel Direct SIL value
Need recommended levels for room type noiseCriteriaRecommendations Lookup table by room use
[audioIn, fs] = audioread("office_noise.wav");

[NC, SIL, specImbalance, rattleRisk] = noiseCriteria(audioIn, fs, ...
    CalibrationFactor=2.3);

[RC, LMF, QAI, rattleRisk] = roomCriteria(audioIn, fs, ...
    CalibrationFactor=2.3);

SIL = speechInterferenceLevel(audioIn, fs, CalibrationFactor=2.3);

rec = noiseCriteriaRecommendations("NC", Occupancy="all");

Return types:

  • NC is a string (e.g., "NC-35" or "NC-56 (1000 Hz)" if a band exceeds)
  • RC is a string (e.g., "RC-32(N)" — the letter indicates spectrum quality: N/R/H/RV)
  • SIL and LMF are double scalars (dB)
  • QAI is a double scalar (dB; 0 = ideal neutral)
  • specImbalance and rattleRisk are structs with .Summary (string) and .Details (table) — access .Summary for display. Exception: specImbalance degrades to a char scalar (not a struct) when NC exceeds the curve range (e.g., "Above NC-70") or falls below NC-15. Guard with isstruct(specImbalance) before dot-indexing.
  • noiseCriteriaRecommendations requires a criteria name argument: "NC", "RNC", or "RC"

Ask the user:

  • What type of space? (office, classroom, hospital, concert hall)
  • Is the noise source HVAC? → prefer roomCriteria
  • Is the noise steady or fluctuating? → if fluctuating, use roomNoiseCriteria

See references/noise-compliance.md for detailed output interpretation and selection guidance.

Speech Quality

[degraded, fs] = audioread("processed_speech.wav");
[reference, ~] = audioread("reference_speech.wav");

mosScore = visqol(degraded, reference, fs, Mode="speech");

intelligibility = stoi(degraded, reference, fs);

Critical: stoi and visqol argument order is (processed, reference, fs) — processed/degraded signal FIRST.

visqol modes: "speech" for narrowband/wideband speech (outputs 1-5 MOS), "audio" (default) for general audio.

STI/SII Measurement

Two methods are available for both speechTransmissionIndex and speechIntelligibilityIndex:

IR-based method (simpler — no alignment needed):

[ir, fs] = audioread("room_impulse_response.wav");
sti = speechTransmissionIndex(ir, fs);
sii = speechIntelligibilityIndex(ir, fs);

Direct method (from test signals transmitted through the system):

fs = 48000;
reference = stipaExcitation(fs, Duration=15);
% Transmit reference through system, record as 'processed'
[processed, ~] = audioread("received_stipa.wav");

% IMPORTANT: Align signals before computing (see Signal Alignment)
processed = alignsignals(processed, reference);

sti = speechTransmissionIndex(processed, reference, fs);

Ask the user:

  • Do they have an impulse response? → use IR method (simpler, no alignment needed)
  • Measuring live? → generate test signal, transmit, record, align, compute
  • STI or SII? STI for room/PA systems (IEC 60268-16); SII for hearing/communication (ANSI S3.5)

See references/sti-measurement.md for STI NV arguments, post-processing adjustment workflow (Annex M), test signal generation, and result interpretation. See references/sii-measurement.md for SII standard clause selection (5.1-5.3), BandImportanceFunction guidance, measurement positioning, and result interpretation.

SPL Measurement (Offline)

[audioIn, fs] = audioread("recording.wav");

spl = splMeter(SampleRate=fs, ...
    Bandwidth="Full band", ...
    FrequencyWeighting="A-weighting", ...
    TimeWeighting="fast", ...
    CalibrationFactor=2.3);

[Lt, Leq, Lpeak, Lmax] = spl(audioIn);

% For a single overall value, read the final row
fprintf("Leq: %.1f dB(A)\n", Leq(end));

Valid Bandwidth values: "Full band" (overall level), "1 octave", "2/3 octave", "1/3 octave". There is no "broadband" option — use "Full band" for a single overall SPL value.

Output arity: All outputs (Lt, Leq, Lpeak, Lmax) have one row per input sample. For a 12-second file at 44.1 kHz, that is 539,648 rows. Never fprintf or disp the full output — it will hang. Instead:

  • Single overall value: read Leq(end) (the final accumulated value)
  • Time-interval readings: downsample to interval boundaries: Leq(round(fs*T):round(fs*T):end, :) where T is the desired interval in seconds

TimeInterval controls the integration window for Leq computation but does NOT reduce the number of output rows.

Do not manually compute 20*log10(rms(audioIn)/20e-6) — splMeter handles calibration, weighting, and time integration correctly per IEC 61672.

Multi-band (octave-band) SPL: Use splMeter with Bandwidth="1 octave" — do NOT use octaveFilterBank + manual dB conversion. Key properties for octave-band SPL:

[audioIn, fs] = audioread("recording.wav");

spl = splMeter(SampleRate=fs, ...
    Bandwidth="1 octave", ...
    FrequencyWeighting="Z-weighting", ...
    TimeWeighting="fast", ...
    FrequencyRange=[15 8000], ...
    OctaveFilterOrder=8, ...
    CalibrationFactor=2.3);

[Lt, Leq, Lpeak, Lmax] = spl(audioIn);
cf = getCenterFrequencies(spl);

Critical gotchas for octave-band splMeter:

  • Property name is OctaveFilterOrder (not FilterOrder). FilterOrder does not exist on splMeter.
  • FrequencyRange compares against true center frequencies (fc = 1000 × 10^(3b/10) per IEC 61260), not nominal values. A band is included only if its true center falls within [lower, upper]. Bands below 1 kHz have true centers slightly above nominal (e.g., "63 Hz" = 63.10); bands above 1 kHz have true centers below nominal (e.g., "2 kHz" = 1995.26, "8 kHz" = 7943.28). Use floor(true_center) for lower bounds and ceil(true_center) for upper bounds. Common case "16 Hz to 8 kHz": use [15 8000] — the 16 Hz true center is 15.85 (so 15 ≤ 15.85 includes it) and 8 kHz true center is 7943 (so 8000 ≥ 7943 includes it). Do NOT use [16 8000] — this drops the 16 Hz band.
  • Output is per-sample (see Output arity note above). Never print the full output. For time-interval readings, downsample: Lt100ms = Lt(round(fs*0.1):round(fs*0.1):end, :);

See references/spl-measurement.md for weighting selection guidance (A/C/Z and fast/slow), SPL interpretation tables, occupational exposure limits, splMeter vs poctave equivalency, and common measurement scenarios.

Broadcast Loudness (EBU R 128)

[audioIn, fs] = audioread("broadcast_content.wav");
[loudness, loudnessRange] = integratedLoudness(audioIn, fs);

% Check compliance: target is -23 LUFS +/- 1 LU
isCompliant = abs(loudness - (-23)) <= 1;

Use integratedLoudness for offline compliance checks. Use loudnessMeter for streaming/real-time monitoring (see matlab-process-streaming-audio).

RT60

[ir, fs] = audioread("room_ir.wav");
rtsummary = rt60(ir, fs, Bandwidth="1 octave");

Ask the user:

  • What is the room type? (drives the target RT60)
  • Do they have an IR, or do they need to measure one?
  • T30 (standard) vs T20 (when noise floor limits dynamic range)?

See references/rt60-measurement.md for target RT60 values by space type, T20/T30/EDT interpretation, frequency-dependent analysis, and relationship to STI.

Tonality

[audioIn, fs] = audioread("machine_noise.wav");

[tnr, tnrFreq, isProminent] = acousticToneToNoiseRatio(audioIn, fs);

[pr, prFreq, isProminent] = acousticProminenceRatio(audioIn, fs);

All outputs are empty (0×1) when no tonal components are detected. Check isempty(tnr) before indexing.

Standards-Compliant Filters (Custom Preprocessing)

The metric functions above perform standards-compliant filtering internally. Use standalone filter objects only for custom measurement workflows where no single metric function applies AND you do not need SPL output:

% A-weighting filter
wf = weightingFilter("A-weighting", fs);
weightedSignal = wf(audioIn);

% Octave-band filter bank (for waveform-level band decomposition only)
ofb = octaveFilterBank("1 octave", fs, ...
    FrequencyRange=[31.5 16000], FilterOrder=8);
bandSignals = ofb(audioIn);

If you need octave-band SPL levels, use splMeter with Bandwidth="1 octave" instead of octaveFilterBank + manual dB conversion. See the Multi-band SPL section above.

Signal Alignment

Function Internal alignment? Action
visqol Yes No action needed
stoi No Align before calling
speechTransmissionIndex No Align for direct method; IR method avoids issue
speechIntelligibilityIndex No Align for direct method; IR method avoids issue

For the direct (processed, reference, fs) method, always align signals:

processed = alignsignals(processed, reference);

SII is more sensitive to misalignment than STI. At 50 ms delay: SII error ~0.016, STI negligible. At 200 ms: SII ~0.036, STI ~0.017. The IR-based (ir, fs) method avoids alignment issues entirely.

Common Mistakes

Mistake Why it fails Correct approach
Call pesq(reference, degraded, fs) Function does not exist visqol(degraded, reference, fs, Mode="speech")
stoi(reference, processed, fs) Wrong argument order stoi(processed, reference, fs) — processed FIRST
acousticFluctuationStrength(...) Wrong function name acousticFluctuation(audioIn, fs, calFactor)
Implement NC from scratch with hardcoded curves Misses SIL, spectrum imbalance, rattle risk noiseCriteria(audioIn, fs, CalibrationFactor=cf)
20*log10(rms(x)/20e-6) for SPL No weighting, no time integration, no calibration splMeter with appropriate settings
Implement STI algorithm manually ~100 lines, error-prone, misses NV options speechTransmissionIndex(ir, fs)
Divide signal by calibration factor manually Incorrect; factor is a multiplier, not a divisor Pass as positional arg or CalibrationFactor=value
Skip alignment for direct STI/SII Silent accuracy degradation alignsignals(processed, reference) before calling
Use acousticLoudness for broadcast loudness Wrong standard (ISO 532 = sones, not LUFS) integratedLoudness for EBU R 128 compliance
string(rattleRisk) or fprintf("%s", specImbalance) These are structs, not strings Access .Summary field: rattleRisk.Summary
specImbalance.Summary when NC is out of curve range specImbalance degrades to char (not struct) when "Above NC-70" or below NC-15 Guard: if isstruct(specImbalance), disp(specImbalance.Summary); end
roomNoiseCriteria on recordings shorter than 15 s ANSI/ASA S12.2-2019 requires ≥150 Lt samples at 100 ms intervals Ensure recording is at least 15 seconds; use noiseCriteria for shorter clips
round(NC) or arithmetic on NC/RC output NC and RC are strings, not numbers Use directly: fprintf("%s\n", NC)
noiseCriteriaRecommendations() with no args First argument (criteria name) is required noiseCriteriaRecommendations("NC")
loudness.OverallLoudness after acousticLoudness Returns a scalar or vector, not a struct Use directly: fprintf("%.1f sones\n", loudness)
splMeter(..., FilterOrder=8) Property does not exist on splMeter Use OctaveFilterOrder=8
splMeter(..., FrequencyRange=[16 8000]) for 16 Hz–8 kHz Drops 16 Hz band (true center is 15.85 Hz, which is below 16) Use FrequencyRange=[15 8000] — use floor(true_center) for lower bound
octaveFilterBank + 20*log10(rms(band)/pRef) for SPL Manual, no time weighting, no calibration integration Use splMeter with Bandwidth="1 octave"
Treating splMeter output as a single value All outputs have one row per input sample (e.g., 539k rows for 12s at 44.1 kHz) Use Leq(end) for a single value, or downsample to interval boundaries

Conventions

  • Always ask about the user's measurement goal before selecting a function
  • Always ask about calibration — recommend calibrateMicrophone for system-level accuracy
  • Default to SoundField="free" for anechoic/outdoor, "diffuse" for reverberant rooms
  • Default to Method="ISO 532-1" for loudness (Zwicker method, most common)
  • Use IR-based methods for STI/SII when an impulse response is available (avoids alignment)
  • Pass CalibrationFactor explicitly rather than pre-scaling the signal
  • For noise compliance, prefer roomCriteria for HVAC and noiseCriteria for general background noise

Copyright 2026 The MathWorks, Inc.