Skip to content

[FEATURE] add pytta.ecma418 with tone-to-noise ratio and prominence ratio - #81

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

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

Conversation

@aguirreSL

@aguirreSL aguirreSL commented Oct 31, 2026 •

Copy link
Copy Markdown

Adds pytta.ecma418 with the two methods of ECMA-418-1:2024 for prominent discrete tones, the same methods as in ECMA-74 Annex D:

  1. tone_to_noise_ratio(signal, tone_frequency=None, resolution=None): tone-to-noise ratio (TNR), Clause 11 and Annex A.
  2. prominence_ratio(signal, tone_frequency=None, resolution=None): prominence ratio (PR), Clause 12 and Annex B.
  3. critical_band(frequency): the edges of the critical band, Clause 10, used by both.

With a tone frequency, each method returns a dict with the frequency, the ratio in dB, the criterion in dB and whether the tone is prominent (11.5 and 12.6). Without one, it searches the spectrum and returns one such dict per candidate. The spectrum is a Welch average with a Hann window, with a line spacing below 0.25 % of the tone frequency for TNR and below 1 % for PR (Clause 7). The implementation covers the bandwidth of Formula (2), the tone band of A.1, the masking noise of A.2, the primary and secondary tones within the proximity spacing of Formula (14), and the outer bands of Tables 2 and 3, with the lower band referred to 100 Hz below 171.4 Hz (B.8).

The standard leaves the identification of tones to the operator (8.2 c). In the search mode, a line is a candidate when it is the highest within 15 % of its critical band and at least 10 dB above the median of that band. The audibility checks of 11.8 and 12.8 are left out, since they need a calibrated level and a listener.

Regarding the text of the standard, four points are worth checking in review:

  1. In the copy used, the powers of ten of C_L,2 and C_U,2 in Tables 2 and 3 are lost. The values -6.90e-5, -8.20e-6, 7.70e-5 and 2.16e-5 were used, since with them Formulae (21) and (22) are continuous at 1600 Hz (1275.5 Hz and 2002.6 Hz from both sides) and f1,L is 20.0 Hz at 171.4 Hz, as 12.4 states.
  2. Formula (21) is printed as C_L,0 + C_L,0 f_t + C_L,2 f_t^2. The second term was read as C_L,1 f_t.
  3. Formula (A.7) is printed with X_t,p - X_t,s. The sum was used, as in Formula (15) and the text of A.6.
  4. Formula (23) is printed "for f_t <= 171.4 Hz". It was applied above 171.4 Hz, as the sentence before it says.

The tests in tests/test_ecma418.py print the measured differences:

  1. The worked values of the standard: the critical bandwidth at 1000 Hz is 162.217 Hz (162.2 in the standard), with band edges 922.18 Hz and 1084.39 Hz (922.2 and 1084.4). The bandwidth at 500 Hz is 117.255 Hz (117.3), and the proximity spacings at 150 Hz and 850 Hz are 23.00 Hz and 63.84 Hz (23 and 63.8).
  2. The criteria of 11.5 and 12.6 at 100, 500, 1000 and 5000 Hz, exact to 1e-3 dB.
  3. TNR of a tone in white noise against the analytic value 10 lg[(A^2/2) / (N0 Δfc)]: +0.025 dB at 300 Hz and +0.062 dB at 2 kHz.
  4. PR against the analytic band powers: -0.046 dB at 150 Hz, where the lower band rule of B.8 applies, and -0.056 dB at 1500 Hz.
  5. Two tones at 600 Hz and 625 Hz are combined, and 600 Hz and 545 Hz, beyond the proximity spacing of 40.7 Hz, are not. The expected values of the two cases are 1.6 dB apart.
  6. Pure noise gives no candidate and no prominent tone, and a 2 kHz tone in noise is found once, within 1 Hz.
  7. Invalid input is rejected.

No external implementation was found to compare with: SQAT and the ITA-Toolbox have no TNR or PR.

Checked with Python 3.12, numpy 1.26 and scipy 1.17. The 7 failures of development (3 in test_h5io.py, 4 in test_pyttasave.py) are the same with and without this change.

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

…atio

New module with the prominent discrete tone procedures of ECMA-418-1:2024
(also in ECMA-74, Annex D), written in the mean-square form of Annexes A and B.

tone_to_noise_ratio and prominence_ratio take a one-channel SignalObj and an
optional tone frequency. Without a frequency, the spectrum between 89.1 Hz and
11 200 Hz is searched for tone candidates. Each result gives the frequency, the
ratio in dB, the criterion in dB and whether the criterion is met. The spectrum
is a Welch average with a Hann window, with line spacing below 0.25 % of the
tone frequency for TNR and 1 % for PR (Clause 7).

Covered: critical band and its edges (Clause 10), tone band between minima
within 15 % of the critical band and tone power above the line joining them
(A.2), masking noise (A.3), primary and secondary tones with the proximity
spacing (A.6), lower and upper band edges from Tables 2 and 3, the 100 Hz
referral of the truncated lower band, and the criteria of 11.5 and 12.6.

Tests check the standard's examples for bandwidth, band edges and proximity
spacing, the criterion formulae, analytic TNR and PR of tones in white noise,
two tones in one critical band, the spectrum search on noise, and invalid
input.

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