diff --git a/docs/upstream-reports/prs/flanger-int32-overflow/flanger_dump.py b/docs/upstream-reports/prs/flanger-int32-overflow/flanger_dump.py index 41954db5..f6bbd069 100644 --- a/docs/upstream-reports/prs/flanger-int32-overflow/flanger_dump.py +++ b/docs/upstream-reports/prs/flanger-int32-overflow/flanger_dump.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + # Dump a Flanger render, one sample a line, for comparing two builds. # micropython flanger_dump.py rails > rails.txt import sys diff --git a/docs/upstream-reports/prs/flanger-int32-overflow/flanger_overflow.py b/docs/upstream-reports/prs/flanger-int32-overflow/flanger_overflow.py index 1f2df7ee..2be9d5b2 100644 --- a/docs/upstream-reports/prs/flanger-int32-overflow/flanger_overflow.py +++ b/docs/upstream-reports/prs/flanger-int32-overflow/flanger_overflow.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + # The Flanger's wet tap interpolates between two int16 taps: # # int32_t wet = s0 + (((s1 - s0) * (int32_t)delay_frac) >> 16); diff --git a/docs/upstream-reports/prs/speedchanger-phase-carry/edge_cases.py b/docs/upstream-reports/prs/speedchanger-phase-carry/edge_cases.py index b5728131..8b2a68c2 100644 --- a/docs/upstream-reports/prs/speedchanger-phase-carry/edge_cases.py +++ b/docs/upstream-reports/prs/speedchanger-phase-carry/edge_cases.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + # Guards for the carry: the stream must still end, a carry larger than a whole # source buffer must not read out of one, and a Resampler's bound rate must # still be the ratio it was asked for. diff --git a/docs/upstream-reports/prs/speedchanger-phase-carry/phase_carry.py b/docs/upstream-reports/prs/speedchanger-phase-carry/phase_carry.py index 89716698..508cb739 100644 --- a/docs/upstream-reports/prs/speedchanger-phase-carry/phase_carry.py +++ b/docs/upstream-reports/prs/speedchanger-phase-carry/phase_carry.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + # What a SpeedChanger renders should not depend on how the node above it # chunks its output. The phase accumulator counts source frames since the # stream began, so a source buffer boundary is not frame zero. diff --git a/docs/upstream-reports/prs/speedchanger-rate-rounding/identity_pair.py b/docs/upstream-reports/prs/speedchanger-rate-rounding/identity_pair.py index faade201..d7ecb677 100644 --- a/docs/upstream-reports/prs/speedchanger-rate-rounding/identity_pair.py +++ b/docs/upstream-reports/prs/speedchanger-rate-rounding/identity_pair.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + # The consequence of the lost LSB: a SpeedChanger pair asked to cancel -- # decimate at N, restore at 1/N -- is not an identity when 1/N truncates. # N = 1.0000000000000004 is what fs / rate_hz returns for a log-mapped rate diff --git a/docs/upstream-reports/prs/speedchanger-rate-rounding/rate_rounding.py b/docs/upstream-reports/prs/speedchanger-rate-rounding/rate_rounding.py index 809c169d..2dc62ae8 100644 --- a/docs/upstream-reports/prs/speedchanger-rate-rounding/rate_rounding.py +++ b/docs/upstream-reports/prs/speedchanger-rate-rounding/rate_rounding.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + # A rate is stored in 16.16, so `rate` should read back within half a step of # what was asked for. Truncating instead of rounding costs a whole step. # diff --git a/docs/upstream-reports/prs/synthio-note-repress-zero-level/sustain0-repro.py b/docs/upstream-reports/prs/synthio-note-repress-zero-level/sustain0-repro.py index ed6e3aad..a5217f0b 100644 --- a/docs/upstream-reports/prs/synthio-note-repress-zero-level/sustain0-repro.py +++ b/docs/upstream-reports/prs/synthio-note-repress-zero-level/sustain0-repro.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + import synthio, audiocore try: from ulab import numpy as np diff --git a/lib/audiodsp_util/__init__.py b/lib/audiodsp_util/__init__.py index b1f0c01a..871ee3e9 100644 --- a/lib/audiodsp_util/__init__.py +++ b/lib/audiodsp_util/__init__.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """One number, the same number, on every target audiodsp runs on. ``import audiodsp_util`` -- a pure-Python tier with no dependencies beyond @@ -22,18 +26,18 @@ `docs/how-it-works/correctness-standard.md` holds our nodes to every target rendering them identically, so a setting derived in Python arithmetic passes through -:func:`float32` before it reaches a node. On a single-precision target that +`float32` before it reaches a node. On a single-precision target that call is the identity. On a double one it rounds to the value the board would have held. Both then agree, which is the whole point. The same rule is what audiocomponents#75 needs on the class side: a class computing ``360 * 4 ** (macro / 127)`` for a filter frequency is deriving a setting in Python, and its board and its desktop land a ULP apart until that -derivation ends in :func:`float32`. +derivation ends in `float32`. ## And why printing needs its own function -:func:`float32_bits` is for output rather than settings. ``"%.6f" % value`` +`float32_bits` is for output rather than settings. ``"%.6f" % value`` is not one string across interpreters even when ``value`` is bit-for-bit the same float32: six decimals of a single-precision number is seven significant digits, and MicroPython's single-precision formatter is not correctly diff --git a/lib/audiorender/__init__.py b/lib/audiorender/__init__.py index c40ad6ed..ec3ffa35 100644 --- a/lib/audiorender/__init__.py +++ b/lib/audiorender/__init__.py @@ -1,6 +1,10 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Render a whole composition offline, through audiodsp's own DSP. -This is the tier above :mod:`audioinstruments` and :mod:`audioeffects`: it +This is the tier above `audioinstruments` and `audioeffects`: it knows what a *piece* is - tracks, a tempo map, notes, automation, sections - and turns one into a mixed stereo master plus the level report that master is judged by. It is deliberately offline and CPython-shaped (numpy @@ -33,9 +37,9 @@ Loading a track's sound is the caller's job, not this package's - a plug-in host has its own script loader and its own reasons to keep it. Pass -:func:`render` a ``voice_for`` that returns a :class:`~.voices.Voice` -around an :mod:`audioinstruments` instrument, or anything else that speaks -the small protocol in :mod:`.voices`. +`render` a ``voice_for`` that returns a `Voice <.voices.Voice>` +around an `audioinstruments` instrument, or anything else that speaks +the small protocol in `.voices`. from audiorender import Clock, Voice, render, report, write_wav diff --git a/lib/audiorender/events.py b/lib/audiorender/events.py index 6fdd8a75..bbdbdf73 100644 --- a/lib/audiorender/events.py +++ b/lib/audiorender/events.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Turn a track's notes and automation into a time-ordered event list. Events are ``(sample_position, kind, data, value)`` where `kind` is one of diff --git a/lib/audiorender/metrics.py b/lib/audiorender/metrics.py index ee1cb365..248a68da 100644 --- a/lib/audiorender/metrics.py +++ b/lib/audiorender/metrics.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """What a finished render is worth looking at. Levels rather than waveforms: which track carries which section, whether diff --git a/lib/audiorender/render.py b/lib/audiorender/render.py index b9885751..4092aec5 100644 --- a/lib/audiorender/render.py +++ b/lib/audiorender/render.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """The block render loop: a composition in, a mixed master out. Rendering happens a block at a time, and every block does the same three @@ -44,10 +48,10 @@ def render_track(track, composition, tempo, voice, total_frames, patch=None, effects=(), clock=None, block=BLOCK): """One track's audio as float32 ``(total_frames, 2)`` in -1.0..1.0. - `voice` is anything implementing the protocol in :mod:`.voices`, and + `voice` is anything implementing the protocol in `.voices`, and `effects` are callables taking the rendered track as int16 bytes and returning something to pull the processed track from - a rack built - from :mod:`audioeffects`, or a host's own effect runner. + from `audioeffects`, or a host's own effect runner. """ events = build_events(track, composition, tempo, patch=patch, block=block) data = np.zeros((total_frames, 2), dtype=np.float32) @@ -112,7 +116,7 @@ def render(composition, voice_for, patch_for=None, effects_for=None, a sound is its own business: ``voice_for(track, clock)`` - the voice to render this track with (see :mod:`.voices`). + the voice to render this track with (see `.voices`). ``patch_for(track)`` ``{macro index: normalized value}`` for the macros the composition does not set - the instrument's own patch, not the middle of every diff --git a/lib/audiorender/tempo.py b/lib/audiorender/tempo.py index 6392d92b..a06983e0 100644 --- a/lib/audiorender/tempo.py +++ b/lib/audiorender/tempo.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Beats to seconds and back, through a piecewise-constant tempo map. A tempo map is a list of rows, each ``(beat, bpm)`` or @@ -37,7 +41,7 @@ def beats_to_seconds(self, beat): return seconds def beat_at_sample(self, sample): - """The inverse of :meth:`beats_to_seconds`, piecewise exact.""" + """The inverse of `beats_to_seconds`, piecewise exact.""" seconds = sample / self.sample_rate elapsed = 0.0 for index, (start, bpm) in enumerate(self._spans): diff --git a/lib/audiorender/voices.py b/lib/audiorender/voices.py index 001081a7..b0d2a43a 100644 --- a/lib/audiorender/voices.py +++ b/lib/audiorender/voices.py @@ -1,12 +1,16 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """What a render pulls audio from, and how it tells them the time. A *voice* is anything a render can drive: it takes the events -:mod:`audiorender.events` builds and hands back frames. +`audiorender.events` builds and hands back frames. deliver(event, sample_position) pull_frames(frames) -> interleaved little-endian stereo int16 -:class:`Voice` is that protocol over an :mod:`audioinstruments` instrument, +`Voice` is that protocol over an `audioinstruments` instrument, which is what a caller wants unless it has its own way of loading a sound - a plug-in host driving its own script runner, say. Keeping the protocol this small is what lets such a host reuse the render loop without reusing @@ -45,7 +49,7 @@ def pull_frames(self, frames): class Voice(Puller): - """One :mod:`audioinstruments` instrument, ready to be rendered.""" + """One `audioinstruments` instrument, ready to be rendered.""" def __init__(self, instrument): Puller.__init__(self, instrument.output) diff --git a/lib/audiorender/wav.py b/lib/audiorender/wav.py index 7d879e87..c27019af 100644 --- a/lib/audiorender/wav.py +++ b/lib/audiorender/wav.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Writing a render out as a 16-bit stereo WAV.""" import wave diff --git a/manifest.py b/manifest.py index 1f91df79..3d1b95fc 100644 --- a/manifest.py +++ b/manifest.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiodsp's manifest: it names the C module and freezes no Python. A build includes this file from its own manifest (see the README), so diff --git a/scripts/test_testpypi_install.py b/scripts/test_testpypi_install.py index cf5a2b75..86bf21b9 100755 --- a/scripts/test_testpypi_install.py +++ b/scripts/test_testpypi_install.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Smoke a fresh omnipython-audiodsp install from TestPyPI. scripts/test_testpypi_install.py [VERSION] [--skip-render] diff --git a/setup.py b/setup.py index 962e306f..8164bd8d 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + import subprocess import sys from pathlib import Path diff --git a/src/cpython/audiobiquad.py b/src/cpython/audiobiquad.py index d6034dd6..79ac49e8 100644 --- a/src/cpython/audiobiquad.py +++ b/src/cpython/audiobiquad.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A biquad and a first-order all-pass cascade that reach exact zero. Not a CircuitPython module, and not from micropython-vst3's engine either -- diff --git a/src/cpython/audioconvolve.py b/src/cpython/audioconvolve.py index 180dbe7f..d394972a 100644 --- a/src/cpython/audioconvolve.py +++ b/src/cpython/audioconvolve.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Convolution: an impulse response applied properly, rather than imitated. Not a CircuitPython module, and not from micropython-vst3's engine either -- diff --git a/src/cpython/audiocore.py b/src/cpython/audiocore.py index 3a14943b..21a367c8 100644 --- a/src/cpython/audiocore.py +++ b/src/cpython/audiocore.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """CircuitPython-compatible audio sample sources for CPython.""" import wave diff --git a/src/cpython/audiodelays.py b/src/cpython/audiodelays.py index 18c8abf4..d2054b32 100644 --- a/src/cpython/audiodelays.py +++ b/src/cpython/audiodelays.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """CircuitPython-compatible streaming delay effects.""" from array import array diff --git a/src/cpython/audiodynamics.py b/src/cpython/audiodynamics.py index 9a893386..07304996 100644 --- a/src/cpython/audiodynamics.py +++ b/src/cpython/audiodynamics.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Envelope-follower dynamics processing: compression, limiting, downward expansion, gating and transient shaping. diff --git a/src/cpython/audioecho.py b/src/cpython/audioecho.py index 5c738300..22c5502a 100644 --- a/src/cpython/audioecho.py +++ b/src/cpython/audioecho.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A delay with a filter, a soft-clip and a cross-feed inside its feedback loop. diff --git a/src/cpython/audiofeatures.py b/src/cpython/audiofeatures.py index 5e4aed53..d3db8166 100644 --- a/src/cpython/audiofeatures.py +++ b/src/cpython/audiofeatures.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Speech features for a wake word model, from PCM at any rate. audiodsp's own, not a CircuitPython module. ``Frontend`` is TensorFlow Lite diff --git a/src/cpython/audiofilters.py b/src/cpython/audiofilters.py index d90e56df..73464ea6 100644 --- a/src/cpython/audiofilters.py +++ b/src/cpython/audiofilters.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """CircuitPython-compatible streaming audio filters.""" from array import array diff --git a/src/cpython/audiofreeverb.py b/src/cpython/audiofreeverb.py index 57253cc6..68c77099 100644 --- a/src/cpython/audiofreeverb.py +++ b/src/cpython/audiofreeverb.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """CircuitPython-compatible Freeverb streaming effect.""" from array import array diff --git a/src/cpython/audiograph.py b/src/cpython/audiograph.py index b91a2aad..7f1ec684 100644 --- a/src/cpython/audiograph.py +++ b/src/cpython/audiograph.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """The graph runner: a whole patch rendered by one C call per block. audiodsp's own, not a CircuitPython module. The runner is diff --git a/src/cpython/audioladder.py b/src/cpython/audioladder.py index 13b27362..119c3789 100644 --- a/src/cpython/audioladder.py +++ b/src/cpython/audioladder.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A transistor ladder filter: four one-pole stages round a feedback loop with an odd saturator inside the loop. diff --git a/src/cpython/audiomath.py b/src/cpython/audiomath.py index 0f0d3cf2..488d8fca 100644 --- a/src/cpython/audiomath.py +++ b/src/cpython/audiomath.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Arithmetic on audio streams: one multiplied by another, and one divided down in frequency. diff --git a/src/cpython/audiometer.py b/src/cpython/audiometer.py index c40d91a7..14701b9d 100644 --- a/src/cpython/audiometer.py +++ b/src/cpython/audiometer.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Band levels for a spectrum meter: N log-spaced bands between two frequencies, plus peak and RMS, one byte each in half-dB steps. diff --git a/src/cpython/audiomixer.py b/src/cpython/audiomixer.py index 576966d1..603c619c 100644 --- a/src/cpython/audiomixer.py +++ b/src/cpython/audiomixer.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """CircuitPython-compatible PCM mixer.""" from audiocore import ( diff --git a/src/cpython/audiomodal.py b/src/cpython/audiomodal.py index 9fdfa9fd..2f4a00fd 100644 --- a/src/cpython/audiomodal.py +++ b/src/cpython/audiomodal.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A bank of resonators, which is what a struck object is. Hit a drum head, a marimba bar, a bell or a wine glass and it rings as a sum diff --git a/src/cpython/audiooscillator.py b/src/cpython/audiooscillator.py index fba88149..ed0d9311 100644 --- a/src/cpython/audiooscillator.py +++ b/src/cpython/audiooscillator.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A band-limited oscillator whose pitch, FM, pulse width and sync are audio streams, read sample by sample. diff --git a/src/cpython/audiopump.py b/src/cpython/audiopump.py index 0fcdd42b..c0218db4 100644 --- a/src/cpython/audiopump.py +++ b/src/cpython/audiopump.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """The audio pump, for CPython. The same module the MicroPython and CircuitPython builds carry, with the same diff --git a/src/cpython/audioroute.py b/src/cpython/audioroute.py index 8b7bd0f4..a0013998 100644 --- a/src/cpython/audioroute.py +++ b/src/cpython/audioroute.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Route audio: fan one stream out to parallel branches, or move it between the channels. diff --git a/src/cpython/audiosampler.py b/src/cpython/audiosampler.py index 2e92aa24..a51548e6 100644 --- a/src/cpython/audiosampler.py +++ b/src/cpython/audiosampler.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Play recorded notes: N voices, each reading a buffer at its own pitch. `audiomixer.Mixer` plays sources at their own rate and `synthio` plays diff --git a/src/cpython/audioshaper.py b/src/cpython/audioshaper.py index 4abd746f..5372a67b 100644 --- a/src/cpython/audioshaper.py +++ b/src/cpython/audioshaper.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A table-driven waveshaper that does its shaping above the sample rate. Not a CircuitPython module, and not from micropython-vst3's engine either -- diff --git a/src/cpython/audiospeed.py b/src/cpython/audiospeed.py index 02e2360b..348247f2 100644 --- a/src/cpython/audiospeed.py +++ b/src/cpython/audiospeed.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """CircuitPython-compatible streaming speed changer.""" from audiocore import GET_BUFFER_DONE, GET_BUFFER_MORE_DATA, _AudioSample, get_buffer, reset_buffer diff --git a/src/cpython/audioverb.py b/src/cpython/audioverb.py index 330cfee0..6b2f3327 100644 --- a/src/cpython/audioverb.py +++ b/src/cpython/audioverb.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """An algorithmic reverberation tank whose topology comes from Python. Not a CircuitPython module, and not from micropython-vst3's engine either -- diff --git a/src/cpython/audiovorbis.py b/src/cpython/audiovorbis.py index b53e81bd..c9c81fb9 100644 --- a/src/cpython/audiovorbis.py +++ b/src/cpython/audiovorbis.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Ogg Vorbis files as audio samples, for CPython. Not part of CircuitPython, which has no Vorbis module: this is PyDevices'. diff --git a/src/cpython/synthio.py b/src/cpython/synthio.py index 591c32d0..00a75a27 100644 --- a/src/cpython/synthio.py +++ b/src/cpython/synthio.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """CircuitPython-compatible synthesizer API for CPython.""" from array import array diff --git a/tests/circuitpython/ring_through_mixer.py b/tests/circuitpython/ring_through_mixer.py index b8361cdd..0dce7c6c 100644 --- a/tests/circuitpython/ring_through_mixer.py +++ b/tests/circuitpython/ring_through_mixer.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + # audiopump.Ring pulled through audiomixer, checked sample by sample. # # Run it with a CircuitPython unix build that has audiodsp in it: diff --git a/tests/circuitpython/ring_tone_board.py b/tests/circuitpython/ring_tone_board.py index d9fa8547..f80b0931 100644 --- a/tests/circuitpython/ring_tone_board.py +++ b/tests/circuitpython/ring_tone_board.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + # A 441 Hz tone written from Python into audiopump.Ring and played by the # board's audiobusio.I2SOut for 30 s, then the ring's counts. Copy it to a # CircuitPython board with an I2S amplifier as code.py, or paste it at the diff --git a/tests/parity/_vstaudio_shim.py b/tests/parity/_vstaudio_shim.py index bbcf7001..ee7e623d 100644 --- a/tests/parity/_vstaudio_shim.py +++ b/tests/parity/_vstaudio_shim.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A stand-in for the `vstaudio` host module, so the original instrument scripts can be imported and played outside a VST3 sidecar. diff --git a/tests/parity/biquad_component_probe.py b/tests/parity/biquad_component_probe.py index 902731f2..2415c656 100644 --- a/tests/parity/biquad_component_probe.py +++ b/tests/parity/biquad_component_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic biquad PCM probes across every FilterMode. The rest of the parity suite only ever builds LOW_PASS filters, which is how diff --git a/tests/parity/cast_probe.py b/tests/parity/cast_probe.py index f88e0faa..ad101f60 100644 --- a/tests/parity/cast_probe.py +++ b/tests/parity/cast_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiomath.cast_into on every target: the same int16 from the same floats. cast_probe.py audiomath diff --git a/tests/parity/convolve_probe.py b/tests/parity/convolve_probe.py index 0497532b..ad2b2c77 100644 --- a/tests/parity/convolve_probe.py +++ b/tests/parity/convolve_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Convolver PCM. convolve_probe.py audioconvolve diff --git a/tests/parity/convolve_state_probe.py b/tests/parity/convolve_state_probe.py index 41861b15..5f9db2b2 100644 --- a/tests/parity/convolve_state_probe.py +++ b/tests/parity/convolve_state_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Convolver PCM across a re-synthesis or a reload on a playing node. diff --git a/tests/parity/cp11_files_probe.py b/tests/parity/cp11_files_probe.py index 8324ca43..f7e2f60c 100644 --- a/tests/parity/cp11_files_probe.py +++ b/tests/parity/cp11_files_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """CircuitPython 11.0.0-alpha.1's WaveFile and MP3Decoder fixes. cp11_files_probe.py # PASS, or FAIL and exit 1 diff --git a/tests/parity/cp11_fixes_probe.py b/tests/parity/cp11_fixes_probe.py index 42fce108..b1a84292 100644 --- a/tests/parity/cp11_fixes_probe.py +++ b/tests/parity/cp11_fixes_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """CircuitPython 11.0.0-alpha.1's synthio, audiomixer and audiocore fixes. cp11_fixes_probe.py synthio diff --git a/tests/parity/deinit_surface_probe.py b/tests/parity/deinit_surface_probe.py index 4ea59fbd..cca77a45 100644 --- a/tests/parity/deinit_surface_probe.py +++ b/tests/parity/deinit_surface_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Every node in the palette releases, and refuses to be used afterwards. Runs unchanged on the CPython target, on desktop MicroPython and on diff --git a/tests/parity/delay_lifecycle_probe.py b/tests/parity/delay_lifecycle_probe.py index f15adb32..6a8138a0 100644 --- a/tests/parity/delay_lifecycle_probe.py +++ b/tests/parity/delay_lifecycle_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """The delay nodes' lifecycle matrix: the same events on every delay node. delay_lifecycle_probe.py audioecho diff --git a/tests/parity/delay_time_probe.py b/tests/parity/delay_time_probe.py index 7fde04b4..0a73afcc 100644 --- a/tests/parity/delay_time_probe.py +++ b/tests/parity/delay_time_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A delay's Time shortened and lengthened again: the line comes back empty. delay_time_probe.py audiodelays diff --git a/tests/parity/drift_probe.py b/tests/parity/drift_probe.py index 9d40bd66..c6a2e2af 100644 --- a/tests/parity/drift_probe.py +++ b/tests/parity/drift_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """DriftResampler following a producer on another clock. drift_probe.py audiopump diff --git a/tests/parity/dynamics_extras_probe.py b/tests/parity/dynamics_extras_probe.py index 190a224e..69d2b002 100644 --- a/tests/parity/dynamics_extras_probe.py +++ b/tests/parity/dynamics_extras_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Dynamics PCM for the two options the original never had. dynamics_extras_probe.py audiodynamics diff --git a/tests/parity/dynamics_options_probe.py b/tests/parity/dynamics_options_probe.py index 38efa81f..e7cbc9eb 100644 --- a/tests/parity/dynamics_options_probe.py +++ b/tests/parity/dynamics_options_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Dynamics PCM for the options the original never had. dynamics_options_probe.py audiodynamics diff --git a/tests/parity/dynamics_probe.py b/tests/parity/dynamics_probe.py index 071ec62d..973d1182 100644 --- a/tests/parity/dynamics_probe.py +++ b/tests/parity/dynamics_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Dynamics PCM, from whichever module provides the node. dynamics_probe.py audiodynamics diff --git a/tests/parity/dynamics_short_buffer_probe.py b/tests/parity/dynamics_short_buffer_probe.py index 435272f2..999790b1 100644 --- a/tests/parity/dynamics_short_buffer_probe.py +++ b/tests/parity/dynamics_short_buffer_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A Dynamics node whose source hands out buffers shorter than its block. The node fills its 256-frame block from as many source buffers as it takes. diff --git a/tests/parity/echo_filter_probe.py b/tests/parity/echo_filter_probe.py index b2dcf0a3..ff57fd96 100644 --- a/tests/parity/echo_filter_probe.py +++ b/tests/parity/echo_filter_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Echo.filter PCM. echo_filter_probe.py audiodelays diff --git a/tests/parity/echo_short_line_probe.py b/tests/parity/echo_short_line_probe.py index 12432c86..7623c536 100644 --- a/tests/parity/echo_short_line_probe.py +++ b/tests/parity/echo_short_line_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Echo PCM for a delay line shorter than one audio buffer. echo_short_line_probe.py audiodelays diff --git a/tests/parity/effects_bounds_probe.py b/tests/parity/effects_bounds_probe.py index 94134102..30225f4f 100644 --- a/tests/parity/effects_bounds_probe.py +++ b/tests/parity/effects_bounds_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic PCM for the effect bounds and silence fills upstream fixed. effects_bounds_probe.py audiodelays audiofilters diff --git a/tests/parity/effects_component_probe.py b/tests/parity/effects_component_probe.py index 0ad14e15..e20879f1 100644 --- a/tests/parity/effects_component_probe.py +++ b/tests/parity/effects_component_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic effect PCM probes shared by MicroPython and CPython.""" from array import array diff --git a/tests/parity/features_probe.py b/tests/parity/features_probe.py index 91c6b5ab..9517b650 100644 --- a/tests/parity/features_probe.py +++ b/tests/parity/features_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic audiofeatures slices. features_probe.py audiofeatures diff --git a/tests/parity/feedback_delay_frames_probe.py b/tests/parity/feedback_delay_frames_probe.py index ae6cc2cd..1f4b6928 100644 --- a/tests/parity/feedback_delay_frames_probe.py +++ b/tests/parity/feedback_delay_frames_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """FeedbackDelay: `delay_frames` reads the line exactly that many frames back. feedback_delay_frames_probe.py audioecho diff --git a/tests/parity/feedback_delay_options_probe.py b/tests/parity/feedback_delay_options_probe.py index 43375ebd..5f6582bc 100644 --- a/tests/parity/feedback_delay_options_probe.py +++ b/tests/parity/feedback_delay_options_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic FeedbackDelay PCM for the four options the node grew later. feedback_delay_options_probe.py audioecho diff --git a/tests/parity/feedback_delay_park_probe.py b/tests/parity/feedback_delay_park_probe.py index 37a0346f..123962bc 100644 --- a/tests/parity/feedback_delay_park_probe.py +++ b/tests/parity/feedback_delay_park_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic FeedbackDelay PCM for two controls taken to their stop. feedback_delay_park_probe.py audioecho diff --git a/tests/parity/feedback_delay_probe.py b/tests/parity/feedback_delay_probe.py index 09e27ccd..9b584168 100644 --- a/tests/parity/feedback_delay_probe.py +++ b/tests/parity/feedback_delay_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic FeedbackDelay PCM. feedback_delay_probe.py audioecho diff --git a/tests/parity/feedback_delay_state_probe.py b/tests/parity/feedback_delay_state_probe.py index 8b486ec7..87988b05 100644 --- a/tests/parity/feedback_delay_state_probe.py +++ b/tests/parity/feedback_delay_state_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic FeedbackDelay PCM for the node's own state: the damping low-pass near the floor, the loop filters taken out and put back, a wow depth moved while playing, and a cross-fed stereo line near the floor. diff --git a/tests/parity/filter_f32_frames_probe.py b/tests/parity/filter_f32_frames_probe.py index e6281383..a60adeb5 100644 --- a/tests/parity/filter_f32_frames_probe.py +++ b/tests/parity/filter_f32_frames_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Biquad and AllPass at the caller's block size. filter_f32_frames_probe.py audiobiquad diff --git a/tests/parity/filter_f32_move_probe.py b/tests/parity/filter_f32_move_probe.py index 6ca33825..f641e640 100644 --- a/tests/parity/filter_f32_move_probe.py +++ b/tests/parity/filter_f32_move_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiobiquad's coefficient moves, rendered for every interpreter to agree on. filter_f32_move_probe.py audiobiquad diff --git a/tests/parity/filter_f32_probe.py b/tests/parity/filter_f32_probe.py index 6d0fad75..c529caf9 100644 --- a/tests/parity/filter_f32_probe.py +++ b/tests/parity/filter_f32_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic audiobiquad PCM, and the two invariants the module exists for. filter_f32_probe.py audiobiquad diff --git a/tests/parity/flanger_probe.py b/tests/parity/flanger_probe.py index 51edfcdf..c4acb6bb 100644 --- a/tests/parity/flanger_probe.py +++ b/tests/parity/flanger_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Flanger PCM. flanger_probe.py audiodelays diff --git a/tests/parity/freeverb_filter_probe.py b/tests/parity/freeverb_filter_probe.py index 4a68c8ab..aca34d96 100644 --- a/tests/parity/freeverb_filter_probe.py +++ b/tests/parity/freeverb_filter_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Freeverb pre_filter / post_filter PCM. freeverb_filter_probe.py audiofreeverb diff --git a/tests/parity/granular_pitch_shift_probe.py b/tests/parity/granular_pitch_shift_probe.py index 19b4e027..eed669cc 100644 --- a/tests/parity/granular_pitch_shift_probe.py +++ b/tests/parity/granular_pitch_shift_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic GranularPitchShift PCM. granular_pitch_shift_probe.py audiodelays diff --git a/tests/parity/graph_carry_probe.py b/tests/parity/graph_carry_probe.py index 75089efd..901e2884 100644 --- a/tests/parity/graph_carry_probe.py +++ b/tests/parity/graph_carry_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiograph's carry(): a graph built beside a running one takes over its nodes' state. graph_carry_probe.py # prints the render, then GRAPH CARRY PASS diff --git a/tests/parity/graph_nodes_probe.py b/tests/parity/graph_nodes_probe.py index 55304f37..bc0edf37 100644 --- a/tests/parity/graph_nodes_probe.py +++ b/tests/parity/graph_nodes_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiograph's PHASER, LAG, LFO shapes, SAMPLE_HOLD, TWOPOLE, OVERDRIVE, PAN, WAVEFOLD, LPG, MODAL, STRUCK, PICKUP, TREMOLO, DECAY, PINK, CRUSH, ACID, SPRING, ADSR2, GLISS, FOLLOW, WAVETABLE, ROTARY and BIQUAD_RAMP, rendered for verify_graph.py. graph_nodes_probe.py # prints the render, then GRAPH NODES PASS diff --git a/tests/parity/graph_parity_probe.py b/tests/parity/graph_parity_probe.py index 969ddebd..389f8df5 100644 --- a/tests/parity/graph_parity_probe.py +++ b/tests/parity/graph_parity_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiograph: one patch, rendered block by block, for verify_graph.py to compare. graph_parity_probe.py # prints the render, then GRAPH PARITY PASS diff --git a/tests/parity/graph_probe.py b/tests/parity/graph_probe.py index 49fdbb8f..2097a8cc 100644 --- a/tests/parity/graph_probe.py +++ b/tests/parity/graph_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiograph: a graph renders what its nodes are wired to render, in one call. graph_probe.py # PASS, or FAIL and exit 1 diff --git a/tests/parity/graph_source_probe.py b/tests/parity/graph_source_probe.py index 23e98dcb..d9750eb9 100644 --- a/tests/parity/graph_source_probe.py +++ b/tests/parity/graph_source_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A graph pulled as a source renders what run() renders, for verify_graph.py. graph_source_probe.py # prints the render, then GRAPH SOURCE PASS diff --git a/tests/parity/host_reset_probe.py b/tests/parity/host_reset_probe.py index 38d8f9b4..b83ee0db 100644 --- a/tests/parity/host_reset_probe.py +++ b/tests/parity/host_reset_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A host reset in the middle of a stream keeps the frames already taken. host_reset_probe.py diff --git a/tests/parity/ladder_frames_probe.py b/tests/parity/ladder_frames_probe.py index bf8499af..63822343 100644 --- a/tests/parity/ladder_frames_probe.py +++ b/tests/parity/ladder_frames_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Ladder at the caller's block size. ladder_frames_probe.py audioladder diff --git a/tests/parity/ladder_probe.py b/tests/parity/ladder_probe.py index 009c6254..671c271e 100644 --- a/tests/parity/ladder_probe.py +++ b/tests/parity/ladder_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Ladder PCM. ladder_probe.py audioladder diff --git a/tests/parity/meter_probe.py b/tests/parity/meter_probe.py index 66cdec20..c43ccc97 100644 --- a/tests/parity/meter_probe.py +++ b/tests/parity/meter_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic audiometer levels. meter_probe.py audiometer diff --git a/tests/parity/midi_component_probe.py b/tests/parity/midi_component_probe.py index 644110ac..32ce9ab1 100644 --- a/tests/parity/midi_component_probe.py +++ b/tests/parity/midi_component_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """MIDI event parser and renderer parity probe.""" from array import array diff --git a/tests/parity/midside_probe.py b/tests/parity/midside_probe.py index 6971d6db..50856e89 100644 --- a/tests/parity/midside_probe.py +++ b/tests/parity/midside_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic MidSide PCM. midside_probe.py audioroute diff --git a/tests/parity/mixdown_knee_probe.py b/tests/parity/mixdown_knee_probe.py index decf9fcb..739daeda 100644 --- a/tests/parity/mixdown_knee_probe.py +++ b/tests/parity/mixdown_knee_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Drive the synthio mix-down limiter across its knee and print digests. WHY THIS PROBE EXISTS (audiodsp#27) diff --git a/tests/parity/mixer_borrowed_block_probe.py b/tests/parity/mixer_borrowed_block_probe.py index e142578e..4f1ec746 100644 --- a/tests/parity/mixer_borrowed_block_probe.py +++ b/tests/parity/mixer_borrowed_block_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A mixer voice mixes the source's buffer as it stands at MIX time. `common_hal_audiomixer_mixervoice_play` fetches one block from the source and diff --git a/tests/parity/mixer_q15_level_probe.py b/tests/parity/mixer_q15_level_probe.py index 85b23eb8..9497ac5c 100644 --- a/tests/parity/mixer_q15_level_probe.py +++ b/tests/parity/mixer_q15_level_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A voice's level and pan are Q15 integers, and a sample is `(x * q) >> 15`. CircuitPython 11.0.0-alpha.2 (adafruit/circuitpython#11617) scales a voice in diff --git a/tests/parity/mixer_reset_probe.py b/tests/parity/mixer_reset_probe.py index 5b718386..1ad60d4a 100644 --- a/tests/parity/mixer_reset_probe.py +++ b/tests/parity/mixer_reset_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A host reset of a Mixer keeps its voices where they are. mixer_reset_probe.py audiomixer diff --git a/tests/parity/mixer_short_buffer_probe.py b/tests/parity/mixer_short_buffer_probe.py index 9f4d3a59..82726a0c 100644 --- a/tests/parity/mixer_short_buffer_probe.py +++ b/tests/parity/mixer_short_buffer_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A level move on a mixer voice whose source hands out short buffers. A voice's level and pan move wait for a zero crossing, so they cannot click, diff --git a/tests/parity/mixer_short_loop_probe.py b/tests/parity/mixer_short_loop_probe.py index a4db0029..3e867eeb 100644 --- a/tests/parity/mixer_short_loop_probe.py +++ b/tests/parity/mixer_short_loop_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A looping sample too short to fill one packed word must not hang. mixer_short_loop_probe.py diff --git a/tests/parity/modal_probe.py b/tests/parity/modal_probe.py index 648817b5..1a835ecd 100644 --- a/tests/parity/modal_probe.py +++ b/tests/parity/modal_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic audiomodal PCM, and the three invariants the module exists for. modal_probe.py audiomodal diff --git a/tests/parity/mp3_rewind_probe.py b/tests/parity/mp3_rewind_probe.py index 542929e4..6e57b4f5 100644 --- a/tests/parity/mp3_rewind_probe.py +++ b/tests/parity/mp3_rewind_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """An MP3 rewound for a loop decodes its second lap exactly like its first. mp3_rewind_probe.py # PASS, or FAIL and exit 1 diff --git a/tests/parity/multiply_probe.py b/tests/parity/multiply_probe.py index 98880aea..9d34b61b 100644 --- a/tests/parity/multiply_probe.py +++ b/tests/parity/multiply_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Multiply PCM. multiply_probe.py audiomath diff --git a/tests/parity/multitap_edges_probe.py b/tests/parity/multitap_edges_probe.py index 7108acdf..62c87b34 100644 --- a/tests/parity/multitap_edges_probe.py +++ b/tests/parity/multitap_edges_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic MultiTapDelay PCM for two loops audiodsp#177 found. multitap_edges_probe.py audiodelays diff --git a/tests/parity/multitap_reset_probe.py b/tests/parity/multitap_reset_probe.py index b1beec0d..3222cdc0 100644 --- a/tests/parity/multitap_reset_probe.py +++ b/tests/parity/multitap_reset_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """MultiTapDelay: a reset puts the write head back at the top. multitap_reset_probe.py audiodelays diff --git a/tests/parity/multitap_time_probe.py b/tests/parity/multitap_time_probe.py index 345ce0aa..3fe6942d 100644 --- a/tests/parity/multitap_time_probe.py +++ b/tests/parity/multitap_time_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """MultiTapDelay: a shorter Time and back again reads silence, not old audio. multitap_time_probe.py audiodelays diff --git a/tests/parity/oscillator_probe.py b/tests/parity/oscillator_probe.py index 9c4477a6..676fb074 100644 --- a/tests/parity/oscillator_probe.py +++ b/tests/parity/oscillator_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Oscillator PCM. oscillator_probe.py audiooscillator diff --git a/tests/parity/pitch_empty_source_probe.py b/tests/parity/pitch_empty_source_probe.py index b26fde56..c111f65b 100644 --- a/tests/parity/pitch_empty_source_probe.py +++ b/tests/parity/pitch_empty_source_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """The pitch shifters and Flanger return from a pull whose looping source has nothing. diff --git a/tests/parity/pitch_reset_probe.py b/tests/parity/pitch_reset_probe.py index a63d9e66..d15fc101 100644 --- a/tests/parity/pitch_reset_probe.py +++ b/tests/parity/pitch_reset_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A reset puts the pitch shifters back where a new node starts. pitch_reset_probe.py audiodelays diff --git a/tests/parity/pitchshift_freeze_probe.py b/tests/parity/pitchshift_freeze_probe.py index 05763a5c..ea53f945 100644 --- a/tests/parity/pitchshift_freeze_probe.py +++ b/tests/parity/pitchshift_freeze_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """PitchShift.freeze, from CircuitPython 11.0.0-alpha.1 (9bef7b7606). pitchshift_freeze_probe.py audiodelays # PASS, or FAIL and exit 1 diff --git a/tests/parity/resampler_probe.py b/tests/parity/resampler_probe.py index 490aca20..8e00d3bf 100644 --- a/tests/parity/resampler_probe.py +++ b/tests/parity/resampler_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Resampler PCM. resampler_probe.py audiospeed diff --git a/tests/parity/route_borrow_probe.py b/tests/parity/route_borrow_probe.py index 890cfc47..22d2d4b9 100644 --- a/tests/parity/route_borrow_probe.py +++ b/tests/parity/route_borrow_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Routing nodes under a mixer voice: borrowed tap buffers, a looping voice over a double-buffered sample, and a Port's pass-through. diff --git a/tests/parity/route_dry_probe.py b/tests/parity/route_dry_probe.py index 87ff3165..15c69e72 100644 --- a/tests/parity/route_dry_probe.py +++ b/tests/parity/route_dry_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """What a Splitter does once its source runs out. route_dry_probe.py audioroute the port diff --git a/tests/parity/route_edge_probe.py b/tests/parity/route_edge_probe.py index 23211d78..1319f0c3 100644 --- a/tests/parity/route_edge_probe.py +++ b/tests/parity/route_edge_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Routing and mixer events no other probe covers (audiodsp#178). EDGE diff --git a/tests/parity/route_probe.py b/tests/parity/route_probe.py index 00862595..9034d04f 100644 --- a/tests/parity/route_probe.py +++ b/tests/parity/route_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Splitter PCM, from whichever module provides the node. route_probe.py audioroute the port diff --git a/tests/parity/samplehold_probe.py b/tests/parity/samplehold_probe.py index e6e39d3a..1f3c5a71 100644 --- a/tests/parity/samplehold_probe.py +++ b/tests/parity/samplehold_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic SampleHold PCM, and the counts PCM alone would not say. samplehold_probe.py audioshaper diff --git a/tests/parity/sampler_cost.py b/tests/parity/sampler_cost.py index c0122cea..af99c410 100644 --- a/tests/parity/sampler_cost.py +++ b/tests/parity/sampler_cost.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """What a block of audiosampler costs, by voice count and by what each voice does. sampler_cost.py [voices ...] diff --git a/tests/parity/sampler_probe.py b/tests/parity/sampler_probe.py index 6866589d..9008d30c 100644 --- a/tests/parity/sampler_probe.py +++ b/tests/parity/sampler_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic audiosampler PCM, and the invariants the module exists for. sampler_probe.py audiosampler diff --git a/tests/parity/speedchanger_hold_probe.py b/tests/parity/speedchanger_hold_probe.py index eb02e019..b53c3d0f 100644 --- a/tests/parity/speedchanger_hold_probe.py +++ b/tests/parity/speedchanger_hold_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A sample-and-hold built out of two SpeedChangers, over several block sizes. speedchanger_hold_probe.py audiospeed diff --git a/tests/parity/splitter_clear_probe.py b/tests/parity/splitter_clear_probe.py index 528c1fbe..e175fe9f 100644 --- a/tests/parity/splitter_clear_probe.py +++ b/tests/parity/splitter_clear_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """`Splitter.clear()`: what the ring held and nobody read is let go. A Splitter writes a whole source buffer into its ring and hands it out a diff --git a/tests/parity/splitter_overflow_probe.py b/tests/parity/splitter_overflow_probe.py index 488332a9..8fdd43a9 100644 --- a/tests/parity/splitter_overflow_probe.py +++ b/tests/parity/splitter_overflow_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A block bigger than the Splitter's ring must arrive whole. splitter_overflow_probe.py diff --git a/tests/parity/streaming_component_probe.py b/tests/parity/streaming_component_probe.py index b1fdb266..36f2773c 100644 --- a/tests/parity/streaming_component_probe.py +++ b/tests/parity/streaming_component_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Streaming, looping, panning, live-property, and tail parity probes.""" from array import array diff --git a/tests/parity/suboctave_probe.py b/tests/parity/suboctave_probe.py index 18931707..ce90baa4 100644 --- a/tests/parity/suboctave_probe.py +++ b/tests/parity/suboctave_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic SubOctave PCM. suboctave_probe.py audiomath diff --git a/tests/parity/synth_component_probe.py b/tests/parity/synth_component_probe.py index 716f86a6..f2b1b29c 100644 --- a/tests/parity/synth_component_probe.py +++ b/tests/parity/synth_component_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Small cross-runtime synthesis probes used while tightening PCM parity.""" from array import array diff --git a/tests/parity/synthtools_acceptance.py b/tests/parity/synthtools_acceptance.py index 759a44b7..12d34fd2 100644 --- a/tests/parity/synthtools_acceptance.py +++ b/tests/parity/synthtools_acceptance.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + # Phase 7 acceptance test: run todbot's unmodified `synthtools` engine # (vendored at tests/vendor/synthtools, see SYNTHTOOLS_COMMIT.txt) against # the real ported synthio/audiomixer/audiofilters/audiodelays modules and diff --git a/tests/parity/tail_rest_probe.py b/tests/parity/tail_rest_probe.py index d3571955..dfbb4e75 100644 --- a/tests/parity/tail_rest_probe.py +++ b/tests/parity/tail_rest_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A tail rings out after its source ends, and a node at rest is empty. tail_rest_probe.py diff --git a/tests/parity/tank_probe.py b/tests/parity/tank_probe.py index adc20575..1282f7c7 100644 --- a/tests/parity/tank_probe.py +++ b/tests/parity/tank_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Tank PCM. tank_probe.py audioverb diff --git a/tests/parity/tank_return_probe.py b/tests/parity/tank_return_probe.py index 1c1e0bc9..0381369d 100644 --- a/tests/parity/tank_return_probe.py +++ b/tests/parity/tank_return_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Tank: a filter or the modulation taken to 0 and brought back. tank_return_probe.py audioverb diff --git a/tests/parity/tank_state_probe.py b/tests/parity/tank_state_probe.py index a15333d0..247a0625 100644 --- a/tests/parity/tank_state_probe.py +++ b/tests/parity/tank_state_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Tank PCM across the node's own state changes. tank_state_probe.py audioverb diff --git a/tests/parity/verify_acceptance.py b/tests/parity/verify_acceptance.py index 2de3a190..f6915aae 100644 --- a/tests/parity/verify_acceptance.py +++ b/tests/parity/verify_acceptance.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Require CPython acceptance stdout to match what this port should produce. This one used to be a straight byte-for-byte match against CircuitPython diff --git a/tests/parity/verify_biquad.py b/tests/parity/verify_biquad.py index 720e5736..64f176d7 100644 --- a/tests/parity/verify_biquad.py +++ b/tests/parity/verify_biquad.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Verify biquad PCM across every FilterMode against committed metadata. Unlike the other verifies here, the golden is captured from *this* port and diff --git a/tests/parity/verify_dsp.py b/tests/parity/verify_dsp.py index 634c61c5..5353953d 100644 --- a/tests/parity/verify_dsp.py +++ b/tests/parity/verify_dsp.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Every target must render these nodes identically. That is the whole gate. verify_dsp.py --micropython PATH [--circuitpython PATH] diff --git a/tests/parity/verify_effects.py b/tests/parity/verify_effects.py index 88f423f4..fe52e377 100644 --- a/tests/parity/verify_effects.py +++ b/tests/parity/verify_effects.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Verify deterministic effect PCM against committed oracle metadata. The gate is an exact hash, and on the reference architecture it stays one: diff --git a/tests/parity/verify_graph.py b/tests/parity/verify_graph.py index f621414d..0ce2ea95 100644 --- a/tests/parity/verify_graph.py +++ b/tests/parity/verify_graph.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A graph renders the same samples on every interpreter. verify_graph.py --micropython PATH [--circuitpython PATH] diff --git a/tests/parity/verify_mixdown_knee.py b/tests/parity/verify_mixdown_knee.py index f46139ba..3c7f0179 100644 --- a/tests/parity/verify_mixdown_knee.py +++ b/tests/parity/verify_mixdown_knee.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """The one gate that can see a synthio voice-ceiling change (audiodsp#27). WHAT THIS COVERS THAT THE OTHER FOUR CANNOT diff --git a/tests/parity/verify_streaming.py b/tests/parity/verify_streaming.py index 6221aa80..b477f8cd 100644 --- a/tests/parity/verify_streaming.py +++ b/tests/parity/verify_streaming.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Verify mixer, delay, and MIDI PCM against committed oracle metadata.""" import hashlib diff --git a/tests/parity/verify_vorbis.py b/tests/parity/verify_vorbis.py index 4a5b5537..eed4b1db 100644 --- a/tests/parity/verify_vorbis.py +++ b/tests/parity/verify_vorbis.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiovorbis against ffmpeg: the same files, the same samples within Tremor's error. verify_vorbis.py [--micropython PATH] [--circuitpython PATH] diff --git a/tests/parity/vorbis_decode.py b/tests/parity/vorbis_decode.py index f4e5abdd..914bbb88 100644 --- a/tests/parity/vorbis_decode.py +++ b/tests/parity/vorbis_decode.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Decode one .ogg to raw PCM through audiovorbis, for verify_vorbis.py. vorbis_decode.py IN.ogg OUT.raw [--fault swap|drop] diff --git a/tests/parity/vorbis_probe.py b/tests/parity/vorbis_probe.py index 294e3de6..d60d9c91 100644 --- a/tests/parity/vorbis_probe.py +++ b/tests/parity/vorbis_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiovorbis.VorbisDecoder: the surface, the edges, and a loop. vorbis_probe.py # PASS, or FAIL and exit 1 diff --git a/tests/parity/vorbis_symbols_check.py b/tests/parity/vorbis_symbols_check.py index ffbbaf5d..e83cdf5c 100644 --- a/tests/parity/vorbis_symbols_check.py +++ b/tests/parity/vorbis_symbols_check.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """No Tremor symbol escapes audiodsp's namespace. vorbis_symbols_check.py # PASS, or FAIL and exit 1 diff --git a/tests/parity/waveshaper_probe.py b/tests/parity/waveshaper_probe.py index 3f9ff146..b2a4433c 100644 --- a/tests/parity/waveshaper_probe.py +++ b/tests/parity/waveshaper_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Deterministic Waveshaper PCM. waveshaper_probe.py audioshaper diff --git a/tests/pump/pump_probe.py b/tests/pump/pump_probe.py index 7e8e2d66..a560ed0b 100644 --- a/tests/pump/pump_probe.py +++ b/tests/pump/pump_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """The pump's own gates: same bytes, no allocation, survives a storm. tests/pump/pump_probe.py [case ...] [--fault WHICH] diff --git a/tests/pump/route_probe.py b/tests/pump/route_probe.py index d92026a6..21b3b2aa 100644 --- a/tests/pump/route_probe.py +++ b/tests/pump/route_probe.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """The three things around the pump: its ring, its queue, its tap -- and the port that must fault instead of recursing. diff --git a/tests/support/ulab/__init__.py b/tests/support/ulab/__init__.py index b5e6e8d7..73edd47a 100644 --- a/tests/support/ulab/__init__.py +++ b/tests/support/ulab/__init__.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + from . import numpy __all__ = ("numpy",) diff --git a/tests/test_audiodsp_util.py b/tests/test_audiodsp_util.py index c5f1b989..b76d14a0 100644 --- a/tests/test_audiodsp_util.py +++ b/tests/test_audiodsp_util.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """`audiodsp_util`: one number, the same number, on every target. A setting written in Python is held at the interpreter's `mp_float_t` width -- diff --git a/tests/test_binding_parity.py b/tests/test_binding_parity.py index 2be9f38b..e62ddcad 100644 --- a/tests/test_binding_parity.py +++ b/tests/test_binding_parity.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Our two hand-written bindings per module must expose the same surface. audiodsp carries each of its nine own modules twice: `src//*.c` binds it diff --git a/tests/test_circuitpython_libm.py b/tests/test_circuitpython_libm.py index f43c68cc..05ad4160 100644 --- a/tests/test_circuitpython_libm.py +++ b/tests/test_circuitpython_libm.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """What CircuitPython builds of audiodsp may call from double-precision libm. CircuitPython's RP2040 port links with the pico-sdk's ``--wrap`` for every diff --git a/tests/test_cpython_api.py b/tests/test_cpython_api.py index 1dac82a2..3703682c 100644 --- a/tests/test_cpython_api.py +++ b/tests/test_cpython_api.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + from array import array import gc import unittest diff --git a/tests/test_cpython_audiobiquad.py b/tests/test_cpython_audiobiquad.py index 3eb23723..56850cbc 100644 --- a/tests/test_cpython_audiobiquad.py +++ b/tests/test_cpython_audiobiquad.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiobiquad: the claims the parity golden cannot make on its own. `tests/parity/filter_f32_probe.py` pins what this module renders, and prints diff --git a/tests/test_cpython_audioconvolve.py b/tests/test_cpython_audioconvolve.py index 1a5c448b..fefc2bac 100644 --- a/tests/test_cpython_audioconvolve.py +++ b/tests/test_cpython_audioconvolve.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audioconvolve: the traits and the surface the renders cannot check. `tests/parity/convolve_probe.py` pins what this module renders across the three diff --git a/tests/test_cpython_audiodynamics.py b/tests/test_cpython_audiodynamics.py index c71ef44b..a3f90c52 100644 --- a/tests/test_cpython_audiodynamics.py +++ b/tests/test_cpython_audiodynamics.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiodynamics: the traits and the surface the renders cannot check. `tests/parity/dynamics_probe.py`, `dynamics_options_probe.py` and diff --git a/tests/test_cpython_audioecho.py b/tests/test_cpython_audioecho.py index 861960a3..5c4fd9aa 100644 --- a/tests/test_cpython_audioecho.py +++ b/tests/test_cpython_audioecho.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audioecho: the traits and the surface the renders cannot check. `tests/parity/feedback_delay_probe.py` and `feedback_delay_options_probe.py` diff --git a/tests/test_cpython_audiofeatures.py b/tests/test_cpython_audiofeatures.py index caa11dad..1c8c24ed 100644 --- a/tests/test_cpython_audiofeatures.py +++ b/tests/test_cpython_audiofeatures.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiofeatures: the frontend's bytes, the resampler, and the surface. The bytes. tests/data/audiofeatures/signal_16k.u16 and .s8 are the features diff --git a/tests/test_cpython_audiofreeverb.py b/tests/test_cpython_audiofreeverb.py index 43d77874..dbdab354 100644 --- a/tests/test_cpython_audiofreeverb.py +++ b/tests/test_cpython_audiofreeverb.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiofreeverb: the traits the renders cannot check. `tests/parity/freeverb_filter_probe.py` pins what this module renders across diff --git a/tests/test_cpython_audiograph.py b/tests/test_cpython_audiograph.py index 9e322c8f..1a85ace9 100644 --- a/tests/test_cpython_audiograph.py +++ b/tests/test_cpython_audiograph.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiograph on CPython: the board's module, with the board's names. | trait | bar | diff --git a/tests/test_cpython_audiograph_nodes.py b/tests/test_cpython_audiograph_nodes.py index de7b2553..8633a534 100644 --- a/tests/test_cpython_audiograph_nodes.py +++ b/tests/test_cpython_audiograph_nodes.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiograph's PHASER, LAG, LFO shapes, SAMPLE_HOLD, TWOPOLE, OVERDRIVE, TANH, PAN, WAVEFOLD, LPG and MODAL, held to what they stand in for. | node | held to | bar | diff --git a/tests/test_cpython_audioladder.py b/tests/test_cpython_audioladder.py index cfcfdc47..b21a33a5 100644 --- a/tests/test_cpython_audioladder.py +++ b/tests/test_cpython_audioladder.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audioladder: the three things the parity gate cannot see. `verify_dsp.py` pins what this node *renders*, byte for byte, on every diff --git a/tests/test_cpython_audioladder_cutoff.py b/tests/test_cpython_audioladder_cutoff.py index 1ed819c8..8f0ec32a 100644 --- a/tests/test_cpython_audioladder_cutoff.py +++ b/tests/test_cpython_audioladder_cutoff.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audioladder's cutoff input: the cutoff moved every sample, not every block. `set(cutoff_hz=...)` moves the cutoff once a block, so a cutoff swept by an diff --git a/tests/test_cpython_audiomath.py b/tests/test_cpython_audiomath.py index 9560a723..df6b8fef 100644 --- a/tests/test_cpython_audiomath.py +++ b/tests/test_cpython_audiomath.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiomath.SubOctave: that it is actually an octave down, and the surface. What the node *renders* is pinned byte-for-byte across the three interpreters diff --git a/tests/test_cpython_audiometer.py b/tests/test_cpython_audiometer.py index 44aa5970..4ebf193c 100644 --- a/tests/test_cpython_audiometer.py +++ b/tests/test_cpython_audiometer.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiometer: that its levels mean what they say, and the surface. That every interpreter reads the same bytes is tests/parity/meter_probe.py's diff --git a/tests/test_cpython_audiomixer_level.py b/tests/test_cpython_audiomixer_level.py index 069416f2..13e8efd6 100644 --- a/tests/test_cpython_audiomixer_level.py +++ b/tests/test_cpython_audiomixer_level.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A voice is scaled in integers, because that is what the kernel does. `src/audiomixer/Mixer.c`'s `mult16signed` is the authority, as CircuitPython diff --git a/tests/test_cpython_audiomodal.py b/tests/test_cpython_audiomodal.py index 4544db61..f14294dc 100644 --- a/tests/test_cpython_audiomodal.py +++ b/tests/test_cpython_audiomodal.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiomodal: the traits and the surface the renders cannot check. `tests/parity/modal_probe.py` pins what this module renders across the three diff --git a/tests/test_cpython_audiooscillator.py b/tests/test_cpython_audiooscillator.py index c6caa49c..086963eb 100644 --- a/tests/test_cpython_audiooscillator.py +++ b/tests/test_cpython_audiooscillator.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiooscillator: the traits the parity gate cannot see. `verify_dsp.py` pins what this node renders, byte for byte, on every diff --git a/tests/test_cpython_audiopump.py b/tests/test_cpython_audiopump.py index 31aef903..15e2a708 100644 --- a/tests/test_cpython_audiopump.py +++ b/tests/test_cpython_audiopump.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiopump on CPython: the service-mode engine, as audiodev drives it. The byte-identity, storm, ring, queue and tap gates are tests/pump/*_probe.py, diff --git a/tests/test_cpython_audioroute.py b/tests/test_cpython_audioroute.py index f82a67ec..9a20c947 100644 --- a/tests/test_cpython_audioroute.py +++ b/tests/test_cpython_audioroute.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audioroute: the traits and the surface the renders cannot check. `tests/parity/route_probe.py`, `route_dry_probe.py` and `midside_probe.py` pin diff --git a/tests/test_cpython_audiosampler.py b/tests/test_cpython_audiosampler.py index 88352413..4fce5ddf 100644 --- a/tests/test_cpython_audiosampler.py +++ b/tests/test_cpython_audiosampler.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiosampler: the traits and the surface the renders cannot check. `tests/parity/sampler_probe.py` pins what this module renders across the diff --git a/tests/test_cpython_audiosampler_modulation.py b/tests/test_cpython_audiosampler_modulation.py index e05a8190..1d488a3e 100644 --- a/tests/test_cpython_audiosampler_modulation.py +++ b/tests/test_cpython_audiosampler_modulation.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiosampler's filter, modulation envelope and LFOs, measured. What a SoundFont region asks of a voice besides its amplitude envelope diff --git a/tests/test_cpython_audioshaper.py b/tests/test_cpython_audioshaper.py index 3fdc8933..17f14fc1 100644 --- a/tests/test_cpython_audioshaper.py +++ b/tests/test_cpython_audioshaper.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audioshaper: the claims the parity goldens cannot carry. tests/parity/waveshaper_probe.py and samplehold_probe.py pin what these two diff --git a/tests/test_cpython_audioverb.py b/tests/test_cpython_audioverb.py index f7f1090c..fc54eeff 100644 --- a/tests/test_cpython_audioverb.py +++ b/tests/test_cpython_audioverb.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audioverb.Tank: the surface the parity gate cannot check. What the tank *renders* is pinned across every interpreter by diff --git a/tests/test_cpython_block_clock.py b/tests/test_cpython_block_clock.py index 43371de1..61c97120 100644 --- a/tests/test_cpython_block_clock.py +++ b/tests/test_cpython_block_clock.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """synthio's block clock belongs to the render in progress. Every render (a Synthesizer's block, an effect's block) advances the block diff --git a/tests/test_cpython_buffer_gc.py b/tests/test_cpython_buffer_gc.py index 19b69433..b1d91715 100644 --- a/tests/test_cpython_buffer_gc.py +++ b/tests/test_cpython_buffer_gc.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A native sample holding a memoryview must survive the cyclic collector. `RawSample` and `BufferOwner` keep a `Py_buffer` open on whatever buffer they diff --git a/tests/test_cpython_drift.py b/tests/test_cpython_drift.py index e8cbf015..8fca8fd4 100644 --- a/tests/test_cpython_drift.py +++ b/tests/test_cpython_drift.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """`audiopump.DriftResampler`: lock, hold and sound, against arithmetic. A producer on one clock writes a Ring; the node plays it on another and diff --git a/tests/test_cpython_envelope_reassignment.py b/tests/test_cpython_envelope_reassignment.py index b51ba307..a44d6987 100644 --- a/tests/test_cpython_envelope_reassignment.py +++ b/tests/test_cpython_envelope_reassignment.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Reassigning ``note.envelope`` takes effect on the next render block. The MicroPython and CircuitPython builds fetch a note's envelope on every diff --git a/tests/test_cpython_free_threading.py b/tests/test_cpython_free_threading.py index 7767ef42..ba2077e8 100644 --- a/tests/test_cpython_free_threading.py +++ b/tests/test_cpython_free_threading.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Many instruments on many threads, each rendering what it renders alone. The native extension declares that it doesn't need the GIL, so on a diff --git a/tests/test_cpython_mixer_empty_source.py b/tests/test_cpython_mixer_empty_source.py index 815f048f..a17a2d82 100644 --- a/tests/test_cpython_mixer_empty_source.py +++ b/tests/test_cpython_mixer_empty_source.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A source claiming MORE_DATA while returning nothing must not hang the mixer. `Mixer._get_buffer` fills a render block by repeatedly pulling from each diff --git a/tests/test_cpython_mixer_scale.py b/tests/test_cpython_mixer_scale.py index e998af78..f3b8d2a4 100644 --- a/tests/test_cpython_mixer_scale.py +++ b/tests/test_cpython_mixer_scale.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """The C scaling kernel renders the same bytes the Python loop does. `Mixer._get_buffer` scales every voice that is not at unity by its per-channel diff --git a/tests/test_cpython_mixer_short_loop.py b/tests/test_cpython_mixer_short_loop.py index 2018edfb..4638eecc 100644 --- a/tests/test_cpython_mixer_short_loop.py +++ b/tests/test_cpython_mixer_short_loop.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A looping sample too short to fill one packed word must not hang (#85). The mixer consumes whole 32-bit words, so a one-frame mono sample -- two diff --git a/tests/test_cpython_multitap_empty_source.py b/tests/test_cpython_multitap_empty_source.py index 8d9e4097..cd1f7c06 100644 --- a/tests/test_cpython_multitap_empty_source.py +++ b/tests/test_cpython_multitap_empty_source.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """MultiTapDelay with a source that says it has more and hands back nothing. Upstream CircuitPython's pull loop goes round for ever on such a source: it diff --git a/tests/test_cpython_multitap_reset.py b/tests/test_cpython_multitap_reset.py index d3fad459..ead07467 100644 --- a/tests/test_cpython_multitap_reset.py +++ b/tests/test_cpython_multitap_reset.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """MultiTapDelay: a reset node is a fresh node. `audiocore.reset_buffer` on a MultiTapDelay emptied the line but left the diff --git a/tests/test_cpython_pitch_empty_source.py b/tests/test_cpython_pitch_empty_source.py index 8c3bb444..57204e4c 100644 --- a/tests/test_cpython_pitch_empty_source.py +++ b/tests/test_cpython_pitch_empty_source.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """The pitch shifters and Flanger with a source that says it has more and hands nothing. diff --git a/tests/test_cpython_pitch_splice.py b/tests/test_cpython_pitch_splice.py index d8f890fe..6a5dc1c8 100644 --- a/tests/test_cpython_pitch_splice.py +++ b/tests/test_cpython_pitch_splice.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiodelays.PitchShift: a held note plays its shifted pitch at every splice. `PitchShift` reads a window of what was played at the shifted rate, and when diff --git a/tests/test_cpython_press_semantics.py b/tests/test_cpython_press_semantics.py index 2c8d4048..943dfdc4 100644 --- a/tests/test_cpython_press_semantics.py +++ b/tests/test_cpython_press_semantics.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """The synthesizer's press semantics, held to CircuitPython's own. These behaviors were sourced from the oracle's synthio_span_change_note diff --git a/tests/test_cpython_released_reclaim.py b/tests/test_cpython_released_reclaim.py index 2f9b3b29..25a8dcc1 100644 --- a/tests/test_cpython_released_reclaim.py +++ b/tests/test_cpython_released_reclaim.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A released note's channel comes back before its tail has finished. The oracle does not simply refuse a press at capacity. When no channel is diff --git a/tests/test_cpython_render.py b/tests/test_cpython_render.py index a3d357ff..2f27f475 100644 --- a/tests/test_cpython_render.py +++ b/tests/test_cpython_render.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audiorender: a whole (small) piece, from a composition to a WAV. The sound source is the smallest instrument audiorender can drive - a diff --git a/tests/test_cpython_resampler.py b/tests/test_cpython_resampler.py index d9709086..3a098884 100644 --- a/tests/test_cpython_resampler.py +++ b/tests/test_cpython_resampler.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """`audiospeed.Resampler`: the ratio is bound by whoever plays it. CircuitPython 10.3.0 added the node. The thing worth testing is not the diff --git a/tests/test_cpython_ring_modulation.py b/tests/test_cpython_ring_modulation.py index 2f544e47..07fe94ea 100644 --- a/tests/test_cpython_ring_modulation.py +++ b/tests/test_cpython_ring_modulation.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Ring modulation actually modulates. The CPython target used to accept `ring_frequency`/`ring_waveform` on a diff --git a/tests/test_cpython_silenced_voice.py b/tests/test_cpython_silenced_voice.py index e2ccf5af..e779935d 100644 --- a/tests/test_cpython_silenced_voice.py +++ b/tests/test_cpython_silenced_voice.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A voice at envelope level 0 contributes nothing, on every target. CircuitPython ends a voice the moment its envelope level hits exactly 0: the diff --git a/tests/test_cpython_silent_render.py b/tests/test_cpython_silent_render.py index dfaac67d..b6abc26c 100644 --- a/tests/test_cpython_silent_render.py +++ b/tests/test_cpython_silent_render.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A block where nothing sounds is silence, and costs next to nothing. The CPython synthio twin renders in Python, and under Pyodide that is the diff --git a/tests/test_cpython_speedchanger.py b/tests/test_cpython_speedchanger.py index e7c9bd29..d0ae7795 100644 --- a/tests/test_cpython_speedchanger.py +++ b/tests/test_cpython_speedchanger.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """`audiospeed.SpeedChanger`: the Q16 rate, and the phase across a boundary. Two deliberate departures from CircuitPython 10.3.0, both of them upstream diff --git a/tests/test_cpython_tank_sequence.py b/tests/test_cpython_tank_sequence.py index 23444c34..0963f3a2 100644 --- a/tests/test_cpython_tank_sequence.py +++ b/tests/test_cpython_tank_sequence.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """audioverb's native TankState reads its delay and tap tables safely. `delays` and `taps` are read one number at a time, and reading a number can diff --git a/tests/test_dynamics_option_table.py b/tests/test_dynamics_option_table.py index 77aed190..1fcab4e5 100644 --- a/tests/test_dynamics_option_table.py +++ b/tests/test_dynamics_option_table.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """The three option tables for `audiodynamics` must agree, name for number. `Dynamics` takes its settings by keyword, and each keyword has to reach the diff --git a/tests/test_prefetch_android_python.py b/tests/test_prefetch_android_python.py index f8932bb4..e86d9c3a 100644 --- a/tests/test_prefetch_android_python.py +++ b/tests/test_prefetch_android_python.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """tools/prefetch_android_python.py: a passing 404 no longer fails the Android build.""" import hashlib diff --git a/tests/test_voice_ceiling_consistency.py b/tests/test_voice_ceiling_consistency.py index f7bea867..65f2426e 100644 --- a/tests/test_voice_ceiling_consistency.py +++ b/tests/test_voice_ceiling_consistency.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """The voice ceiling lives in five places. This asserts they agree. Why this test exists, and why it is not paranoia: diff --git a/tools/check_lock_shape.py b/tools/check_lock_shape.py index ecff5177..c005dc3f 100644 --- a/tools/check_lock_shape.py +++ b/tools/check_lock_shape.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Fail if a ``deinit()`` does work outside the pump lock. The live-audio-path audit's rule, in one line: **a deinit() needs the lock diff --git a/tools/check_portable.py b/tools/check_portable.py index 24c66b56..6240dda3 100644 --- a/tools/check_portable.py +++ b/tools/check_portable.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Fail if this repo's C has learned about a platform. audiodsp is the DSP half of the audio stack. The same sources build as a diff --git a/tools/design_drift_table.py b/tools/design_drift_table.py index 43cb625e..28f5f546 100644 --- a/tools/design_drift_table.py +++ b/tools/design_drift_table.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Write (or check) the DriftResampler's polyphase interpolation table. tools/design_drift_table.py --write regenerate src/shared/audiodsp_drift_table.h diff --git a/tools/design_halfband.py b/tools/design_halfband.py index 8e6e71a8..69a7efdf 100644 --- a/tools/design_halfband.py +++ b/tools/design_halfband.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Design (and check) audiodsp_shaper.c's polyphase half-band coefficients. tools/design_halfband.py --verify check the shipped four diff --git a/tools/prefetch_android_python.py b/tools/prefetch_android_python.py index f0872239..12897395 100644 --- a/tools/prefetch_android_python.py +++ b/tools/prefetch_android_python.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Download cibuildwheel's Android Python tarballs ahead of the build, with retries. cibuildwheel downloads the target Python for each Android wheel once per