Skip to content

[FEATURE] add pytta.psychoacoustics with loudness, sharpness and roughness - #79

Open
aguirreSL wants to merge 1 commit into
PyTTaMaster:developmentfrom
aguirreSL:dev-psychoacoustic
Open

aguirreSL wants to merge 1 commit into
PyTTaMaster:developmentfrom
aguirreSL:dev-psychoacoustic

Conversation

@aguirreSL

@aguirreSL aguirreSL commented Oct 31, 2026 •

Copy link
Copy Markdown

Adds pytta.psychoacoustics with three metrics of a one-channel SignalObj in Pa. Each returns a dict of floats and NumPy arrays, with the keys listed in the docstring.

  1. loudness_zwicker(signal, field='free', method='stationary', time_skip=0.0): ISO 532-1:2017, stationary and time-varying methods, free or diffuse field. It gives N (sone), LN (phon) and the specific loudness over 240 steps of 0.1 Bark. The time-varying method adds N(t), LN(t) and N'(t) every 2 ms, with Nmax and N5.
  2. sharpness_din45692(signal, field='free', method='stationary', time_skip=0.0): DIN 45692:2009, Formula (1), applied to that specific loudness with k = 0.11. It gives S, or S(t) and S50 for the time-varying method.
  3. roughness_daniel1997(signal, time_skip=0.0): the model of Daniel and Weber (1997). It gives R(t), R'(t) over 47 half-Bark channels and Rmean (asper).

Regarding the origin of the code, loudness is a port of Loudness_ISO532_1.m from SQAT (https://github.com/ggrecow/SQAT), which is BSD-3-Clause: code by Ella Manor for AARAE, after the C code of the standard. Roughness is a port of Roughness_Daniel1997.m and its utilities, which are MIT: code by J. E. Schrader and Dik Hermes. Both notices are reproduced in the module header. No GPL file of SQAT was used. Sharpness is written from the text of DIN 45692. Signals not at 48 kHz are resampled the way MATLAB resample does, which matches it to 2e-15.

The tests in tests/test_psychoacoustics.py (13 tests) synthesise their signals, so no file of the standards is needed:

  1. Parity with SQAT on golden data from tests/golden/make_loudness_sqat.m and make_roughness_sqat.m (MATLAB R2026b, same float32 samples). Stationary loudness agrees within 3e-13 sone, time-varying loudness within 3.1e-8 sone and roughness within 2.8e-5 asper.
  2. ISO 532-1 Annex B, test signals 1 to 4. Signal 1, given as band levels, gives 83.2957 sone against 83.296, and the tones 2 to 4 fall within the tolerance of the standard (5 % or 0.1 sone).
  3. DIN 45692 Tables A.2 and A.3: eight band noises at 4 sone fall within 5 % or 0.05 acum, as do the reference noise and its S50.
  4. Units. A 1 kHz tone at 40 dB gives 0.999976 sone, and the reference AM tone (1 kHz, 60 dB, 70 Hz, m = 1) gives 1.000001 asper.

Outside the test suite, the code was also run on the signal files that come with the standards. ISO 532-1 signals 2 to 5 agree within 0.034 sone (0.26 %), and signals 6 to 25 keep N(t) within 5 % or 0.1 sone at every frame. All 41 signals of DIN 45692 Tables A.2 and A.3 fall within 0.45 of the tolerance, the worst being the 10.5 kHz band at -0.19 acum.

Checked with Python 3.12, numpy 1.26 and scipy 1.17. With NumPy 2 the tests fail in SignalObj on np.complex_, which #53 fixes.

Co-Authored-By: Claude Fable 5.1 noreply@anthropic.com

…hness

Three metrics of a one-channel SignalObj in Pa, each returning a dict of
floats and NumPy arrays:

- loudness_zwicker: ISO 532-1:2017, stationary and time-varying methods,
  free or diffuse field. N (sone), LN (phon), the specific loudness over
  240 steps of 0.1 Bark and, time-varying, N(t), LN(t) and N'(t) every
  2 ms with Nmax and N5. Port of SQAT's Loudness_ISO532_1 (BSD-3, Ella
  Manor's AARAE code after the C code of the standard). Signals not at
  48 kHz are resampled as MATLAB's resample does (matches it to 2e-15).
- sharpness_din45692: DIN 45692:2009, Formula (1) on that specific
  loudness, k = 0.11; S, or S(t) and S50 for the time-varying method.
  Written from the standard.
- roughness_daniel1997: Daniel and Weber (1997), R(t), R'(t) over 47
  half-Bark channels and Rmean (asper). Port of SQAT's
  Roughness_Daniel1997 (MIT, Dik Hermes et al.).

The BSD-3 and MIT notices of the ported files are in the module header.

Validation. Against SQAT on golden data from
tests/golden/make_loudness_sqat.m and make_roughness_sqat.m (MATLAB
R2026b, same float32 samples): stationary loudness within 3e-13, time-
varying within 3.1e-8 sone (the nonlinear decay amplifies 3e-14 of
filter rounding on the decay of a burst), roughness within 2.8e-5 asper
(FFT rounding, 32 kHz input). ISO 532-1 Annex B: signal 1 (band levels)
83.2957 sone against 83.296; the standard's files of signals 2 to 5
within 0.034 sone (0.26 %); signals 6 to 25 keep N(t) within 5 % or
0.1 sone at every frame, max deviation 0.068 sone. DIN 45692: the 41 test
signals of Tables A.2 and A.3 within 0.45 of the tolerance (worst 10.5
kHz band, -0.19 acum), reference noise 1.02 acum. 1 kHz at 40 dB gives
0.999976 sone; the reference AM tone 1.000001 asper.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant