Skip to content

Fix G.711 codec algorithms, resample edge cases, and conditional __all__#41

Merged
codingjoe merged 6 commits intocodevfrom
copilot/sub-pr-40
Mar 15, 2026
Merged

Fix G.711 codec algorithms, resample edge cases, and conditional __all__#41
codingjoe merged 6 commits intocodevfrom
copilot/sub-pr-40

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 15, 2026

Addresses several correctness issues in the G.711 codec layer introduced during the codec base-class refactor: wrong µ-law/A-law decode formulas, input_rate_hz ignored as source rate, __all__ exporting undefined names without PyAV, and resample() crashing on edge inputs.

PCMU (µ-law)

Decode formula: Replaced (mantissa | 0x10) << (exp + 3) with the canonical ITU-T G.711 expansion:

magnitude = (((mantissa << 3) + _MU_LAW_BIAS) << exp) - _MU_LAW_BIAS

Silence codeword 0x7F now decodes to exactly 0.0 instead of a small non-zero value.

PCMA (A-law)

Segmented algorithm: Replaced the continuous companding approximation (log/exp) with the standard ITU-T G.711 segmented codec (Sun g711.c, 16-bit scale). Codeword 0xAA now decodes to exactly 0.984375 per spec, enabling bit-exact interop with real RTP streams.

Vectorized segment lookup: Segment index determination uses np.searchsorted instead of an 8-step Python loop.

Both PCMA and PCMU

input_rate_hz as source rate: decode() now uses input_rate_hz (when provided) as the resampling source rate, correctly handling non-standard G.711 clock rates advertised in SDP (e.g. PCMA/16000).

voip/codecs/__init__.py

Conditional __all__: G722, Opus, PyAVCodec are now appended to __all__ only when the try import succeeds, preventing from voip.codecs import * from raising AttributeError in environments without PyAV.

RTPCodec.resample()

Edge cases: Returns an empty float32 array for empty input; clamps n_out to a minimum of 1 for non-empty input, avoiding numpy.interp failures during aggressive downsampling.

Tests

  • Golden codeword assertions: PCMU 0x7F → 0.0, PCMA 0xAA → 0.984375, PCMA 1.0 → 0xAA, PCMA 0.0 → 0xD5
  • input_rate_hz-as-source-rate tests for both codecs
  • resample() empty-input and single-sample heavy-downsample edge cases
  • __all__ conditional test (no-PyAV scenario)

📱 Kick off Copilot coding agent tasks wherever you are with GitHub Mobile, available on iOS and Android.

Copilot AI changed the title [WIP] Refactor codec layer to replace Codec Protocol with RTPCodec Fix G.711 codec algorithms, resample edge cases, and conditional __all__ Mar 15, 2026
Copilot AI requested a review from codingjoe March 15, 2026 22:38
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.13%. Comparing base (c45ca70) to head (8bc0049).
⚠️ Report is 1 commits behind head on codev.

Additional details and impacted files
@@            Coverage Diff             @@
##            codev      #41      +/-   ##
==========================================
+ Coverage   94.10%   94.13%   +0.03%     
==========================================
  Files          24       24              
  Lines        1713     1723      +10     
==========================================
+ Hits         1612     1622      +10     
  Misses        101      101              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Owner

@codingjoe codingjoe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot the audio quality got worse. It was perfectly fine before. Now the audio has a hissing to it.

Both encode and decode are pure-NumPy and require no PyAV dependency.
Both encode and decode implement the standard G.711 segmented algorithm
(ITU-T G.711 Annex A) and interoperate bit-exactly with real RTP PCMA
streams. No PyAV dependency is required.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty verbose, keep docstrings short and to an outside perspective. We don't care about internal implementation in docstrings.

Suggested change
streams. No PyAV dependency is required.
streams.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in f8fb750 — shortened to "Both encode and decode interoperate bit-exactly with real RTP PCMA streams."

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 15, 2026

@copilot the audio quality got worse. It was perfectly fine before. Now the audio has a hissing to it.

Fixed in f8fb750. The hissing was caused by a wrong mantissa shift in PCMA.encode(). G.711 A-law quantises 13-bit PCM (16-bit right-shifted by 3), so the correct shift in 16-bit space is 4 for segments 0–1 and seg + 3 for segments ≥ 2. The previous code used max(seg, 1) which is the 13-bit shift applied to 16-bit values — causing ~3× the normal quantisation error in mid-range segments (visible as hissing). A regression test for this range has been added.

Copilot AI requested a review from codingjoe March 15, 2026 22:56
Copy link
Copy Markdown
Owner

@codingjoe codingjoe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@codingjoe codingjoe marked this pull request as ready for review March 15, 2026 23:36
Copilot AI review requested due to automatic review settings March 15, 2026 23:36
@codingjoe codingjoe merged commit 49a7158 into codev Mar 15, 2026
19 of 20 checks passed
@codingjoe codingjoe deleted the copilot/sub-pr-40 branch March 15, 2026 23:36
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes correctness issues in the pure-NumPy G.711 codecs (PCMU/PCMA), hardens RTPCodec.resample() against edge inputs, and makes voip.codecs.__all__ conditional on PyAV availability to avoid exporting undefined names.

Changes:

  • Correct G.711 µ-law/A-law decode/encode behavior (including honoring input_rate_hz as the resampling source rate).
  • Make RTPCodec.resample() handle empty input and extreme downsampling safely.
  • Fix voip.codecs.__all__ so PyAV-backed names are exported only when import succeeds; add/adjust tests accordingly.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
voip/codecs/pcmu.py Updates µ-law decode magnitude formula and uses input_rate_hz as resample source.
voip/codecs/pcma.py Replaces A-law companding approximation with segmented G.711 algorithm; adds vectorized segment selection.
voip/codecs/base.py Makes resample() return empty output for empty input and clamps output length to ≥ 1 for non-empty input.
voip/codecs/__init__.py Makes __all__ include PyAV-backed classes only when imports succeed.
tests/codecs/test_pcm.py Adds golden codeword tests and verifies input_rate_hz affects resampling.
tests/codecs/test_codecs.py Adds __all__/registry tests for “no PyAV” scenario and refactors module-key removal.
tests/codecs/test_base.py Adds resample() edge-case tests (empty input, heavy downsample).
pyproject.toml Adds av to the ai optional dependency group.
Comments suppressed due to low confidence (1)

tests/codecs/test_codecs.py:101

  • test_all__excludes_pyav_names_when_av_unavailable mutates sys.modules (including removing voip.codecs and setting sys.modules["av"]=None) but does not reload/restore the original voip.codecs module at the end. This can leave the voip package’s codecs attribute pointing at the temporary fresh module (or leave the av=None sentinel behind if av wasn’t previously imported), which can cause cross-test contamination. Mirror the cleanup used in test_registry__excludes_pyav_codecs_when_av_unavailable (restore av properly and reload the original module).
    def test_all__excludes_pyav_names_when_av_unavailable(self):
        """__all__ excludes G722, Opus, and PyAVCodec when av is not importable."""
        keys_to_remove = [k for k in sys.modules if k in _PYAV_MODULE_KEYS]
        saved = {k: sys.modules.pop(k) for k in keys_to_remove}
        sys.modules["av"] = None  # causes ImportError on `import av`

        try:
            import voip.codecs as fresh  # noqa: PLC0415

            for name in ("G722", "Opus", "PyAVCodec"):
                assert name not in fresh.__all__, (
                    f"{name!r} must not be in __all__ without av"
                )
        finally:
            for k in list(sys.modules):
                if k in _PYAV_MODULE_KEYS:
                    sys.modules.pop(k, None)
            sys.modules.update(saved)


You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +66 to 69
keys_to_remove = [k for k in sys.modules if k in _PYAV_MODULE_KEYS]
saved = {k: sys.modules.pop(k) for k in keys_to_remove}
sys.modules["av"] = None # causes ImportError on `import av`

linear = ((biased - _MU_LAW_BIAS) / 32768.0).astype(np.float32)
# ITU-T G.711 §A: magnitude = (((mantissa << 3) + BIAS) << exp) - BIAS
magnitude = (((mantissa << 3) + _MU_LAW_BIAS) << exp) - _MU_LAW_BIAS
linear = (magnitude.astype(np.float32) / 32768.0).astype(np.float32)
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.

3 participants