speech.sti
Speech Transmission Index (STI) per IEC 60268-16:2020 (Edition 5).
Implements the full-STI indirect method from impulse responses (Schroeder modulation transfer function), the direct STIPA method on recorded signals (Annex B), an Ed.5-conformant STIPA test-signal generator (clauses A.4 and A.6.1) and the Annex M adjustment of a measured result to other speech and occupancy-noise levels. Only the male speech option exists: Edition 5 removed the female spectrum and weighting factors (foreword, item d).
The computation chain (octave-band MTF -> auditory masking and reception threshold correction -> effective SNR clipped to +/-15 dB -> transmission indices -> band MTI -> weighted STI) is numerically identical between Ed.4 (2011) clauses A.5.2-A.5.6 and Ed.5; the only Ed.5 numeric change is the male test-signal spectrum (A.6.1).
The level adjustment of sti_adjusted_for_levels is the four-step
procedure of Ed.4 (2011) Annex M, verified against the printed
intermediates of its Table M.1. The Ed.5 foreword (item g) says greater
information is given in Annex M about these adjustments, and its table of
contents grows the annex from three printed pages to ten and adds a flow
chart of the steps; the body of the Ed.5 annex could not be obtained, so
what those pages add beside this procedure is unknown here.
Auto-generated from the source docstrings by
scripts/generate_api_docs.py(make api-docs). Do not edit by hand.
sti_adjusted_for_levels
Section titled “sti_adjusted_for_levels”sti_adjusted_for_levels( mtf: np.ndarray, *, measured_level: Sequence[float] | np.ndarray, measured_ambient: Sequence[float] | np.ndarray | None = None, operational_level: Sequence[float] | np.ndarray, operational_ambient: Sequence[float] | np.ndarray | None = None,) -> STIResultSTI of a measured MTF matrix moved to other speech and noise levels.
A room is measured empty, at whatever level the test signal ran; the question is what the STI would be with the room occupied and the talker at the operational level. The four steps of IEC 60268-16 Annex M answer it without measuring again:
- acquire the modulation transfer matrix together with the speech and
background-noise octave-band levels that were present during the
measurement (
mtf,measured_level,measured_ambient); - divide out the correction those levels produced, which removes the background noise, the auditory masking and the reception threshold and leaves the matrix of the transmission channel alone;
- multiply by the correction the operational levels produce, putting the occupancy noise, masking and threshold of the simulated condition back in;
- process the resulting matrix into the index by the usual A.5.4 to A.5.6 chain.
Steps 2 and 3 run the one level-dependent correction of clause A.5.3 that the forward chain applies, once inverted and once as it stands, so the adjustment cannot drift from the masking and threshold model the rest of the module uses.
What is implemented is the Ed.4 (2011) Annex M procedure, verified against the printed intermediates of its Table M.1 worked example. The Ed.5 foreword (item g) says greater information is given in Annex M about these adjustments, and its table of contents grows the annex from three printed pages to ten; the body of the Ed.5 annex could not be obtained, so what those pages add beside this procedure is unknown here.
Parameters
| Name | Description |
|---|---|
mtf | Measured modulation transfer matrix, shape (7, n_modulation_frequencies). |
measured_level | Speech octave-band levels during the measurement, dB SPL (7 values). |
measured_ambient | Background-noise octave-band levels during the measurement, dB SPL (7 values); None for a measurement whose matrix carries masking and threshold but no noise. |
operational_level | Speech octave-band levels of the condition being simulated, dB SPL (7 values). |
operational_ambient | Occupancy-noise octave-band levels of that condition, dB SPL (7 values); None simulates a silent room. |
Returns: STIResult at the operational levels, carrying them in band_levels and ambient_levels.
Raises
| Exception | When |
|---|---|
| ValueError | if a level vector is not 7 finite values, or mtf is not a (7, n) matrix of finite non-negative values. |
sti_from_impulse_response
Section titled “sti_from_impulse_response”sti_from_impulse_response( ir: Signal | list[float] | np.ndarray, fs: int | None, snr: None, level: Sequence[float] | np.ndarray, ambient: Sequence[float] | np.ndarray,) -> STIResult
sti_from_impulse_response( ir: Signal | list[float] | np.ndarray, fs: int | None = ..., snr: None = ..., *, level: Sequence[float] | np.ndarray, ambient: Sequence[float] | np.ndarray,) -> STIResult
sti_from_impulse_response( ir: Signal | list[float] | np.ndarray, fs: int | None = ..., snr: float | Sequence[float] | np.ndarray | None = ..., level: Sequence[float] | np.ndarray | None = ...,) -> STIResultFull STI from a room/system impulse response (indirect method).
The impulse response is filtered into the seven octave bands 125 Hz - 8 kHz (IEC 61260-1 filters) and the modulation transfer function is obtained from the Schroeder integral (IEC 60268-16, indirect method):
at the 14 modulation frequencies 0,63-12,5 Hz (A.2.2). The result then follows the standard chain: optional noise degradation, optional auditory masking and absolute reception threshold correction, effective SNR clipped to +/-15 dB, transmission indices, band MTIs and the male-weighted STI (Ed.5 Table A.1).
When neither level nor ambient is given the level-dependent
auditory masking and the absolute reception threshold corrections are
skipped (they require absolute band levels), matching the common
“noise-free indirect measurement” use of the standard.
Parameters
| Name | Description |
|---|---|
ir | Impulse response (1D). Accepts a phonometry.io.Signal, whose calibration is applied to the samples and then cancels: every modulation index is normalised by the total intensity of its own band, so a factor on the record moves neither the transfer values nor the STI. The absolute levels the noise corrections need arrive through level and ambient, in dB, not from the samples. |
fs | Sample rate in Hz (>= 22,5 kHz so the 8 kHz band fits). Required for a bare array; a Signal brings its own, and an explicit value that disagrees with it raises instead of silently winning. |
snr | Optional signal-to-noise ratio in dB, scalar or one value per octave band. Degrades m by 1/(1 + 10^(-SNR/10)); combined with level it is interpreted as ambient levels level - snr so noise is not applied twice. Mutually exclusive with ambient. |
level | Optional speech octave-band levels in dB SPL (7 values) at the listener position; enables the auditory masking (Ed.5 Table A.2) and reception threshold (Ed.5 Table A.3) corrections. |
ambient | Optional ambient noise octave-band levels in dB SPL (7 values); requires level. |
Returns: STIResult with mtf of shape (7, 14).
stipa( x: Signal | list[float] | np.ndarray, fs: int | None, reference: Signal | list[float] | np.ndarray | None, level: Sequence[float] | np.ndarray, ambient: Sequence[float] | np.ndarray,) -> STIResult
stipa( x: Signal | list[float] | np.ndarray, fs: int | None = ..., reference: Signal | list[float] | np.ndarray | None = ..., *, level: Sequence[float] | np.ndarray, ambient: Sequence[float] | np.ndarray,) -> STIResult
stipa( x: Signal | list[float] | np.ndarray, fs: int | None = ..., reference: Signal | list[float] | np.ndarray | None = ..., level: Sequence[float] | np.ndarray | None = ...,) -> STIResultSTIPA on a recorded test signal (direct method, Annex B).
The recording is filtered into the seven octave bands, squared and
low-passed (~100 Hz) into intensity envelopes, and the modulation
depths at the two Table B.1 modulation frequencies of each band are
measured with the sine/cosine correlation over an integer number of
periods (Ed.4 A.5.2 = Ed.5). The modulation transfer values are the
measured depths normalized by the source modulation index 0,55
(Annex B) - or by the depths measured on reference when the
actually emitted signal is supplied - and feed the same masking /
threshold / TI / STI chain as the full method.
Physical background noise is already contained in the recording; use
level (and optionally ambient) only to enable the absolute
level-dependent corrections, which are otherwise skipped.
An STIWarning is emitted when the recording is shorter than
the recommended 15 s (IEC 60268-16 STIPA practice, 15 s to 25 s),
because the slow modulation components are then averaged over too few
periods and the recovered modulation depths - and hence the STI - are
biased low (an ideal loopback gives STI ~0.956 at 5 s vs ~0.998 at 18 s).
Parameters
| Name | Description |
|---|---|
x | Recorded STIPA signal (1D), 15 s to 25 s recommended. Accepts a phonometry.io.Signal, whose calibration is applied to the samples and then cancels: every modulation index is normalised by the total intensity of its own band, so a factor on the record moves neither the transfer values nor the STI. The absolute levels the noise corrections need arrive through level and ambient, in dB, not from the samples. |
fs | Sample rate in Hz (>= 22,5 kHz). Required for a bare array; a Signal brings its own, and an explicit value that disagrees with it raises instead of silently winning. |
reference | Optional reference recording of the undistorted test signal; its measured modulation depths replace the nominal 0,55 as normalization (useful for non-conformant sources). Accepts a phonometry.io.Signal. It is a second recording of the same test signal, so it has to share the rate: two Signals that disagree are refused rather than arbitrated, and measuring the reference on a rate that is not its own would return a perfect STI for a mismatch. Its calibration is applied like any other record’s, and then cancels, because what is taken from it is a modulation depth and those are normalised. |
level | Optional speech octave-band levels in dB SPL (7 values) enabling auditory masking and reception threshold corrections. |
ambient | Optional ambient noise octave-band levels in dB SPL (7 values); requires level. |
Returns: STIResult with mtf of shape (7, 2).
stipa_signal
Section titled “stipa_signal”stipa_signal( fs: int, seconds: float = 18.0, level_db: float | None = None, seed: int | None = None,) -> np.ndarrayGenerate an IEC 60268-16:2020 conformant STIPA test signal.
Pink-noise carriers are band-limited to half-octave bands centred on
the seven octave-band frequencies (clause A.4), set to the Ed.5 male
speech spectrum of clause A.6.1 (-2,5; 0,5; 0; -6; -12; -18; -24
dB re the 500 Hz band) and intensity-modulated with
0,5 (1 + 0,55 (sin 2 pi f1 t - sin 2 pi f2 t)) - the Table B.1
frequency pair of each band, 180 degrees between components, applied
in amplitude through its square root (Annex B).
Parameters
| Name | Description |
|---|---|
fs | Sample rate in Hz (>= 22,5 kHz). |
seconds | Duration in seconds (the standard recommends 15 s to 25 s; default 18 s). |
level_db | Optional overall level in dB SPL: the output is scaled so its RMS, taken as pascals, sits at level_db re 20 uPa. Default (None) normalizes the RMS to 0,1 (digital full scale headroom for the 12-14 dB crest factor). |
seed | Seed for the pink-noise generator (None: random). |
Returns: Test signal, 1D array of round(seconds * fs) samples.
STIResult
Section titled “STIResult”STIResult( sti: float, mti: np.ndarray, mtf: np.ndarray, band_levels: np.ndarray | None, rating: str, ambient_levels: np.ndarray | None = None,)Result of a Speech Transmission Index computation.
mtf holds the modulation transfer values actually used for the
transmission indices, i.e. after the optional SNR / masking /
reception-threshold corrections and after clipping to [0, 1]; its
shape is (7, 14) for full STI and (7, 2) for STIPA. mti is the
per-band modulation transfer index (7,), band_levels and
ambient_levels echo the speech and background-noise octave-band
levels used for the level-dependent corrections (None when they were
skipped) and rating is the Annex F qualification letter
(A+ .. U).
The two level spectra are what adjusted_for_levels reads to
undo the corrections they produced, so a result that carries them can
be moved to another speech and occupancy-noise condition.
STIResult.adjusted_for_levels()
Section titled “STIResult.adjusted_for_levels()”STIResult.adjusted_for_levels( *, operational_level: Sequence[float] | np.ndarray, operational_ambient: Sequence[float] | np.ndarray | None = None,) -> STIResultThis result moved to another speech and noise condition (Annex M).
The measurement condition is the one this result already carries in
band_levels and ambient_levels, so only the target condition
is passed: what the room would have scored occupied, or with the
talker raised to the operational level. See
sti_adjusted_for_levels, which does the work and states the
edition the procedure comes from.
Parameters
| Name | Description |
|---|---|
operational_level | Speech octave-band levels of the condition being simulated, in dB SPL (7 values). |
operational_ambient | Occupancy-noise octave-band levels of that condition, in dB SPL (7 values); None simulates a silent room. |
Returns: A new STIResult at the operational levels.
Raises
| Exception | When |
|---|---|
| ValueError | if this result carries no speech band levels, i.e. it was computed without the level-dependent corrections and its modulation transfer matrix holds nothing to undo. |
STIResult.plot()
Section titled “STIResult.plot()”STIResult.plot( ax: Axes | None = None, *, language: str = 'en', **kwargs: Any,) -> AxesPlot the per-band MTI bars with the STI and rating letter.
Requires matplotlib (pip install phonometry[plot]); returns the
Axes.
STIResult.report()
Section titled “STIResult.report()”STIResult.report( path: str, *, metadata: ReportMetadata | None = None, engine: str = 'reportlab', verbose: bool = False, language: str = 'en',) -> strRender an IEC 60268-16 speech-transmission-index fiche to a PDF.
Writes a one-page voice-alarm / public-address intelligibility
verification report: a standard-basis line stating the measurement
method (the full STI indirect method from an impulse response, or the
direct STIPA method on a recorded signal), an optional metadata header
block, a per-octave-band modulation transfer index table beside the
per-band MTI bars (the result’s own plot), the boxed
STI = X single number with the Annex F qualification band, an
optional verdict row and a footer with the fixed disclaimer.
Parameters
| Name | Description |
|---|---|
path | Destination path of the PDF file. |
metadata | Optional ReportMetadata; None produces a bare fiche (body, result and disclaimer only). A supplied requirement is read as the minimum required STI (a higher STI passes). |
engine | Rendering back end; only "reportlab" is supported. |
verbose | Accepted for a uniform signature; it has no effect on the single-layout STI fiche. |
language | Fiche language: "en" (default, English) or "es" (Spanish, with a comma decimal separator). |
Returns: The written path as a str.
Raises
| Exception | When |
|---|---|
| ValueError | If engine is not "reportlab" or language is not a supported language. |
| ImportError | If reportlab is not installed (pip install phonometry[report]), or matplotlib is missing for the embedded figure (pip install phonometry[plot]). |
STIWarning
Section titled “STIWarning”Warns about suspect STI/STIPA measurements or inputs.