diff --git a/examples/poly106_demo.py b/examples/poly106_demo.py index bd4e3e7..dfa0da9 100644 --- a/examples/poly106_demo.py +++ b/examples/poly106_demo.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Play the Poly106: a pad progression, a bass line, brass stabs and a lead. python examples/poly106_demo.py # writes wav_exports/poly106_demo.wav diff --git a/examples/poly80_bench_mix.py b/examples/poly80_bench_mix.py index 76f61d3..37352c4 100644 --- a/examples/poly80_bench_mix.py +++ b/examples/poly80_bench_mix.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """The Poly80's cost with one channel heard: Init, eight notes, ``mix`` at channel I. python examples/poly80_bench_mix.py # CPython diff --git a/examples/poly_bench.py b/examples/poly_bench.py index 1344782..18af6dc 100644 --- a/examples/poly_bench.py +++ b/examples/poly_bench.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """How much of real time a polyphonic instrument takes. python examples/poly_bench.py [voice] [voices] # CPython diff --git a/examples/poly_render.py b/examples/poly_render.py index 6901a72..af8784d 100644 --- a/examples/poly_render.py +++ b/examples/poly_render.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Render chords through the polyphonic adapter and check the steals for clicks. python examples/poly_render.py [voice] # CPython diff --git a/lib/audioinstruments/_rack/_lazy.py b/lib/audioinstruments/_rack/_lazy.py index d964f8d..7690ace 100644 --- a/lib/audioinstruments/_rack/_lazy.py +++ b/lib/audioinstruments/_rack/_lazy.py @@ -3,7 +3,7 @@ """Import a package's public names on first use. A package keeps a table of name -> submodule path and defines a module-level -``__getattr__`` that calls :func:`load`. The first ``pkg.Name`` imports only the +``__getattr__`` that calls `load`. The first ``pkg.Name`` imports only the submodule that holds ``Name`` and stores the result in the package's globals, so every later access is a plain attribute read. """ diff --git a/lib/audioinstruments/_rack/dsp/ensemble.py b/lib/audioinstruments/_rack/dsp/ensemble.py index cb62cb7..3b05757 100644 --- a/lib/audioinstruments/_rack/dsp/ensemble.py +++ b/lib/audioinstruments/_rack/dsp/ensemble.py @@ -6,7 +6,7 @@ each swept by the sum of a slow and a fast LFO, the three 120 degrees apart on both. Their sum in mono is steady (the three sweeps add to zero), while the left and right sides each take a different mix of the three and spread apart. -It is a different animal from :class:`~audioinstruments._rack.dsp.chorus.BBDChorusCore`, which +It is a different animal from `BBDChorusCore `, which is two lines swept in antiphase by one triangle with the dry signal always mixed in, so it sits beside it on the same node. @@ -27,7 +27,7 @@ Those are the defaults. The number of lines and everything about each one are constructor parameters, so a different ensemble is a different list of -:class:`EnsembleLine` and a few keywords, not a different core: each line has +`EnsembleLine` and a few keywords, not a different core: each line has its own slow rate, fast ratio, phase (a fraction of a period, on both sweeps; 0.5 turns the sweep upside down), a level into the left and the right side, and a ``mix`` for its own node (2 is the wet alone, 1 the dry plus the wet). @@ -186,7 +186,7 @@ def sweep_table(sweep, table_len=TABLE_LEN): class EnsembleCore: """Swept lines, wet only, and a bypass input that is as late as they are. - ``lines`` is a sequence of :class:`EnsembleLine` (the string ensemble's + ``lines`` is a sequence of `EnsembleLine` (the string ensemble's three by default). The other keywords are the delay every line sweeps around (``centre_ms``), the depths of the slow and fast sweeps in ms, the lines' low-pass (``tone_hz``), the length of a sweep table and the slow diff --git a/lib/audioinstruments/_rack/dsp/organ.py b/lib/audioinstruments/_rack/dsp/organ.py index 13183ad..87c3b6d 100644 --- a/lib/audioinstruments/_rack/dsp/organ.py +++ b/lib/audioinstruments/_rack/dsp/organ.py @@ -5,10 +5,10 @@ An organ voice is a few pitches a key switches on together, each at a fixed interval from the key (a footage: 8' is the key's own pitch, 16' an octave under it, 4' an octave over it), each at a level a drawbar or a tab sets. -:class:`DrawbarToneCore` sums those pitches into one single-cycle table at a +`DrawbarToneCore` sums those pitches into one single-cycle table at a base pitch every footage is a whole harmonic of, and synthio plays the table. What each footage sounds like is the ``partial`` it is given: a tonewheel's is -a near-sine (:func:`sine_partial`); a divide-down organ's is a square or a +a near-sine (`sine_partial`); a divide-down organ's is a square or a pulse, which is one more function of the same form that sums a few odd sines. Which harmonic each footage plays on a given key (the foldback at the ends of the keyboard) belongs to the organ, so the voice hands the core both the @@ -17,8 +17,8 @@ A tonewheel organ has 91 wheels on one shaft, each in front of a pickup, so every pitch is a near-sine that is always turning; a key closes nine contacts that connect nine of them to the drawbars' busbars. The nine footages and the -harmonic each takes of the 16' pitch are in :data:`TONEWHEEL_FOOTAGES`; -:func:`tonewheel_harmonics` folds them at the keyboard's ends, as the organ +harmonic each takes of the 16' pitch are in `TONEWHEEL_FOOTAGES`; +`tonewheel_harmonics` folds them at the keyboard's ends, as the organ does because it has no wheels past its top and bottom. """ @@ -117,14 +117,14 @@ class DrawbarToneCore: ``harmonics`` lists every harmonic of the base pitch a footage may be asked to play (their partials are fetched once, here); ``partial(h)`` gives the - table of one footage at harmonic ``h`` (:func:`sine_partial` by default); + table of one footage at harmonic ``h`` (`sine_partial` by default); ``base_ratio`` is the table's pitch against the key's (0.5: the table is at 16', so the 8' is its second harmonic); ``level`` scales the sum; ``table_len`` is the table's length, which every partial shares (synthio plays the table without interpolating, so a longer one sounds cleaner). - :meth:`set_levels` rebuilds the table when a level or a harmonic changes, - in buffers the core owns. :meth:`render` plays it through a + `set_levels` rebuilds the table when a level or a harmonic changes, + in buffers the core owns. `render` plays it through a ``ChunkedBridge``, so the tone trails the controls by 256 frames. The contacts (a 3 ms ramp each way) follow the gate 256 frames late too, which is when the tone at the key's pitch arrives, so a note never begins on the diff --git a/lib/audioinstruments/_rack/modules/combo_organ/voice.py b/lib/audioinstruments/_rack/modules/combo_organ/voice.py index 6e4c2a3..7dd308b 100644 --- a/lib/audioinstruments/_rack/modules/combo_organ/voice.py +++ b/lib/audioinstruments/_rack/modules/combo_organ/voice.py @@ -148,7 +148,7 @@ def _band_hz(note): def _rolloff(hz): - """The divider's level at ``hz`` (48 kHz), from :data:`ROLLOFF`.""" + """The divider's level at ``hz`` (48 kHz), from `ROLLOFF`.""" x = hz / 1000.0 i = int(x) if i >= len(ROLLOFF) - 1: @@ -202,7 +202,7 @@ def __init__(self): self._sine = None def partial(self, key): - """The partial ``DrawbarToneCore`` sums for a footage key (see :func:`footage_key`).""" + """The partial ``DrawbarToneCore`` sums for a footage key (see `footage_key`).""" table = self.squares.get(key) if table is None: table = self._square(key[0], key[1], key[2]) @@ -232,7 +232,7 @@ def _rank_table(self, count): def _square(self, h, count, f1): """An int16 table holding ``h`` cycles of a square of ``count`` odd harmonics, - :data:`PARTIAL_UNIT` counts high, each harmonic rolled off as a divider's + `PARTIAL_UNIT` counts high, each harmonic rolled off as a divider's square at ``f1`` Hz would be.""" n = TONE_LEN sine = self._sine diff --git a/lib/audioinstruments/_rack/sequencer.py b/lib/audioinstruments/_rack/sequencer.py index edd2c1c..3690c25 100644 --- a/lib/audioinstruments/_rack/sequencer.py +++ b/lib/audioinstruments/_rack/sequencer.py @@ -165,7 +165,7 @@ def retrack(self, instrument, index=0, pattern=None): For a player changing kit mid-bar. The steps already on the queue belong to the instrument that is leaving, so they are taken back here rather than left to sound on a machine the dropdown says is - gone; :meth:`relay` lays them again on the new one. + gone; `relay` lays them again on the new one. """ if not instrument.schedulable: raise ValueError("%r cannot be scheduled" % instrument) diff --git a/tests/support/fake_pump.py b/tests/support/fake_pump.py index b02f0f0..3de28a8 100644 --- a/tests/support/fake_pump.py +++ b/tests/support/fake_pump.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A stand-in for ``audiopump.Events`` and ``audiopump.now`` for the sequencer. A `Sequencer` writes to its queue through two calls, ``q.at(frame, op, @@ -9,7 +13,7 @@ wrapping comparison, hands back a token counting from 1, and answers **0** rather than raising when the queue is full; * ``cancel()`` is True only while the event is still pending; -* :meth:`Events.apply` applies every event inside ``[now, now + block)`` in +* `Events.apply` applies every event inside ``[now, now + block)`` in frame order, and counts late ones; * ``stats()`` returns the numbers in the same order. diff --git a/tests/support/sf2_builder.py b/tests/support/sf2_builder.py index a395bc4..b1ab174 100644 --- a/tests/support/sf2_builder.py +++ b/tests/support/sf2_builder.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Write a SoundFont 2 bank from scratch, for the `_sf2` reader's tests. Nothing here reads a `.sf2`; it only writes one, chunk by chunk, from the diff --git a/tests/support/sf2_play.py b/tests/support/sf2_play.py index 1c655a4..7e75df4 100644 --- a/tests/support/sf2_play.py +++ b/tests/support/sf2_play.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Render a SoundFont instrument and measure what came out, for the `soundfont` tests. No numpy: these run wherever audiodsp builds.""" diff --git a/tests/support/sf2_smoke.py b/tests/support/sf2_smoke.py index 85bca0d..ca02c0e 100644 --- a/tests/support/sf2_smoke.py +++ b/tests/support/sf2_smoke.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """Build the tiny bank, read it back, check it: the one check that runs on every interpreter. diff --git a/tests/test_midi_dispatch.py b/tests/test_midi_dispatch.py index 8c3accc..a8a2b65 100644 --- a/tests/test_midi_dispatch.py +++ b/tests/test_midi_dispatch.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """`audioinstruments._rack.midi_dispatch`: MIDI in, instrument methods out. A recording instrument checks what each kind of input becomes: bytes with diff --git a/tests/test_sequencer.py b/tests/test_sequencer.py index d1d1f15..3663df2 100644 --- a/tests/test_sequencer.py +++ b/tests/test_sequencer.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """`audioinstruments._rack.sequencer.Sequencer`, driven by a clock a test moves. The sequencer's whole claim is that the groove does not depend on when diff --git a/tests/test_sf2.py b/tests/test_sf2.py index ef862af..7744556 100644 --- a/tests/test_sf2.py +++ b/tests/test_sf2.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """`_sf2`: a SoundFont 2 bank read back exactly. The bank under test is written by `tests/support/sf2_builder.py` from the diff --git a/tests/test_soundfont.py b/tests/test_soundfont.py index 86f5268..25055d6 100644 --- a/tests/test_soundfont.py +++ b/tests/test_soundfont.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """`soundfont` and `soundfont_drums` on the bank `sf2_builder` writes. The plumbing, without a real bank: how the bank is found, the patch list diff --git a/tests/test_soundfont_gm.py b/tests/test_soundfont_gm.py index 744a2d9..70bc4b7 100644 --- a/tests/test_soundfont_gm.py +++ b/tests/test_soundfont_gm.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """The General MIDI gate: a real bank plays every program and the kit. Against FluidR3_GM.sf2 (MIT), looked for at `$FLUIDR3_GM`, then diff --git a/tests/test_soundfont_heap.py b/tests/test_soundfont_heap.py index c5c8b26..2d89b18 100644 --- a/tests/test_soundfont_heap.py +++ b/tests/test_soundfont_heap.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + """A second SoundFont instrument after a big bank has loaded. Resolving every preset when a bank opened left a MicroPython heap in pieces