Psychoacoustic annoyance and fluctuation strength
Key references: Fastl & Zwicker 2007Osses Vecchi et al. 2016
How annoying a sound is depends on more than how loud it is. Fastl & Zwicker, Psychoacoustics: Facts and Models, combine four psychoacoustic sensations (loudness, sharpness, roughness and fluctuation strength) into a single psychoacoustic annoyance , a scalar that grows with loudness and is lifted further when the sound is sharp, rough or slowly fluctuating. This page covers the exact model (Eqs 16.2–16.4), the fluctuation strength it consumes, both the closed form for amplitude-modulated broadband noise (Eq. 10.2) and the Osses et al. (2016) signal model, and the signal convenience that derives all four sensations from a recording.
Show the code for this figure
import matplotlib.pyplot as pltimport numpy as npfrom phonometry import psychoacoustics
n5 = np.linspace(4.0, 60.0, 200)profiles = [ ("Baseline: S = 1.75 acum, F = R = 0", 1.75, 0.0, 0.0), ("Sharp: S = 3.5 acum", 3.5, 0.0, 0.0), ("Rough + fluctuating: F = 1.2 vacil, R = 0.7 asper", 2.0, 1.2, 0.7),]fig, ax = plt.subplots()for label, s, f, r in profiles: pa = [psychoacoustics.psychoacoustic_annoyance(v, s, f, r).annoyance for v in n5] ax.plot(n5, pa, label=label)
ex = psychoacoustics.psychoacoustic_annoyance(30.0, 2.0, 0.5, 0.3)ax.plot([30.0], [ex.annoyance], "o", label=f"Worked example (PA = {ex.annoyance:.2f})")ax.set_xlabel("Percentile loudness N5 [sone]")ax.set_ylabel("Psychoacoustic annoyance PA")ax.legend()plt.show()1. The four sensations
Section titled “1. The four sensations”Psychoacoustic annoyance rests on four hearing sensations, each with its own model and unit in the library:
- Loudness: the percentile loudness (the loudness exceeded 5 % of the
time), in sone, from the ISO 532-1 Zwicker time-varying model
(
loudness_zwicker, see Loudness). - Sharpness , in acum: the spectral balance towards high
frequencies, DIN 45692 (
sharpness_din). - Roughness , in asper: the harshness of fast (~70 Hz) amplitude
modulation, ECMA-418-2 Sottek model (
roughness_ecma). - Fluctuation strength , in vacil: the sensation of slow (~4 Hz) loudness fluctuation (§3 below).
2. Psychoacoustic annoyance (Eqs 16.2–16.4)
Section titled “2. Psychoacoustic annoyance (Eqs 16.2–16.4)”The exact model (Fastl & Zwicker Eq. 16.2; origin Widmann 1992) scales by a factor that grows with the sharpness weighting and the combined roughness/fluctuation weighting :
is zero for (sharpness only adds annoyance above that threshold); weights roughness more heavily than fluctuation strength (0.6 vs 0.4). Those two choices are the whole of the modelling judgement in the formula, and they are easier to trust once seen:
A sound has to be sharper than the 1.75 acum reference before sharpness costs anything at all, and the fan above that point is the loudness dependence of . On the right, the same sensation magnitude buys about half again as much annoyance when it arrives as roughness rather than as fluctuation strength.
Show the code for this figure
import matplotlib.pyplot as pltimport numpy as np
# `psychoacoustics` is imported by the snippet above.sharpness = np.linspace(1.0, 5.0, 200)for n5 in (10.0, 30.0, 60.0): w_s = [psychoacoustics.psychoacoustic_annoyance(n5, float(s), 0.0, 0.0).w_s for s in sharpness] plt.plot(sharpness, w_s, label=f"N5 = {n5:g} sone")plt.axvline(1.75, linestyle="--")plt.xlabel("Sharpness S [acum]")plt.ylabel("Sharpness weighting wS")plt.legend()plt.show()
# The same magnitude spent on roughness or on fluctuation strength, at a# fixed N5 = 30 sone and S = 2.0 acum:rough = psychoacoustics.psychoacoustic_annoyance(30.0, 2.0, 0.0, 1.5).annoyancefluct = psychoacoustics.psychoacoustic_annoyance(30.0, 2.0, 1.5, 0.0).annoyanceprint(round(rough, 1), round(fluct, 1)) # 45.4 40.5psychoacoustic_annoyance takes the four quantities directly and
returns the annoyance together with the two intermediate weightings:
from phonometry import psychoacoustics
res = psychoacoustics.psychoacoustic_annoyance(30.0, 2.0, 0.5, 0.3) # N5, S, F, Rprint(round(res.annoyance, 4)) # 37.0477print(round(res.w_s, 4), round(res.w_fr, 4)) # 0.1001 0.2125
res.plot() # PA beside its wS and wFR weightings (needs matplotlib)What that number means. carries no unit and no criterion. It is built so that a neutral sound — sharpness at or below 1.75 acum, no roughness, no fluctuation — has a equal to its in sone, and every sharp, rough or fluctuating quality can only push it above that floor. So read as “the loudness this sound is worth once its character is taken into account”: the worked example’s 37.05 against an of 30 sone says the character costs 23 %. Comparison is the point — rank two designs of the same product, or quantify what a fix bought — and no limit exists to quote it against.
The figure above sweeps against for three profiles: a neutral baseline (, , so ), a sharp sound and a rough-and-fluctuating sound, both lifted above the baseline, with the worked example marked.
The model is small enough to draw whole: two weightings and one combination. The diagram traces the worked example through it, sensation by sensation.
2.1 From a signal (engineering estimate)
Section titled “2.1 From a signal (engineering estimate)”psychoacoustic_annoyance_from_signal is a convenience that derives all four
sensations from a calibrated pressure signal and combines them: from the
ISO 532-1 Zwicker time-varying loudness, from DIN 45692 sharpness,
from the ECMA-418-2 Sottek roughness and from the fluctuation-strength
signal model.
The convenience is only as good as the record it is given, and neither entry
point on this page takes a calibration argument, so x must already be
calibrated pressure in pascals — see
Measuring the input
on the loudness page for how that factor is obtained. Beyond calibration:
unweighted; one declared operating state rather than a run-up; long enough that
is a percentile of something and that the ~4 Hz fluctuation analysis has
enough modulation periods, which in practice means several seconds and the same
length for every design being compared; and field= matching how the sound was
captured. A two-second clip yields an that is nearly and an
biased low. A binaural or artificial-head recording is analysed one ear at a
time, already equalized to the free or diffuse field, and the louder channel is
the one usually reported.
import numpy as npfrom phonometry import psychoacoustics
# A raw recording plus its calibration so the guide runs standalone. There is# no calibration_factor argument here, so x must already be in pascals.fs = 48000t = np.arange(int(5.0 * fs)) / fsx = (1.0 + 0.8 * np.sin(2 * np.pi * 4.0 * t)) * np.sin(2 * np.pi * 1000 * t)x *= 2e-5 * 10 ** (70 / 20) / np.sqrt(np.mean(x**2)) # calibrated to 70 dB SPL
res = psychoacoustics.psychoacoustic_annoyance_from_signal(x, fs, field="free")print(f"PA = {res.annoyance:.1f} (N5 = {res.n5:.1f} sone, S = {res.sharpness:.2f} acum," f" R = {res.roughness:.2f} asper, F = {res.fluctuation_strength:.2f} vacil)")
res.plot() # the same PA / wS / wFR view, now from the four derived sensationsAll four sensations must come from the same record and the same operating state: PA is a combination rule, not a measurement in its own right, so computing over a minute and over two seconds of it silently mixes two different sounds. Quote each sensation with the model that produced it.
3. Fluctuation strength
Section titled “3. Fluctuation strength”Fluctuation strength (vacil) quantifies the perception of slow loudness fluctuation. Like roughness it is a band-pass sensation of the modulation frequency, but it peaks about an order of magnitude lower, at rather than the ~70 Hz roughness peak. By definition, a 1 kHz tone at 60 dB, 100 % amplitude-modulated at 4 Hz, produces 1 vacil. This page covers two of the three fluctuation-strength models in the library: the Fastl & Zwicker closed form for amplitude-modulated broadband noise (§3.1) and the Osses et al. (2016) signal model for arbitrary signals (§3.2). The normative Sottek-model fluctuation strength of ECMA-418-2 Clause 9, in vacil_HMS, lives in Sound Quality Metrics — note that this page’s unit is the vacil and that one the vacil_HMS, so the two are not the same number for the same sound.
The two models on the same stimulus, which is the comparison the caution box below turns into a rule. Both are band-passes centred near 4 Hz, but on AM broadband noise the signal model overshoots the closed form by about half again, because it accumulates modulation band by band across the whole Bark axis. On the AM tone it was calibrated for, right, it behaves.
Show the code for this figure
import matplotlib.pyplot as pltimport numpy as npfrom phonometry import psychoacoustics
# Left panel, the two models on the same stimulus: AM broadband noise at# 60 dB, 100 % modulation.fmod = np.logspace(np.log10(0.5), np.log10(32.0), 240)f_bbn = [psychoacoustics.fluctuation_strength_am_noise(60.0, 1.0, fm) for fm in fmod]
rng = np.random.default_rng(3)tn = np.arange(int(4.0 * fs)) / fsnoise = rng.standard_normal(tn.size)fm_noise = [0.5, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0]f_noise = []for fm in fm_noise: am = (1.0 + np.sin(2 * np.pi * fm * tn)) * noise am = am / np.sqrt(np.mean(am ** 2)) * 2e-5 * 10 ** (60 / 20) f_noise.append(psychoacoustics.fluctuation_strength(am, float(fs)).fluctuation_strength)print(round(f_noise[3], 1), round(f_bbn[np.argmin(np.abs(fmod - 4.0))], 1)) # 5.5 3.7
fig, ax = plt.subplots()ax.semilogx(fmod, f_bbn, label="closed form, Eq. 10.2")ax.semilogx(fm_noise, f_noise, "s--", label="Osses 2016 signal model")ax.axvline(4.0, ls="--", color="0.4")ax.set_xlabel("Modulation frequency f_mod [Hz]")ax.set_ylabel("Fluctuation strength F [vacil]")ax.legend(loc="upper right")plt.show()3.1 Closed form for AM broadband noise (Eq. 10.2)
Section titled “3.1 Closed form for AM broadband noise (Eq. 10.2)”For sinusoidally amplitude-modulated broadband noise, Fastl & Zwicker give a closed form (Eq. 10.2) in modulation factor , level and modulation frequency :
The denominator is the 4 Hz band-pass: it bottoms out near and rises on either side. The result is clamped at 0 (the sensation vanishes below ~20 dB or ). This exact form is the value to quote for AM broadband noise.
from phonometry import psychoacoustics
print(round(psychoacoustics.fluctuation_strength_am_noise(60.0, 1.0, 4.0), 4)) # 3.6943 vacil3.2 The Osses 2016 signal model
Section titled “3.2 The Osses 2016 signal model”fluctuation_strength implements the Osses et al. (2016) signal model: it builds
an excitation pattern over 47 auditory filters, extracts the ~4 Hz envelope
modulation per band, weights and combines it, and returns the overall
(vacil) plus the specific fluctuation strength over the Bark axis and the
time-dependent trace.
from phonometry import psychoacoustics
res = psychoacoustics.fluctuation_strength(x, fs)print(round(res.fluctuation_strength, 2)) # 0.65 vacil for the signal aboveres.plot() # specific fluctuation strength F′(z) over the Bark axis (needs matplotlib)Show the code for this figure
import matplotlib.pyplot as pltimport numpy as npfrom phonometry import psychoacoustics
# The reference-like stimulus: a 1 kHz tone at 70 dB SPL, 100 % amplitude# modulated at 4 Hz, where the sensation peaks.fs = 48000t = np.arange(int(2.0 * fs)) / fsam = (1.0 + np.sin(2 * np.pi * 4.0 * t)) * np.sin(2 * np.pi * 1000 * t)am = am / np.sqrt(np.mean(am ** 2)) * 2e-5 * 10 ** (70 / 20)res = psychoacoustics.fluctuation_strength(am, float(fs))print(round(res.fluctuation_strength, 2)) # 1.09 vacil
# One line: the specific fluctuation strength over the Bark axis.res.plot()plt.show()
# Or draw f'(z) by hand from the arrays the result carries:fig, ax = plt.subplots()ax.fill_between(res.bark_axis, res.specific, alpha=0.3)ax.plot(res.bark_axis, res.specific)ax.set_xlabel("Critical-band rate z [Bark]")ax.set_ylabel("Specific fluctuation strength [vacil/Bark]")plt.show()The sensation stays where the modulated energy is, in the critical bands around the carrier, which is exactly why the closed form of §3.1 and the signal model diverge for broadband modulated noise: there the modulation is spread over the whole Bark axis and the band-by-band model accumulates more of it than the single closed form allows.
What this guide covers
Section titled “What this guide covers”Covered
Fastl & Zwicker’s Psychoacoustics: Facts and Models gives the exact model, Eqs 16.2-16.4 (origin Widmann 1992), run by
psychoacoustic_annoyance. It also gives the closed form for AM broadband noise, Eq. 10.2, run byfluctuation_strength_am_noise. The Osses et al. (2016) signal model for fluctuation strength runs asfluctuation_strength, cross-checked against its Table 1 literature values and the SQAT reference.psychoacoustic_annoyance_from_signalderives the four inputs from a signal (ISO 532-1 Zwicker , DIN 45692 , ECMA-418-2 ) and combines them with the exact model.Not covered
The Osses 2016 model’s accuracy for FM tones is explicitly not pursued. Only AM stimuli are validated here. The front-end also departs from the paper’s exact framing (2 s frames, 90 % overlap, absolute-threshold gating). As a result, a steady 1 kHz tone reads about 0.09 vacil instead of 0, and steady broadband noise reads about 0.15-0.19 vacil. For AM broadband noise, quote
fluctuation_strength_am_noise(§3.1) instead offluctuation_strength, which overshoots that stimulus. The normative Sottek-model fluctuation strength of ECMA-418-2 Clause 9 is not implemented on this page: it lives in Sound Quality Metrics.
See also
Section titled “See also”- Loudness: the ISO 532-1 percentile loudness this model takes as its first argument, and the acquisition rules the recording has to satisfy before any of it applies.
- Sound Quality Metrics: the DIN 45692 sharpness , the ECMA-418-2 roughness and the normative vacil_HMS fluctuation strength of the same sensation this page models in vacil.
- Advanced Loudness: the alternative loudness models, and why mixing families makes the composite an engineering estimate.
- Environmental Levels: community annoyance, the other thing the word means, and the rating levels it is assessed with.
- Theory: Advanced loudness models and sound quality: the loudness and sound-quality metrics the annoyance model combines, derived one at a time.
- API reference:
psychoacoustics.quality.annoyanceandpsychoacoustics.quality.fluctuation_strength.
References
Section titled “References”- Fastl, H., & Zwicker, E. (2007). Psychoacoustics: Facts and models (3rd ed.). Springer. https://doi.org/10.1007/978-3-540-68888-4The source of the psychoacoustic-annoyance model of section 2 (Eqs 16.2-16.4, chapter 16; origin Widmann 1992) and of the closed-form fluctuation strength for AM broadband noise of section 3.1 (Eq. 10.2, chapter 10).
- Felix Greco, G., Merino-Martínez, R., Osses, A., & Lotinga, M. J. B. (2025). SQAT: A sound quality analysis toolbox for MATLAB. GitHub. https://doi.org/10.5281/zenodo.7934709The open MATLAB reference (open-source software) used as the numeric oracle for the fluctuation-strength cross-checks on this page.
- Osses Vecchi, A., García León, R., & Kohlrausch, A. (2016). Modelling the sensation of fluctuation strength. Proceedings of Meetings on Acoustics, 28, 050005. https://doi.org/10.1121/2.0000410The fluctuation-strength signal model implemented in section 3.2 (presented at ICA 2016; clean-room, with no numeric standard, calibrated to 1 vacil at the reference AM tone), including the Table 1 literature values used as its cross-check.