Generated banner for Matlab Evaluate Acoustic Metrics
Generated logo for Matlab Evaluate Acoustic Metrics

Matlab Evaluate Acoustic Metrics

MathWorksOfficialCreated by MathWorks

  • Skill
  • Tool integration
  • Science and research
  • BSD-2-Clause
  • No issues found

From the author

Select and use Audio Toolbox acoustic, psychoacoustic, and speech quality metrics. Use when computing loudness, sharpness, roughness, fluctuation strength, noise criteria (NC/RNC/RC), speech transmission index (STI), speech intelligibility index (SII), speech quality (ViSQOL, STOI), SPL, reverberation time (RT60), tone-to-noise ratio, or prominence ratio. Also use when asked about PESQ, POLQA, calibration factor, EBU R 128, LUFS, HVAC noise, background noise evaluation, or how to measure audio quality or noise compliance. Covers calibration, standard selection, signal alignment, and the complete function inventory including what does NOT exist.
— the description by MathWorks

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured approach to using MATLAB Audio Toolbox for acoustic measurement and analysis. It guides users through selecting the correct functions for psychoacoustic metrics, speech intelligibility, noise compliance, and broadcast loudness. The skill emphasizes proper calibration techniques, standard-compliant workflows, and the correct handling of signal data to ensure accurate results, while explicitly warning against the use of nonexistent or licensed-restricted functions.

What it does

  • Calculates psychoacoustic metrics like loudness, sharpness, roughness, and fluctuation strength.
  • Evaluates speech quality and intelligibility using metrics such as ViSQOL, STOI, STI, and SII.
  • Assesses noise compliance using NC, RC, and RNC ratings for various environments.
  • Performs offline SPL measurements and broadcast loudness checks (EBU R 128).
  • Provides guidance on microphone calibration and system-level calibration factors.
  • Offers standard-compliant filtering and unit conversion utilities.

When to use it

  • When you need to measure perceived loudness or sound quality in a research or engineering context.
  • When evaluating room acoustics or HVAC noise compliance against industry standards.
  • When analyzing speech intelligibility for communication systems or public address setups.
  • When you need to calibrate audio recordings for accurate SPL or metric computation.
  • When you need to perform broadcast loudness compliance checks.

Advantages

  • Ensures adherence to international standards like ISO 532-1/2, ANSI/ASA S12.2, and IEC 60268-16.
  • Prevents common errors such as incorrect function usage, argument ordering, or manual implementation of complex algorithms.
  • Provides clear guidance on calibration to ensure measurement accuracy.
  • Streamlines the selection of appropriate metrics based on specific application goals.

Usage examples

“How do I calculate the loudness of a recording in sones?”
The agent will guide you to use the acousticLoudness function, specifying the ISO 532-1 method, applying your calibration factor, and selecting the appropriate sound field.
“Is my office HVAC noise compliant?”
The agent will recommend using the roomCriteria function to obtain an RC rating, explaining how to interpret the output and verify the measurement conditions.
“How can I measure the speech quality of a processed audio file?”
The agent will instruct you to use the visqol function with the 'speech' mode, ensuring the processed signal is passed as the first argument.
“How do I calibrate my microphone for SPL measurements?”
The agent will explain how to use calibrateMicrophone with a 1 kHz tone recording and a known SPL reference to compute the correct calibration factor.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/matlab/matlab-evaluate-acoustic-metrics/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip matlab-evaluate-acoustic-metrics.zip -d .agents/skills

Documentation by MathWorks

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
GoalCategoryGo 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

FunctionStatusUse Instead
pesqDoes NOT exist (proprietary ITU-T P.862 license)visqol(degraded, reference, fs, Mode="speech")
polqaDoes 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

FunctionPurposeStandardAvailable From
acousticLoudnessPerceived loudness (sones)ISO 532-1/2R2020a
acousticSharpnessPerceived sharpness (acum)DIN 45692R2020a
acousticRoughnessPerceived roughness (asper)—R2021a
acousticFluctuationFluctuation strength (vacil)—R2020b
integratedLoudnessBroadcast loudness (LUFS)EBU R 128 / ITU-R BS.1770R2016b
loudnessMeterStreaming broadcast loudness meterEBU R 128 / ITU-R BS.1770R2018a
visqolFull-reference audio quality (MOS)—R2024a
stoiShort-time objective intelligibility—R2024a
mnruModulated noise reference unitITU-T P.810R2024a
dBovPower level relative to overload (dB)ITU-T G.100.1R2024a
speechTransmissionIndexSTI from IR or test signalsIEC 60268-16R2026a
speechIntelligibilityIndexSII from IR, test signals, or levelsANSI/ASA S3.5-1997R2026b
stipaExcitationGenerate STI/STIPA test signalsIEC 60268-16R2026a
siiExcitationGenerate SII test signalsANSI/ASA S3.5-1997R2026b
noiseCriteriaNC rating + SIL + spectrum imbalance + rattle riskANSI/ASA S12.2R2026b
roomNoiseCriteriaRNC rating (detects surging/fluctuations)ANSI/ASA S12.2R2026b
roomCriteriaRC Mark II rating (HVAC systems)ANSI/ASA S12.2R2026b
noiseCriteriaCurvesLook up NC curve valuesANSI/ASA S12.2R2026b
noiseCriteriaRecommendationsRecommended NC levels by room typeANSI/ASA S12.2R2026b
speechInterferenceLevelSIL from recordingANSI/ASA S12.2R2026b
rt60Reverberation time from impulse responseISO 3382-1/2R2025a
acousticToneToNoiseRatioDetect/quantify tonesECMA-418-1R2023b
acousticProminenceRatioTonality via critical band comparisonECMA-418-1R2023b
splMeterSPL measurement (time-weighted, per-band)IEC 61672R2018a
calibrateMicrophoneCompute calibration factor from known tone—R2018b
weightingFilterA/C/Z/K frequency weightingIEC 61672-1R2016b
octaveFilterSingle octave-band filterANSI S1.11-2004R2017b
octaveFilterBankMulti-band octave filteringANSI S1.11-2004R2019a

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:

ConversionFunctionMethod selection
phon → sonephon2sone(phon, standard)"ISO 532-1" (default), "ISO 532-2"
sone → phonsone2phon(sone, standard)"ISO 532-1" (default), "ISO 532-2"
Hz → ERBhz2erb(freq)Single formula (no variants)
ERB → Hzerb2hz(erb)Single formula (no variants)
Hz → barkhz2bark(freq)Single formula (no variants)
bark → Hzbark2hz(bark)Single formula (no variants)
Hz → melhz2mel(freq, MelStyle=s)"oshaughnessy" (default), "slaney"
mel → Hzmel2hz(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:

SituationFunctionWhy
General background noise ratingnoiseCriteriaStandard NC curves (ANSI/ASA S12.2)
HVAC system noise specificallyroomCriteriaRC Mark II designed for HVAC evaluation
Noise with audible surging/fluctuationroomNoiseCriteriaRNC penalizes time-varying noise (requires ≥15 s recording)
Quick speech interference checkspeechInterferenceLevelDirect SIL value
Need recommended levels for room typenoiseCriteriaRecommendationsLookup 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

FunctionInternal alignment?Action
visqolYesNo action needed
stoiNoAlign before calling
speechTransmissionIndexNoAlign for direct method; IR method avoids issue
speechIntelligibilityIndexNoAlign 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

MistakeWhy it failsCorrect approach
Call pesq(reference, degraded, fs)Function does not existvisqol(degraded, reference, fs, Mode="speech")
stoi(reference, processed, fs)Wrong argument orderstoi(processed, reference, fs) — processed FIRST
acousticFluctuationStrength(...)Wrong function nameacousticFluctuation(audioIn, fs, calFactor)
Implement NC from scratch with hardcoded curvesMisses SIL, spectrum imbalance, rattle risknoiseCriteria(audioIn, fs, CalibrationFactor=cf)
20*log10(rms(x)/20e-6) for SPLNo weighting, no time integration, no calibrationsplMeter with appropriate settings
Implement STI algorithm manually~100 lines, error-prone, misses NV optionsspeechTransmissionIndex(ir, fs)
Divide signal by calibration factor manuallyIncorrect; factor is a multiplier, not a divisorPass as positional arg or CalibrationFactor=value
Skip alignment for direct STI/SIISilent accuracy degradationalignsignals(processed, reference) before calling
Use acousticLoudness for broadcast loudnessWrong standard (ISO 532 = sones, not LUFS)integratedLoudness for EBU R 128 compliance
string(rattleRisk) or fprintf("%s", specImbalance)These are structs, not stringsAccess .Summary field: rattleRisk.Summary
specImbalance.Summary when NC is out of curve rangespecImbalance degrades to char (not struct) when "Above NC-70" or below NC-15Guard: if isstruct(specImbalance), disp(specImbalance.Summary); end
roomNoiseCriteria on recordings shorter than 15 sANSI/ASA S12.2-2019 requires ≥150 Lt samples at 100 ms intervalsEnsure recording is at least 15 seconds; use noiseCriteria for shorter clips
round(NC) or arithmetic on NC/RC outputNC and RC are strings, not numbersUse directly: fprintf("%s\n", NC)
noiseCriteriaRecommendations() with no argsFirst argument (criteria name) is requirednoiseCriteriaRecommendations("NC")
loudness.OverallLoudness after acousticLoudnessReturns a scalar or vector, not a structUse directly: fprintf("%.1f sones\n", loudness)
splMeter(..., FilterOrder=8)Property does not exist on splMeterUse OctaveFilterOrder=8
splMeter(..., FrequencyRange=[16 8000]) for 16 Hz–8 kHzDrops 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 SPLManual, no time weighting, no calibration integrationUse splMeter with Bandwidth="1 octave"
Treating splMeter output as a single valueAll 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.


Files

references/sti-measurement.md

STI Measurement

Intake Questions

1. What is the application?

Target STI values from IEC 60268-16 Table G.1 and application-specific standards:

ApplicationTarget STIStandard/Regulation
Voice alarm / emergency PA>= 0.50IEC 60268-16 Table G.1 (Cat. G); EN 54-16, BS 5839-8
Classroom / lecture hall>= 0.62IEC 60268-16 Table G.1 (Cat. D); BB93 (UK), ANSI S12.60
Courtroom / parliament>= 0.70IEC 60268-16 Table G.1 (Cat. B)
Teleconferencing>= 0.66IEC 60268-16 Table G.1 (Cat. C)
Recording studio / broadcast> 0.76IEC 60268-16 Table G.1 (Cat. A+)
PA in difficult acoustic environments>= 0.46IEC 60268-16 Table G.1 (Cat. H)
2. What measurement method should they use?
ScenarioRecommended MethodRationale
Room acoustics simulationIR (indirect)Fastest; works directly with simulated or measured IRs
PA/VA system commissioningDirect STIPAIEC 60268-16 Annex B recommended; captures nonlinear behavior
System with AGC, compression, or limitingDirect STIPAIR method assumes linearity -- invalid for nonlinear systems
Lab reference measurementDirect Full STIMaximum precision (all 98 modulation frequencies)
Comparing rooms from IR databasesIR (indirect)Quick what-if analysis; vary noise without re-measuring
Digital codec or telecom evaluationDirect STIPACaptures codec distortion the IR method cannot
Fluctuating industrial noiseDirect (with caution)All methods have limited accuracy with time-varying noise

Limitations of indirect (IR) methods: The IR method cannot capture the effect of ambient noise directly -- noise must be added mathematically to the MTF after measurement (see Post-Processing Adjustment Workflow below). Always measure in silence when using the IR method, then specify operational noise via OperationalNoiseLevel.

3. Do they have an impulse response or will they measure?
  • Have an IR: Use the IR method -- simpler, no alignment needed, fastest

  • Will measure live: Generate test signal with stipaExcitation, transmit through system, record, align, compute

  • Simulated room: Use acousticRoomResponse to get an IR, then use the IR method

4. What are the operational conditions?

These significantly affect the result -- ask about:

  • Ambient noise level: What NC rating (or octave-band levels) is expected during normal operation? Default is NC-35. Changing from NC-25 to NC-45 can reduce STI by 0.10-0.20.

  • Speech level: Normal voice (60 dBA at 1 m) is the default. Raised voice changes the spectral shape and typically improves STI.

  • Auditory effects: Should auditory masking be included? (Default: yes.) Disabling gives the "physical" STI without listener hearing model.

5. Measurement conditions (for direct method)
  • Sample rate: >=16 kHz required; 48 kHz recommended

  • Signal duration: >=15 s for STIPA (default 18 s provides margin)

  • Signal level: STIPA test signal should be played at 3 dB above measured speech LAeq at the mic position (IEC 60268-16 clause 5.1 / Annex J)

  • Background noise: Measure ambient noise with system off before the STI measurement

  • Multiple positions: Measure at representative listener positions; report worst-case STI per zone

  • HVAC: Ensure all building systems in normal operating state during measurement

speechTransmissionIndex

Two syntaxes are available. All NV arguments work with both syntaxes.

Method 1: IR-based

Compute STI directly from a room impulse response. No alignment needed. The IR must be at least 1.6 seconds long (the period of the lowest modulation frequency, 0.63 Hz).

[ir, fs] = audioread("room_ir.wav");
sti = speechTransmissionIndex(ir, fs);

To obtain the impulse response: use sweeptone to generate a sine sweep, play it through the system, record the response, then extract the IR with impzest. See matlab-play-record-audio for the device I/O workflow.

Method 2: Direct (test signal)

Transmit a test signal through the system and compute STI from the received signal. Two test signal types are available:

TypeSignal ShapeDurationPrecision
"Direct STIPA"Column vector (N-by-1)~15 s minimum14 modulation frequencies (2 per band)
"Direct Full STI"N-by-14-by-7 array~15 min totalAll 98 modulation frequencies

STIPA (faster, most common):

fs = 48000;
reference = stipaExcitation(fs, Duration=15, Type="Direct STIPA");

% --- Transmit 'reference' through system, record 'processed' ---

% Align signals (required -- function does not align internally)
processed = alignsignals(processed, reference);

sti = speechTransmissionIndex(processed, reference, fs);

Full STI (maximum precision):

fs = 48000;
reference = stipaExcitation(fs, Duration=15, Type="Direct Full STI");

% --- Transmit 'reference' through system, record 'processed' ---

processed = alignsignals(processed, reference);

sti = speechTransmissionIndex(processed, reference, fs);

For both types, the processed signal must have the same dimensions as the reference.

Name-Value Arguments (both syntaxes)
ArgumentDefaultPurpose
OperationalSignalLevel60 + [-2.5 0.5 0 -6 -12 -18 -24]Expected speech level per octave band (dB)
OperationalNoiseLevel"NC-35"Ambient noise -- string ("NC-xx", "RNC-xx", or "RC-xx") or 1-by-7 vector (dB). String input requires R2026b+; earlier releases require the vector form.
AddOperationalAmbientNoisetrueAdd ambient noise to MTF computation
AddOperationalAuditoryContributionstrueAdd auditory masking and threshold effects
RawSignalLevel--Measured signal level per band (dB)
RawNoiseLevel--Measured noise level per band (dB)
RemoveRawAmbientNoisefalseRemove ambient noise from raw measurement
RemoveRawAuditoryContributionsfalseRemove auditory effects from raw measurement

Constraints:

  • RawSignalLevel and RawNoiseLevel must be specified together (both or neither)

  • RemoveRawAmbientNoise=true requires both RawSignalLevel and RawNoiseLevel to be set

  • RemoveRawAuditoryContributions=true requires both RawSignalLevel and RawNoiseLevel to be set

Post-Processing Adjustment Workflow (IEC 60268-16 Annex M)

The NV arguments implement the post-processing steps from Table M.1 of the standard. The midpoint of the process is deriving an MTF free of ambient noise and auditory effects -- then adding back the desired operational conditions.

Select the workflow based on measurement conditions:

Measurement ConditionWhat to SetWhy
Measured in silence (ideal)OperationalNoiseLevel, OperationalSignalLevelMTF is already free of noise -- just add operational conditions (Step 5)
Measured with ambient noise presentRemoveRawAmbientNoise=true, RawSignalLevel, RawNoiseLevel, then operational argsRemove measured noise (Step 4), then add operational conditions (Step 5)
Measured with auditory model already appliedRemoveRawAuditoryContributions=true, RawSignalLevel, RawNoiseLevel, then operational argsRemove old auditory effects (Step 3), optionally remove noise (Step 4), then add new conditions (Step 5)
Simulating different operational conditionsChange OperationalNoiseLevel and/or OperationalSignalLevelSimulate operational conditions with alternative speech/noise levels without re-measuring

Recommended practice: Measure the MTF in silence (test signal only, no ambient noise), then separately measure the operational noise level. This gives the cleanest starting point -- the function adds operational noise and auditory effects via AddOperationalAmbientNoise and AddOperationalAuditoryContributions (both default to true). If the MTF cannot be measured in silence, separately measure background noise levels per octave band so they can be removed.

Ask the user about:

  • What is the expected operational noise level? (NC rating or octave-band levels)

  • What speech level is expected? (default assumes normal speech at 1 m)

  • Should auditory masking effects be included? (default: yes)

  • Were the measurements taken in silence, or was ambient noise present during measurement?

Outputs
[sti, mti, mtf] = speechTransmissionIndex(ir, fs, ...
    OperationalNoiseLevel="NC-40");
  • sti -- Speech transmission index (0 to 1)

  • mti -- Modulation transmission index (table with per-band values)

  • mtf -- Modulation transfer function (table)

stipaExcitation

% STIPA signal (default -- single merged signal)
reference = stipaExcitation(fs, Duration=15, Type="Direct STIPA");

% Full STI signal (all 98 modulation frequencies)
reference = stipaExcitation(fs, Duration=15, Type="Direct Full STI");

STIPA uses a subset of modulation frequencies for faster measurement (~15 seconds minimum). Full STI uses all 98 combinations (14 modulation frequencies x 7 octave bands) for maximum accuracy.

Signal Alignment

speechTransmissionIndex does not perform internal alignment for the direct method.

Impact of misalignment (empirically measured):

DelaySTI Error (15s STIPA)
<=50 ms<0.001 (negligible)
100 ms0.004
200 ms0.017
500 ms0.046 (10s signal)

Always align for the direct method:

processed = alignsignals(processed, reference);

The IR-based method avoids this issue entirely -- impzest deconvolution handles propagation delay during IR extraction.

Complete Workflow: Measure STI in a Room

fs = 48000;

% Generate test signal
reference = stipaExcitation(fs, Duration=15);

% Play through room and record (see matlab-play-record-audio for device I/O)
% processed = <recorded signal>

% Align
processed = alignsignals(processed, reference);

% Compute STI with operational conditions
[sti, mti] = speechTransmissionIndex(processed, reference, fs, ...
    OperationalNoiseLevel="NC-40", ...
    AddOperationalAuditoryContributions=true);

fprintf("STI = %.2f\n", sti);

Interpreting Results

Interpreting STI (IEC 60268-16 Table G.1)

For a native listener with no hearing impairment:

CategoryNominal STIComment
A+> 0.76Excellent intelligibility
A0.74High intelligibility
B0.70High intelligibility
C0.66High intelligibility
D0.62Good intelligibility
E0.58High quality public address systems
F0.54Good quality public address systems
G0.50Target value for voice alarm systems
H0.46Lower limit for voice alarm systems
I0.42Limited intelligibility
J0.38Not suitable for public address systems
U< 0.36Not suitable for public address systems
Using MTI for Diagnostics

The per-band Modulation Transfer Index reveals where intelligibility is being lost:

Low MTI LocationLikely CauseRemediation
Low frequencies (125-250 Hz)Excessive low-frequency reverberation or rumble noiseAdd low-frequency absorption; isolate HVAC vibration
Mid frequencies (500-2000 Hz)General reverberance problem -- most critical for intelligibilityAdd broadband absorption; reduce RT60
High frequencies (4000-8000 Hz)Air absorption, poor HF loudspeaker coverage, or HVAC hissImprove speaker positioning; treat HF noise sources
Uniform across all bandsHigh ambient noise floor overwhelming the signalReduce noise or increase signal level (loudspeaker power)
RT60-to-STI Rule of Thumb

Approximate STI for purely diffuse exponential-decay rooms with no ambient noise (computed using speechTransmissionIndex on synthetic IRs with AddOperationalAmbientNoise=false):

RT60 (s)Approximate STI
0.50.74
1.00.60
1.50.50
2.00.44
3.00.36
5.00.27

Adding ambient noise can only reduce STI below these values.

Effect of Noise Curve on STI

Changing the operational noise has a dramatic effect. The magnitude depends on the room — drier rooms (lower RT60) are more sensitive to noise because modulations are better preserved before noise is added. Computed using speechTransmissionIndex on synthetic diffuse-decay IRs:

Noise ConditionSTI Reduction (RT60=0.5s)STI Reduction (RT60=1.0s)
NC-25ReferenceReference
NC-35-0.06-0.04
NC-45-0.22-0.16
NC-55-0.47-0.37
NC-65-0.68-0.55

Troubleshooting

ObservationLikely CauseAction
STI too high (> 0.9) for a reverberant roomOperational corrections not appliedVerify AddOperationalAmbientNoise=true and AddOperationalAuditoryContributions=true (both default to true)
STI too low for a quiet, dry roomNoisy IR (background noise in the recording)Use direct STIPA method, or re-record IR with higher SNR (need >=20 dB)
STIPA and IR method disagree by > 0.05System is nonlinear (AGC, compression)Trust STIPA for real-world condition; IR method is invalid for nonlinear systems
MTF values > 1.0 reportedNoise contamination during direct measurementValues are clipped internally, but if many exceed 1.3, re-measure (impulsive noise likely)
STI varies significantly across positionsNormal for large rooms -- coverage is non-uniformReport worst-case per position; investigate speaker aiming
IR method gives STI = 0IR is likely all zeros or wrong channelVerify IR has energy: max(abs(ir)) should be > 0
Direct method gives very low STI with correct signalSignals not aligned, or reference type mismatchVerify alignment with alignsignals; ensure stipaExcitation Type matches

Copyright 2026 The MathWorks, Inc.


Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
MathWorks
Publisher
MathWorks (Official)
Source
matlab/matlab-agentic-toolkit · skills-catalog/signal-processing/matlab-evaluate-acoustic-metrics · commit 2a7f215
Licence
BSD 2-Clause License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE.md)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by MathWorks.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download matlab-evaluate-acoustic-metrics.zip