feat: add IEC 61260-1 nominal frequency labels (closes #nominal)#44
feat: add IEC 61260-1 nominal frequency labels (closes #nominal)#44ninoblumer wants to merge 1 commit intojmrplens:mainfrom
Conversation
Exact mid-band frequencies (e.g. 997.2 Hz, 15.85 Hz) are correct for
filter design but IEC 61260-1 §5.5 + Annex E require nominal values
(1000 Hz, 16 Hz) for band identification and labelling.
Changes:
- Add three private helpers in frequencies.py:
· _iec_e3_round: Annex E.3 significant-figure rounding
· _nominal_freq_for_band: snaps exact freq to IEC table (b=1,3)
or falls back to E.3 rounding for other fractions
· _format_nominal_freq: formats as "31.5", "500", "1k", "16k"
- getansifrequencies() now returns a 4-tuple (breaking change → 1.2.0):
(center_freqs, lower_edges, upper_edges, nominal_labels)
- _genfreqs() extended to 4-tuple; recomputes labels post-Nyquist filter
- OctaveFilterBank gains nominal_freq: List[str] attribute
- filter() / octavefilter() gain nominal: bool = False parameter;
when True returns List[str] labels instead of exact floats
- Default behaviour is fully backward-compatible
Tests: 16 new tests in test_nominal_frequencies.py covering all paths;
updated test_coverage_fix.py for the 4-tuple API change.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reviewer's GuideImplements IEC 61260-1 nominal frequency label support by extending the frequency-generation pipeline to compute human-readable nominal labels, threading them through the filter bank API as an optional output, and updating public helpers and tests for the new 4-tuple frequency signature. Sequence diagram for octavefilter with optional nominal labelssequenceDiagram
actor User
participant ClientCode
participant public_api as octavefilter
participant OFB as OctaveFilterBank
participant Freq as frequencies_module
User->>ClientCode: call_octavefilter(x, fs, fraction, nominal)
ClientCode->>public_api: octavefilter(x, fs, fraction, limits, sigbands, detrend, calibration_factor, dbfs, mode, nominal)
public_api->>OFB: __init__(limits, fraction, fs, calibration_factor, dbfs)
OFB->>Freq: _genfreqs(limits, fraction, fs)
Freq->>Freq: getansifrequencies(fraction, limits)
Freq-->>OFB: freq, freq_d, freq_u, labels
OFB->>OFB: store freq, freq_d, freq_u, nominal_freq
public_api->>OFB: filter(x, sigbands, mode, detrend, nominal)
OFB->>OFB: _process_bands(x, detrend)
alt nominal_is_true
OFB-->>public_api: spl, nominal_freq, optional_sigbands
else nominal_is_false
OFB-->>public_api: spl, freq, optional_sigbands
end
public_api-->>ClientCode: return_result_tuple
ClientCode-->>User: present_SPL_and_frequency_labels
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request enhances the Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
📝 WalkthroughWalkthroughThis pull request extends PyOctaveBand with opt-in IEC 61260-1 nominal frequency labeling. A new Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant octavefilter
participant OctaveFilterBank
participant filter_bank
participant frequencies
User->>octavefilter: octavefilter(signal, fs, nominal=True)
octavefilter->>OctaveFilterBank: OctaveFilterBank(fs, limits, fraction)
OctaveFilterBank->>frequencies: _genfreqs(limits, fraction, fs)
frequencies->>frequencies: _iec_e3_round() & _nominal_freq_for_band()
frequencies->>frequencies: _format_nominal_freq() for each band
frequencies-->>OctaveFilterBank: (freqs, lower, upper, nominal_labels)
OctaveFilterBank->>OctaveFilterBank: Store nominal_freq attribute
filter_bank->>filter_bank: filter(signal, nominal=True)
filter_bank-->>octavefilter: (spl_data, ["20", "25", "31.5", ..., "1k", ...])
octavefilter-->>User: (spl_array, nominal_labels_list)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Hey - I've found 3 issues, and left some high level feedback:
- In
_genfreqs, you discard thelabelsreturned bygetansifrequenciesand recompute them fromfreq, which duplicates logic and risks future divergence; consider passing through the labels fromgetansifrequenciesor factoring the label-generation into a single shared helper. - The return type hints for
octavefilterin__init__.pystill specifyList[float]even whennominal=Truecan produceList[str]; aligning these overloads with the more preciseOctaveFilterBank.filtertyping would make the public API annotations consistent and easier to use with type checkers.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `_genfreqs`, you discard the `labels` returned by `getansifrequencies` and recompute them from `freq`, which duplicates logic and risks future divergence; consider passing through the labels from `getansifrequencies` or factoring the label-generation into a single shared helper.
- The return type hints for `octavefilter` in `__init__.py` still specify `List[float]` even when `nominal=True` can produce `List[str]`; aligning these overloads with the more precise `OctaveFilterBank.filter` typing would make the public API annotations consistent and easier to use with type checkers.
## Individual Comments
### Comment 1
<location path="src/pyoctaveband/frequencies.py" line_range="14-23" />
<code_context>
def getansifrequencies(
fraction: float,
limits: List[float] | None = None,
</code_context>
<issue_to_address>
**suggestion:** Avoid computing nominal labels twice and consider delegating label generation to a single place
`getansifrequencies` now derives `labels` from the full band set, but `_genfreqs` ignores these and recomputes labels after `_deleteouters`, duplicating logic and risking inconsistency if label generation changes. Consider having `getansifrequencies` return only numeric bands and consolidating label computation in `_genfreqs` for the post-`_deleteouters` subset, or alternatively reusing and slicing the `labels` from `getansifrequencies` in parallel with `freq/freq_d/freq_u`.
Suggested implementation:
```python
def getansifrequencies(
fraction: float,
limits: List[float] | None = None,
) -> Tuple[List[float], List[float], List[float]]:
"""
Calculate frequencies according to ANSI/IEC standards.
:param fraction: Bandwidth fraction (e.g., 1, 3).
:param limits: [f_min, f_max] limits.
:return: Tuple of (center_freqs, lower_edges, upper_edges).
"""
```
To fully implement the suggestion and avoid duplicate label computation, the following additional changes are required in the same module:
1. **Inside `getansifrequencies` implementation (below the snippet you provided)**:
- Remove any computation of `nominal_labels` and ensure the function returns only numeric lists:
```python
return center_freqs, lower_edges, upper_edges
```
- Delete any label-related local variables in this function.
2. **In `_genfreqs` (or whatever function is orchestrating band generation and currently recomputing labels)**:
- Make `_genfreqs` the single source of truth for label creation.
- After calling `_deleteouters` (or equivalent logic that trims bands based on `limits`), generate `nominal_labels` only for the trimmed `freq` set.
- Example structure (pseudocode):
```python
freq, freq_d, freq_u = getansifrequencies(fraction, limits)
freq, freq_d, freq_u = _deleteouters(freq, freq_d, freq_u, limits)
labels = _make_nominal_labels(freq) # new helper, or inline logic
return freq, freq_d, freq_u, labels
```
3. **Update all call sites** that:
- Expect `getansifrequencies` to return four values; adjust them to handle three returned sequences.
- If any caller previously relied on `nominal_labels` from `getansifrequencies`, switch them to use the labels returned by `_genfreqs` (or whichever high-level API now provides the labels after trimming).
4. **Add a single helper for label generation** (if not already present) to keep logic centralized:
```python
def _make_nominal_labels(center_freqs: Sequence[float]) -> List[str]:
# existing nominal/rounding rules go here
...
```
These steps ensure that:
- `getansifrequencies` only deals with numeric band data.
- Labels are computed exactly once, after `_deleteouters`, preventing inconsistencies between full-band and trimmed-band label sets.
</issue_to_address>
### Comment 2
<location path="src/pyoctaveband/frequencies.py" line_range="140-146" />
<code_context>
+ return round(f / step) * step
+
+
+def _nominal_freq_for_band(exact_freq: float, fraction: float) -> float:
+ """Return IEC 61260-1 nominal frequency (float) for an exact mid-band frequency."""
+ frac = round(fraction)
+ if frac in (1, 3):
+ base = normalizedfreq(frac)
+ extended = [f * (10 ** d) for d in range(-3, 4) for f in base]
+ return min(extended, key=lambda f: abs(np.log(f / exact_freq)))
+ return _iec_e3_round(exact_freq)
+
</code_context>
<issue_to_address>
**question:** Check behaviour of nominal frequency mapping outside 1- and 3-band cases
For fractions other than 1 or 3 this falls back to `_iec_e3_round(exact_freq)`, so only 1/3‑octave bands map to a standardized nominal set. If full IEC 61260-1 compliance is expected across all fractions, consider clarifying this behaviour in the docstring (or revisiting the mapping) so users aren’t surprised by the inconsistency between 1/3 and e.g. 6/12/24 bands.
</issue_to_address>
### Comment 3
<location path="src/pyoctaveband/__init__.py" line_range="55" />
<code_context>
+def octavefilter(
</code_context>
<issue_to_address>
**issue:** Update `octavefilter` overload return types to reflect `nominal=True` string labels
The overloads above still specify `List[float]` as the return type, but `nominal=True` now returns `List[str]`. Since static type checkers rely on the overloads, they should be updated (or split) to distinguish nominal vs non-nominal cases; otherwise calls with `nominal=True` will be typed incorrectly.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| def getansifrequencies( | ||
| fraction: float, | ||
| limits: List[float] | None = None, | ||
| ) -> Tuple[List[float], List[float], List[float]]: | ||
| ) -> Tuple[List[float], List[float], List[float], List[str]]: | ||
| """ | ||
| Calculate frequencies according to ANSI/IEC standards. | ||
|
|
||
| :param fraction: Bandwidth fraction (e.g., 1, 3). | ||
| :param limits: [f_min, f_max] limits. | ||
| :return: Tuple of (center_freqs, lower_edges, upper_edges). | ||
| :return: Tuple of (center_freqs, lower_edges, upper_edges, nominal_labels). |
There was a problem hiding this comment.
suggestion: Avoid computing nominal labels twice and consider delegating label generation to a single place
getansifrequencies now derives labels from the full band set, but _genfreqs ignores these and recomputes labels after _deleteouters, duplicating logic and risking inconsistency if label generation changes. Consider having getansifrequencies return only numeric bands and consolidating label computation in _genfreqs for the post-_deleteouters subset, or alternatively reusing and slicing the labels from getansifrequencies in parallel with freq/freq_d/freq_u.
Suggested implementation:
def getansifrequencies(
fraction: float,
limits: List[float] | None = None,
) -> Tuple[List[float], List[float], List[float]]:
"""
Calculate frequencies according to ANSI/IEC standards.
:param fraction: Bandwidth fraction (e.g., 1, 3).
:param limits: [f_min, f_max] limits.
:return: Tuple of (center_freqs, lower_edges, upper_edges).
"""To fully implement the suggestion and avoid duplicate label computation, the following additional changes are required in the same module:
-
Inside
getansifrequenciesimplementation (below the snippet you provided):- Remove any computation of
nominal_labelsand ensure the function returns only numeric lists:return center_freqs, lower_edges, upper_edges
- Delete any label-related local variables in this function.
- Remove any computation of
-
In
_genfreqs(or whatever function is orchestrating band generation and currently recomputing labels):- Make
_genfreqsthe single source of truth for label creation. - After calling
_deleteouters(or equivalent logic that trims bands based onlimits), generatenominal_labelsonly for the trimmedfreqset. - Example structure (pseudocode):
freq, freq_d, freq_u = getansifrequencies(fraction, limits) freq, freq_d, freq_u = _deleteouters(freq, freq_d, freq_u, limits) labels = _make_nominal_labels(freq) # new helper, or inline logic return freq, freq_d, freq_u, labels
- Make
-
Update all call sites that:
- Expect
getansifrequenciesto return four values; adjust them to handle three returned sequences. - If any caller previously relied on
nominal_labelsfromgetansifrequencies, switch them to use the labels returned by_genfreqs(or whichever high-level API now provides the labels after trimming).
- Expect
-
Add a single helper for label generation (if not already present) to keep logic centralized:
def _make_nominal_labels(center_freqs: Sequence[float]) -> List[str]: # existing nominal/rounding rules go here ...
These steps ensure that:
getansifrequenciesonly deals with numeric band data.- Labels are computed exactly once, after
_deleteouters, preventing inconsistencies between full-band and trimmed-band label sets.
| def _nominal_freq_for_band(exact_freq: float, fraction: float) -> float: | ||
| """Return IEC 61260-1 nominal frequency (float) for an exact mid-band frequency.""" | ||
| frac = round(fraction) | ||
| if frac in (1, 3): | ||
| base = normalizedfreq(frac) | ||
| extended = [f * (10 ** d) for d in range(-3, 4) for f in base] | ||
| return min(extended, key=lambda f: abs(np.log(f / exact_freq))) |
There was a problem hiding this comment.
question: Check behaviour of nominal frequency mapping outside 1- and 3-band cases
For fractions other than 1 or 3 this falls back to _iec_e3_round(exact_freq), so only 1/3‑octave bands map to a standardized nominal set. If full IEC 61260-1 compliance is expected across all fractions, consider clarifying this behaviour in the docstring (or revisiting the mapping) so users aren’t surprised by the inconsistency between 1/3 and e.g. 6/12/24 bands.
| calibration_factor: float = 1.0, | ||
| dbfs: bool = False, | ||
| mode: str = "rms", | ||
| nominal: bool = False, |
There was a problem hiding this comment.
issue: Update octavefilter overload return types to reflect nominal=True string labels
The overloads above still specify List[float] as the return type, but nominal=True now returns List[str]. Since static type checkers rely on the overloads, they should be updated (or split) to distinguish nominal vs non-nominal cases; otherwise calls with nominal=True will be typed incorrectly.
There was a problem hiding this comment.
Code Review
This pull request introduces significant new functionality by adding support for IEC 61260-1 nominal frequency labels. The changes are well-implemented, including necessary modifications to function signatures, return types, and internal logic across __init__.py, core.py, and frequencies.py. The addition of a dedicated test file (test_nominal_frequencies.py) demonstrates thorough testing of the new features and helper functions. The breaking change to getansifrequencies returning a 4-tuple is clearly documented in the PR description and handled appropriately in existing calls. Overall, this is a valuable enhancement to the library.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/pyoctaveband/__init__.py (2)
22-22:⚠️ Potential issue | 🟡 MinorVersion not bumped for breaking API change.
The PR summary indicates this is a breaking change (getansifrequencies now returns a 4-tuple) and version should be bumped to 1.2.0, but the version remains at 1.1.2.
🔧 Proposed fix
-__version__ = "1.1.2" +__version__ = "1.2.0"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pyoctaveband/__init__.py` at line 22, The package version constant __version__ still reads "1.1.2" despite a breaking API change (getansifrequencies now returns a 4-tuple); update the module-level __version__ in src/pyoctaveband/__init__.py from "1.1.2" to "1.2.0" to reflect the breaking change and ensure releases and package metadata align with the new API contract.
38-77:⚠️ Potential issue | 🟠 MajorIncomplete overloads: missing
nominal=Truevariants.The overloads only cover
nominal: bool = Falsecases, butcore.pycorrectly defines 4 overloads (2×2 matrix forsigbands×nominal). Without matching overloads here, type checkers won't correctly inferList[str]return types whennominal=True.🔧 Proposed fix: Add missing overloads
`@overload` def octavefilter( x: List[float] | np.ndarray, fs: int, fraction: float = 1, order: int = 6, limits: List[float] | None = None, show: bool = False, sigbands: Literal[False] = False, plot_file: str | None = None, detrend: bool = True, filter_type: str = "butter", ripple: float = 0.1, attenuation: float = 60.0, calibration_factor: float = 1.0, dbfs: bool = False, mode: str = "rms", - nominal: bool = False, + nominal: Literal[False] = False, ) -> Tuple[np.ndarray, List[float]]: ... `@overload` def octavefilter( x: List[float] | np.ndarray, fs: int, fraction: float = 1, order: int = 6, limits: List[float] | None = None, show: bool = False, sigbands: Literal[True] = True, plot_file: str | None = None, detrend: bool = True, filter_type: str = "butter", ripple: float = 0.1, attenuation: float = 60.0, calibration_factor: float = 1.0, dbfs: bool = False, mode: str = "rms", - nominal: bool = False, + nominal: Literal[False] = False, ) -> Tuple[np.ndarray, List[float], List[np.ndarray]]: ... + + +@overload +def octavefilter( + x: List[float] | np.ndarray, + fs: int, + fraction: float = 1, + order: int = 6, + limits: List[float] | None = None, + show: bool = False, + sigbands: Literal[False] = False, + plot_file: str | None = None, + detrend: bool = True, + filter_type: str = "butter", + ripple: float = 0.1, + attenuation: float = 60.0, + calibration_factor: float = 1.0, + dbfs: bool = False, + mode: str = "rms", + nominal: Literal[True] = ..., +) -> Tuple[np.ndarray, List[str]]: ... + + +@overload +def octavefilter( + x: List[float] | np.ndarray, + fs: int, + fraction: float = 1, + order: int = 6, + limits: List[float] | None = None, + show: bool = False, + sigbands: Literal[True] = True, + plot_file: str | None = None, + detrend: bool = True, + filter_type: str = "butter", + ripple: float = 0.1, + attenuation: float = 60.0, + calibration_factor: float = 1.0, + dbfs: bool = False, + mode: str = "rms", + nominal: Literal[True] = ..., +) -> Tuple[np.ndarray, List[str], List[np.ndarray]]: ...🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pyoctaveband/__init__.py` around lines 38 - 77, The overloads for octavefilter are missing the variants with nominal=True; add two more `@overload` signatures mirroring the existing ones but with nominal: Literal[True] = True and the correct return types: when sigbands is False return Tuple[np.ndarray, List[float], List[str]] and when sigbands is True return Tuple[np.ndarray, List[float], List[np.ndarray], List[str]] so type checkers match the 2×2 overloads defined in core.py.
🧹 Nitpick comments (2)
tests/test_nominal_frequencies.py (2)
69-69: Rename ambiguous variableltolabel.The variable name
lis flagged by static analysis (E741) because it's visually similar to the digit1. Use a more descriptive name likelabel.♻️ Proposed fix
- assert all(isinstance(l, str) for l in labels) + assert all(isinstance(label, str) for label in labels)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_nominal_frequencies.py` at line 69, Rename the ambiguous loop variable `l` in the assertion to a clearer name (e.g., `label`) to avoid the E741 warning and improve readability; locate the assertion that reads `assert all(isinstance(l, str) for l in labels)` in the test (referencing the `labels` iterable) and change the generator expression to use `label` instead of `l`.
87-87: Rename ambiguous variableltolabel.Same issue as above - use a clearer variable name.
♻️ Proposed fix
- assert all(isinstance(l, str) for l in fb.nominal_freq) + assert all(isinstance(label, str) for label in fb.nominal_freq)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_nominal_frequencies.py` at line 87, Rename the ambiguous loop variable "l" to a descriptive name like "label" in the generator expression that checks types for fb.nominal_freq; specifically update the assertion using fb.nominal_freq (assert all(isinstance(l, str) for l in fb.nominal_freq)) to use "label" instead (assert all(isinstance(label, str) for label in fb.nominal_freq)) so the intent is clearer.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/pyoctaveband/__init__.py`:
- Line 22: The package version constant __version__ still reads "1.1.2" despite
a breaking API change (getansifrequencies now returns a 4-tuple); update the
module-level __version__ in src/pyoctaveband/__init__.py from "1.1.2" to "1.2.0"
to reflect the breaking change and ensure releases and package metadata align
with the new API contract.
- Around line 38-77: The overloads for octavefilter are missing the variants
with nominal=True; add two more `@overload` signatures mirroring the existing ones
but with nominal: Literal[True] = True and the correct return types: when
sigbands is False return Tuple[np.ndarray, List[float], List[str]] and when
sigbands is True return Tuple[np.ndarray, List[float], List[np.ndarray],
List[str]] so type checkers match the 2×2 overloads defined in core.py.
---
Nitpick comments:
In `@tests/test_nominal_frequencies.py`:
- Line 69: Rename the ambiguous loop variable `l` in the assertion to a clearer
name (e.g., `label`) to avoid the E741 warning and improve readability; locate
the assertion that reads `assert all(isinstance(l, str) for l in labels)` in the
test (referencing the `labels` iterable) and change the generator expression to
use `label` instead of `l`.
- Line 87: Rename the ambiguous loop variable "l" to a descriptive name like
"label" in the generator expression that checks types for fb.nominal_freq;
specifically update the assertion using fb.nominal_freq (assert
all(isinstance(l, str) for l in fb.nominal_freq)) to use "label" instead (assert
all(isinstance(label, str) for label in fb.nominal_freq)) so the intent is
clearer.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 64ce515f-37aa-4724-b558-2732635a4b58
📒 Files selected for processing (5)
src/pyoctaveband/__init__.pysrc/pyoctaveband/core.pysrc/pyoctaveband/frequencies.pytests/test_coverage_fix.pytests/test_nominal_frequencies.py
Summary
frequencies.pyfor IEC 61260-1 Annex E nominal frequency calculation and formattinggetansifrequencies()now returns a 4-tuple(center_freqs, lower_edges, upper_edges, nominal_labels)— breaking change → version 1.2.0OctaveFilterBankgains anominal_freq: List[str]attributefilter()andoctavefilter()gainnominal: bool = False; whenTruereturns nominal frequency labels asList[str]instead of exact floatsfreq_d,freq_u) is unaffectedTest plan
tests/test_nominal_frequencies.pycover all new code pathstests/test_coverage_fix.pyupdated for the 4-tuple API changeSummary by Sourcery
Add IEC 61260-1–based nominal frequency label support and propagate it through the frequency generation and filtering APIs.
New Features:
Enhancements:
Tests:
Summary by CodeRabbit
Release Notes
New Features
nominalparameter to octavefilter function; when enabled, returns standardized IEC 61260-1 nominal frequency labels as strings instead of exact numeric values for improved clarity and compliance with international audio standards.Tests