# Vendored packages

## ear — EBU ADM Renderer

- Upstream: https://github.com/ebu/ebu_adm_renderer
- Pinned commit: `5bb17b4278e8e67f24f90efb7a81a8aea7aed4f3` (master, 2026-02-11)
- License: BSD-3-Clause-Clear (`ear/LICENSE`)

EAR is the reference implementation of ITU-R BS.2127 (the ITU distribution is
the EAR codebase renamed "iar"). We vendor it because no upstream release or
fork supports NumPy 2 / Python 3.14 — upstream pins `numpy~=1.14`, which has no
cp314 wheels. The `numpy>=2,<3` override in this worker's `pyproject.toml` is
ours to own; it was validated by running EAR's full test suite (742 tests) on
Python 3.14 + NumPy 2 (CDAM-3806 port gate, 2026-06-11).

### Why we use it as a library instead of the CLI, and why we vendor it instead of installing

We use EAR as a library rather than its `ear/cmdline/render_file.py` CLI for two
reasons — neither of which required modifying EAR:

- **Streaming.** We render directly from S3 with no local file. The CLI is
  file-to-file (`openBw64Adm` / `openBw64` open a path on disk for both input and
  output, `ear/fileio/utils.py`), but the underlying `Bw64Reader` /
  `Bw64AdmReader` constructors take any seekable binary file-like object.
  `src/atmos/render.py` builds `Bw64AdmReader(Bw64Reader(handle))` on the
  `smart_open` S3 handle (`open_s3_seekable` in `src/clients/s3.py`, outside this
  tree), bypassing the path helper, and consumes the render block by block.
- **Dolby pre-fixes.** Real Dolby ADM Profile masters need two structural fixes
  (`src/atmos/fix_dolby.py`, also outside this tree) applied to the parsed ADM
  before `adm.validate()` passes. The CLI validates then renders in one shot with
  no hook to inject them, so it would reject those masters outright.

Both run against byte-for-byte upstream `ear/fileio/` — the only vendor edits are
the two NumPy-2 patches below. So vendoring is purely the NumPy 2 / Python 3.14
incompatibility above; streaming and the pre-fixes survive a re-vendor untouched.

### Local patches

Both fix NEP-51 scalar-repr changes under NumPy 2 (marked `CDAM-3806 vendor patch`):

1. `ear/core/layout.py` (`Layout.check_upmix_matrix`): cast `np.nonzero` outputs
   to `int` before message formatting, so the user-facing warning reads
   `[0, 2]` rather than `[np.int64(0), np.int64(2)]`.
2. `ear/common.py` (`azimuth` doctest): expected value `0` → `np.int64(0)`.

### Exclusions

- All `test/` directories (`ear/test/`, `ear/core/test/`, `ear/fileio/bw64/test/`,
  …) and `ear/conftest.py` — pytest-only, never imported at runtime.
  `ear/test/data/test_bwf.wav` is kept as `tests/fixtures/test_bwf.wav` for the
  render tests.

### Import mechanism

The vendored package keeps its upstream absolute `ear.*` imports.
`src/__init__.py` prepends `src/vendor` to `sys.path` (front, deduped), so
`import ear` resolves to this copy — taking precedence over any installed
`ear` — without rewriting vendor code. `src/vendor/**` is excluded from
ruff, mypy, and coverage (`pyproject.toml`); only the worker's own code is held
to the strict toolchain.

### Re-vendoring

1. `git clone https://github.com/ebu/ebu_adm_renderer && git -C ebu_adm_renderer checkout <new-commit>`
2. `rsync -a --delete --exclude='test/' --exclude='conftest.py' --exclude='__pycache__/' ebu_adm_renderer/ear/ src/vendor/ear/`
3. `cp ebu_adm_renderer/LICENSE src/vendor/ear/LICENSE`
4. Re-apply the local patches above (or drop any that upstream has fixed).
5. Diff upstream's `pyproject.toml` dependencies against this worker's and
   reconcile (upstream's `numpy~=1.14` stays overridden to `numpy>=2,<3`).
6. Run EAR's own test suite on the worker's Python/NumPy stack before trusting
   the bump: `pip install './ebu_adm_renderer[test]' 'numpy>=2,<3' 'pytest>=8.4' && pytest --doctest-modules --pyargs ear`.
7. Update the pinned commit in this file.
