From 83b8e8b7bff1c966196c0291d15dd1ed4effdc08 Mon Sep 17 00:00:00 2001 From: Brad Barnett <127794626+bdbarnett@users.noreply.github.com> Date: Sat, 31 Oct 2026 04:26:11 -0500 Subject: [PATCH 1/3] Move the rack into the package: vrack becomes audioinstruments._rack lib/vrack moves to lib/audioinstruments/_rack, so the wheel and a frozen build ship one package. Imports, module paths and docs follow; prose that named the package now says the rack. VERSION moves to 0.5.0.dev2 and every face is regenerated with it. --- CONTRIBUTING.md | 20 +- ROADMAP.md | 6 +- VERSION | 2 +- docs/allocation.md | 2 +- docs/architecture.md | 10 +- docs/audiodsp.md | 20 +- docs/instruments.md | 148 ++++---- docs/overview.md | 22 +- docs/performance.md | 6 +- docs/polyphony.md | 12 +- docs/soundfont.md | 18 +- examples/bass_pedals_demo.py | 2 +- examples/clavichord_demo.py | 4 +- examples/combo_organ_demo.py | 2 +- examples/compact_organ_demo.py | 2 +- examples/duosynth_demo.py | 2 +- examples/electric_grand70_demo.py | 4 +- examples/electric_piano_demo.py | 4 +- examples/electronic_drums5_demo.py | 2 +- examples/headless_synth.py | 10 +- examples/hmi_board.py | 8 +- examples/hmi_instrument.py | 6 +- examples/mono101_demo.py | 2 +- examples/mono_synth_demo.py | 2 +- examples/patch2600_demo.py | 6 +- examples/play_audio.py | 4 +- examples/poly106_demo.py | 4 +- examples/poly5_demo.py | 2 +- examples/poly6_demo.py | 2 +- examples/poly80_bench_mix.py | 2 +- examples/poly80_demo.py | 2 +- examples/poly8_demo.py | 2 +- examples/poly_bench.py | 2 +- examples/poly_render.py | 2 +- examples/polya6_demo.py | 2 +- examples/polyxa_demo.py | 2 +- examples/rackview/__init__.py | 2 +- examples/rackview/index.html | 6 +- examples/rackview/lvgl_panel.py | 8 +- examples/reed_piano200a_demo.py | 4 +- examples/reed_piano_demo.py | 4 +- examples/rhythm606_demo.py | 2 +- examples/rhythm78_demo.py | 2 +- examples/rhythm808_demo.py | 2 +- examples/rhythm909_demo.py | 2 +- examples/soundfont_demo.py | 10 +- examples/string_ensemble_demo.py | 2 +- examples/tonewheel3_demo.py | 4 +- examples/vocoder330_demo.py | 4 +- lib/audioinstruments/__init__.py | 6 +- lib/audioinstruments/_names.py | 2 +- .../_rack}/__init__.py | 8 +- .../_rack}/_component.py | 2 +- lib/{vrack => audioinstruments/_rack}/_gm.py | 0 .../_rack}/_lazy.py | 0 lib/{vrack => audioinstruments/_rack}/_sf2.py | 0 .../_rack}/adapter.py | 18 +- .../_rack}/core/__init__.py | 0 .../_rack}/core/bridge.py | 20 +- .../_rack}/core/cable.py | 0 .../_rack}/core/graph.py | 0 .../_rack}/core/jack.py | 0 .../_rack}/core/module.py | 4 +- .../_rack}/core/poly.py | 0 .../_rack}/core/rack.py | 0 .../_rack}/core/signal.py | 0 .../_rack}/dsp/__init__.py | 0 .../_rack}/dsp/amplifier.py | 0 .../_rack}/dsp/biquad.py | 0 .../_rack}/dsp/blocks.py | 0 .../_rack}/dsp/chorus.py | 0 .../_rack}/dsp/delay.py | 0 .../_rack}/dsp/drums.py | 0 .../_rack}/dsp/ensemble.py | 2 +- .../_rack}/dsp/envelope.py | 0 .../_rack}/dsp/function_generator.py | 0 .../_rack}/dsp/glide.py | 0 .../_rack}/dsp/granular.py | 0 .../_rack}/dsp/keys.py | 0 .../_rack}/dsp/ladder.py | 2 +- .../_rack}/dsp/lfo.py | 0 .../_rack}/dsp/lpg.py | 0 .../_rack}/dsp/macro_oscillator.py | 0 .../_rack}/dsp/modal.py | 0 .../_rack}/dsp/noise.py | 0 .../_rack}/dsp/organ.py | 0 .../_rack}/dsp/oscillator.py | 0 .../_rack}/dsp/reverb.py | 2 +- .../_rack}/dsp/rotary.py | 0 .../_rack}/dsp/saturation.py | 0 .../_rack}/dsp/sequencers.py | 0 .../_rack}/dsp/tremolo.py | 0 .../_rack}/dsp/twopole.py | 2 +- .../_rack}/dsp/utility.py | 0 .../_rack}/dsp/wavefolder.py | 0 .../_rack}/dsp/west_coast.py | 0 lib/{vrack => audioinstruments/_rack}/hmi.py | 2 +- .../_rack}/instruments/__init__.py | 2 +- .../_rack}/instruments/bass_pedals.py | 4 +- .../_rack}/instruments/clavichord.py | 4 +- .../_rack}/instruments/combo_organ.py | 4 +- .../_rack}/instruments/compact_organ.py | 4 +- .../_rack}/instruments/duosynth.py | 4 +- .../_rack}/instruments/electric_grand70.py | 4 +- .../_rack}/instruments/electric_piano.py | 4 +- .../_rack}/instruments/electronic_drums5.py | 4 +- .../_rack}/instruments/mono101.py | 4 +- .../_rack}/instruments/mono_synth.py | 4 +- .../_rack}/instruments/poly106.py | 6 +- .../_rack}/instruments/poly5.py | 6 +- .../_rack}/instruments/poly6.py | 6 +- .../_rack}/instruments/poly8.py | 4 +- .../_rack}/instruments/poly80.py | 4 +- .../_rack}/instruments/polya6.py | 4 +- .../_rack}/instruments/polyxa.py | 4 +- .../_rack}/instruments/reed_piano.py | 4 +- .../_rack}/instruments/reed_piano200a.py | 4 +- .../_rack}/instruments/rhythm606.py | 4 +- .../_rack}/instruments/rhythm78.py | 4 +- .../_rack}/instruments/rhythm808.py | 4 +- .../_rack}/instruments/rhythm909.py | 4 +- .../_rack}/instruments/string_ensemble.py | 4 +- .../_rack}/instruments/tonewheel3.py | 4 +- .../_rack}/instruments/vocoder330.py | 6 +- .../_rack}/midi_cc.py | 4 +- .../_rack}/midi_dispatch.py | 4 +- .../_rack}/modules/__init__.py | 4 +- .../_rack}/modules/_util.py | 0 .../_rack}/modules/acid303.py | 0 .../_rack}/modules/bass_pedals/__init__.py | 2 +- .../_rack}/modules/bass_pedals/voice.py | 0 .../_rack}/modules/clavichord/__init__.py | 2 +- .../_rack}/modules/clavichord/output.py | 0 .../_rack}/modules/clavichord/voice.py | 0 .../_rack}/modules/combo_organ/__init__.py | 0 .../_rack}/modules/combo_organ/output.py | 2 +- .../_rack}/modules/combo_organ/vibrato.py | 0 .../_rack}/modules/combo_organ/voice.py | 0 .../_rack}/modules/compact_organ/__init__.py | 0 .../_rack}/modules/compact_organ/output.py | 0 .../_rack}/modules/compact_organ/vibrato.py | 0 .../_rack}/modules/compact_organ/voice.py | 0 .../_rack}/modules/drum_synth.py | 0 .../_rack}/modules/duo20.py | 2 +- .../_rack}/modules/duosynth/__init__.py | 2 +- .../_rack}/modules/duosynth/lfo.py | 0 .../_rack}/modules/duosynth/output.py | 0 .../_rack}/modules/duosynth/voice.py | 0 .../modules/electric_grand70/__init__.py | 2 +- .../_rack}/modules/electric_grand70/output.py | 0 .../_rack}/modules/electric_grand70/voice.py | 0 .../_rack}/modules/electric_piano/__init__.py | 2 +- .../_rack}/modules/electric_piano/output.py | 0 .../_rack}/modules/electric_piano/voice.py | 0 .../modules/electronic_drums5/__init__.py | 0 .../_rack}/modules/electronic_drums5/voice.py | 0 .../_rack}/modules/function_generator.py | 0 .../_rack}/modules/granular_processor.py | 0 .../_rack}/modules/hybrid_mono.py | 2 +- .../_rack}/modules/macro_oscillator.py | 0 .../_rack}/modules/midi_cv.py | 0 .../_rack}/modules/modal_resonator.py | 0 .../_rack}/modules/modular100/__init__.py | 0 .../_rack}/modules/modular100/env_lfo.py | 0 .../_rack}/modules/modular100/utility.py | 0 .../_rack}/modules/modular100/vca.py | 0 .../_rack}/modules/modular100/vcf.py | 0 .../_rack}/modules/modular100/vco.py | 0 .../_rack}/modules/modular55/__init__.py | 0 .../_rack}/modules/modular55/envelope.py | 0 .../_rack}/modules/modular55/highpass.py | 0 .../_rack}/modules/modular55/lowpass.py | 0 .../_rack}/modules/modular55/vca.py | 0 .../_rack}/modules/modular55/vco.py | 0 .../_rack}/modules/mono101/__init__.py | 2 +- .../_rack}/modules/mono101/lfo.py | 0 .../_rack}/modules/mono101/voice.py | 0 .../_rack}/modules/mono_one.py | 0 .../_rack}/modules/mono_synth/__init__.py | 2 +- .../_rack}/modules/mono_synth/voice.py | 0 .../_rack}/modules/paraphonic_synth.py | 0 .../_rack}/modules/patch2600.py | 0 .../_rack}/modules/pin_matrix3.py | 0 .../_rack}/modules/poly106/__init__.py | 2 +- .../_rack}/modules/poly106/lfo.py | 0 .../_rack}/modules/poly106/output.py | 4 +- .../_rack}/modules/poly106/voice.py | 2 +- .../_rack}/modules/poly5/__init__.py | 2 +- .../_rack}/modules/poly5/lfo.py | 2 +- .../_rack}/modules/poly5/voice.py | 4 +- .../_rack}/modules/poly6/__init__.py | 0 .../_rack}/modules/poly6/lfo.py | 2 +- .../_rack}/modules/poly6/output.py | 2 +- .../_rack}/modules/poly6/voice.py | 2 +- .../_rack}/modules/poly8/__init__.py | 2 +- .../_rack}/modules/poly8/lfo.py | 0 .../_rack}/modules/poly8/voice.py | 2 +- .../_rack}/modules/poly80/__init__.py | 2 +- .../_rack}/modules/poly80/mod.py | 0 .../_rack}/modules/poly80/output.py | 0 .../_rack}/modules/poly80/voice.py | 0 .../_rack}/modules/poly_midi_cv.py | 2 +- .../_rack}/modules/polya6/__init__.py | 2 +- .../_rack}/modules/polya6/noise.py | 0 .../_rack}/modules/polya6/output.py | 0 .../_rack}/modules/polya6/voice.py | 0 .../_rack}/modules/polyrhythm_synth.py | 0 .../_rack}/modules/polyxa/__init__.py | 2 +- .../_rack}/modules/polyxa/lfo.py | 0 .../_rack}/modules/polyxa/output.py | 0 .../_rack}/modules/polyxa/voice.py | 2 +- .../_rack}/modules/random_sequencer.py | 0 .../_rack}/modules/reed_piano/__init__.py | 0 .../_rack}/modules/reed_piano/output.py | 0 .../_rack}/modules/reed_piano/voice.py | 0 .../_rack}/modules/reed_piano200a/__init__.py | 0 .../_rack}/modules/reed_piano200a/output.py | 0 .../_rack}/modules/reed_piano200a/voice.py | 0 .../_rack}/modules/rhythm606/__init__.py | 0 .../_rack}/modules/rhythm606/bass_drum.py | 0 .../_rack}/modules/rhythm606/cymbal.py | 0 .../_rack}/modules/rhythm606/hats.py | 0 .../_rack}/modules/rhythm606/snare.py | 0 .../_rack}/modules/rhythm606/tom.py | 0 .../_rack}/modules/rhythm78/__init__.py | 0 .../_rack}/modules/rhythm78/cowbell.py | 0 .../_rack}/modules/rhythm78/drum.py | 0 .../_rack}/modules/rhythm78/guiro.py | 0 .../_rack}/modules/rhythm78/hats.py | 0 .../_rack}/modules/rhythm78/output.py | 0 .../_rack}/modules/rhythm78/snare.py | 0 .../_rack}/modules/rhythm78/tambourine.py | 0 .../_rack}/modules/rhythm808/__init__.py | 0 .../_rack}/modules/rhythm808/bass_drum.py | 0 .../_rack}/modules/rhythm808/clap.py | 0 .../_rack}/modules/rhythm808/claves.py | 0 .../_rack}/modules/rhythm808/conga.py | 0 .../_rack}/modules/rhythm808/cowbell.py | 0 .../_rack}/modules/rhythm808/cymbal.py | 0 .../_rack}/modules/rhythm808/hats.py | 0 .../_rack}/modules/rhythm808/maracas.py | 0 .../_rack}/modules/rhythm808/output.py | 0 .../_rack}/modules/rhythm808/rim_shot.py | 0 .../_rack}/modules/rhythm808/snare.py | 0 .../_rack}/modules/rhythm808/tom.py | 0 .../_rack}/modules/rhythm909/__init__.py | 0 .../_rack}/modules/rhythm909/bass_drum.py | 0 .../_rack}/modules/rhythm909/clap.py | 0 .../_rack}/modules/rhythm909/cymbal.py | 0 .../_rack}/modules/rhythm909/hats.py | 0 .../_rack}/modules/rhythm909/rim_shot.py | 0 .../_rack}/modules/rhythm909/snare.py | 0 .../_rack}/modules/rhythm909/tom.py | 0 .../_rack}/modules/rotary_speaker.py | 0 .../_rack}/modules/semi32.py | 0 .../_rack}/modules/state_variable_mono.py | 0 .../modules/string_ensemble/__init__.py | 0 .../_rack}/modules/string_ensemble/bass.py | 0 .../modules/string_ensemble/envelope.py | 0 .../_rack}/modules/string_ensemble/output.py | 0 .../_rack}/modules/string_ensemble/voice.py | 0 .../_rack}/modules/tonewheel3/__init__.py | 0 .../_rack}/modules/tonewheel3/expression.py | 0 .../_rack}/modules/tonewheel3/percussion.py | 0 .../_rack}/modules/tonewheel3/rotary.py | 0 .../_rack}/modules/tonewheel3/vibrato.py | 0 .../_rack}/modules/tonewheel3/voice.py | 0 .../_rack}/modules/tremolo.py | 0 .../_rack}/modules/vocoder330/__init__.py | 0 .../_rack}/modules/vocoder330/bank.py | 0 .../_rack}/modules/vocoder330/filters.py | 0 .../_rack}/modules/vocoder330/keys.py | 0 .../_rack}/modules/vocoder330/mic.py | 2 +- .../_rack}/modules/vocoder330/output.py | 0 .../_rack}/modules/vocoder330/voice.py | 0 .../_rack}/modules/voice_mix.py | 2 +- .../_rack}/modules/west_coast0.py | 0 .../_rack}/modules/west_coast_synth.py | 0 .../_rack}/sequencer.py | 2 +- .../_rack}/soundfont.py | 2 +- .../_rack}/soundfont_drums.py | 2 +- lib/audioinstruments/acid303.py | 4 +- lib/audioinstruments/bass_pedals.py | 4 +- lib/audioinstruments/clavichord.py | 4 +- lib/audioinstruments/combo_organ.py | 4 +- lib/audioinstruments/compact_organ.py | 4 +- lib/audioinstruments/duo20.py | 4 +- lib/audioinstruments/duosynth.py | 4 +- lib/audioinstruments/electric_grand70.py | 4 +- lib/audioinstruments/electric_piano.py | 4 +- lib/audioinstruments/electronic_drums5.py | 4 +- lib/audioinstruments/hybrid_mono.py | 4 +- lib/audioinstruments/midi_cc.py | 4 +- lib/audioinstruments/midi_dispatch.py | 4 +- lib/audioinstruments/mono101.py | 4 +- lib/audioinstruments/mono_one.py | 4 +- lib/audioinstruments/mono_synth.py | 4 +- lib/audioinstruments/patch2600.py | 4 +- lib/audioinstruments/poly106.py | 4 +- lib/audioinstruments/poly5.py | 4 +- lib/audioinstruments/poly6.py | 4 +- lib/audioinstruments/poly8.py | 4 +- lib/audioinstruments/poly80.py | 4 +- lib/audioinstruments/polya6.py | 4 +- lib/audioinstruments/polyxa.py | 4 +- lib/audioinstruments/reed_piano.py | 4 +- lib/audioinstruments/reed_piano200a.py | 4 +- lib/audioinstruments/rhythm606.py | 4 +- lib/audioinstruments/rhythm78.py | 4 +- lib/audioinstruments/rhythm808.py | 4 +- lib/audioinstruments/rhythm909.py | 4 +- lib/audioinstruments/sequencer.py | 4 +- lib/audioinstruments/soundfont.py | 4 +- lib/audioinstruments/soundfont_drums.py | 4 +- lib/audioinstruments/state_variable_mono.py | 4 +- lib/audioinstruments/string_ensemble.py | 4 +- lib/audioinstruments/tonewheel3.py | 4 +- lib/audioinstruments/vocoder330.py | 4 +- lib/audioinstruments/west_coast_synth.py | 4 +- manifest.py | 1 - tests/conftest.py | 2 +- tests/mpy/bench.py | 4 +- tests/mpy/probe_nodes.py | 16 +- tests/mpy/run.py | 12 +- tests/mpy/test_alloc.py | 33 +- tests/mpy/test_bass_pedals.py | 14 +- tests/mpy/test_bridge.py | 10 +- tests/mpy/test_clavichord.py | 2 +- tests/mpy/test_combo_organ.py | 10 +- tests/mpy/test_compact_organ.py | 10 +- tests/mpy/test_duosynth.py | 18 +- tests/mpy/test_electric_grand70.py | 2 +- tests/mpy/test_electric_piano.py | 2 +- tests/mpy/test_electronic_drums5.py | 4 +- tests/mpy/test_ensemble.py | 4 +- tests/mpy/test_graph_mono_duo_a6.py | 2 +- tests/mpy/test_graph_poly106.py | 2 +- tests/mpy/test_graph_poly5.py | 2 +- tests/mpy/test_graph_poly6.py | 2 +- tests/mpy/test_graph_pump.py | 2 +- tests/mpy/test_kernels.py | 2 +- tests/mpy/test_lazy.py | 92 ++--- tests/mpy/test_midi_cv_duo.py | 2 +- tests/mpy/test_midi_cv_expression.py | 6 +- tests/mpy/test_mono.py | 8 +- tests/mpy/test_mono101.py | 2 +- tests/mpy/test_mono_synth.py | 2 +- tests/mpy/test_poly.py | 4 +- tests/mpy/test_poly106.py | 2 +- tests/mpy/test_poly5.py | 6 +- tests/mpy/test_poly6.py | 2 +- tests/mpy/test_poly8.py | 8 +- tests/mpy/test_poly80.py | 12 +- tests/mpy/test_poly_buses.py | 6 +- tests/mpy/test_polya6.py | 18 +- tests/mpy/test_polyxa.py | 18 +- tests/mpy/test_pressure.py | 8 +- tests/mpy/test_reed_piano.py | 2 +- tests/mpy/test_reed_piano200a.py | 2 +- tests/mpy/test_rhythm606.py | 4 +- tests/mpy/test_rhythm78.py | 4 +- tests/mpy/test_rhythm808.py | 4 +- tests/mpy/test_rhythm909.py | 8 +- tests/mpy/test_soundfont.py | 10 +- tests/mpy/test_string_ensemble.py | 14 +- tests/mpy/test_tonewheel3.py | 10 +- tests/mpy/test_unison.py | 4 +- tests/mpy/test_vocoder330.py | 14 +- tests/poly_support.py | 2 +- tests/support/sf2_builder.py | 2 +- tests/support/sf2_smoke.py | 2 +- tests/test_adapter.py | 4 +- tests/test_allocation.py | 321 +++++++++--------- tests/test_audio_buffer_protocol.py | 2 +- tests/test_audio_export.py | 6 +- tests/test_ci_scope.py | 108 +++--- tests/test_core.py | 8 +- tests/test_core_bridge.py | 2 +- tests/test_core_graph.py | 6 +- tests/test_core_kernels.py | 2 +- tests/test_core_midi_cv.py | 2 +- tests/test_core_schedule.py | 2 +- tests/test_core_serialization.py | 4 +- tests/test_dsp_cores.py | 18 +- tests/test_dsp_drums.py | 6 +- tests/test_dsp_ensemble.py | 10 +- tests/test_dsp_keys.py | 6 +- tests/test_ensemble_core.py | 4 +- tests/test_eurorack_function_generator.py | 4 +- tests/test_eurorack_granular_processor.py | 4 +- tests/test_eurorack_macro_oscillator.py | 4 +- tests/test_eurorack_modal_resonator.py | 4 +- tests/test_eurorack_modular100.py | 6 +- tests/test_eurorack_modular55.py | 6 +- tests/test_eurorack_random_sequencer.py | 4 +- tests/test_face.py | 6 +- tests/test_graph_buffer_reuse.py | 6 +- tests/test_graph_duo20.py | 14 +- tests/test_graph_duosynth.py | 16 +- tests/test_graph_hybrid_mono.py | 8 +- tests/test_graph_live_patch.py | 2 +- tests/test_graph_modal_resonator.py | 2 +- tests/test_graph_mono101.py | 22 +- tests/test_graph_monosynth.py | 12 +- tests/test_graph_poly106.py | 14 +- tests/test_graph_poly5.py | 16 +- tests/test_graph_poly6.py | 14 +- tests/test_graph_poly8.py | 18 +- tests/test_graph_polya6.py | 24 +- tests/test_graph_polyxa.py | 20 +- tests/test_graph_pump.py | 2 +- tests/test_graph_pump_swap.py | 4 +- tests/test_graph_voice_mix_pan.py | 4 +- tests/test_graph_voice_mix_park.py | 2 +- tests/test_graph_west_coast_synth.py | 4 +- tests/test_hmi.py | 6 +- tests/test_lazy_imports.py | 2 +- tests/test_merged_macros.py | 2 +- tests/test_midi_cc.py | 6 +- tests/test_midi_cv_changed_only.py | 12 +- tests/test_midi_cv_duo.py | 4 +- tests/test_midi_cv_expression.py | 8 +- tests/test_midi_dispatch.py | 8 +- tests/test_mono_mode.py | 14 +- tests/test_patch_files.py | 18 +- tests/test_poly_allocator.py | 2 +- tests/test_poly_buses.py | 6 +- tests/test_poly_kit.py | 4 +- tests/test_poly_pool.py | 10 +- tests/test_pool_note_unison.py | 4 +- tests/test_pressure.py | 8 +- tests/test_sampler_adapter.py | 6 +- tests/test_sequencer.py | 10 +- tests/test_sf2.py | 2 +- tests/test_showcase_integrity.py | 6 +- tests/test_soundfont.py | 8 +- tests/test_soundfont_gm.py | 2 +- tests/test_synth_acid303.py | 4 +- tests/test_synth_bass_pedals.py | 22 +- tests/test_synth_clavichord.py | 6 +- tests/test_synth_combo_organ.py | 4 +- tests/test_synth_compact_organ.py | 4 +- tests/test_synth_drum_synth.py | 4 +- tests/test_synth_duo20.py | 6 +- tests/test_synth_duosynth.py | 20 +- tests/test_synth_electric_grand70.py | 4 +- tests/test_synth_electric_piano.py | 4 +- tests/test_synth_electronic_drums5.py | 4 +- tests/test_synth_hybrid_mono.py | 6 +- tests/test_synth_mono101.py | 6 +- tests/test_synth_mono_one.py | 6 +- tests/test_synth_mono_synth.py | 6 +- tests/test_synth_paraphonic_synth.py | 8 +- tests/test_synth_patch2600.py | 8 +- tests/test_synth_pin_matrix3.py | 6 +- tests/test_synth_poly106.py | 6 +- tests/test_synth_poly5.py | 6 +- tests/test_synth_poly6.py | 12 +- tests/test_synth_poly8.py | 10 +- tests/test_synth_poly80.py | 17 +- tests/test_synth_polya6.py | 23 +- tests/test_synth_polyrhythm_synth.py | 4 +- tests/test_synth_polyxa.py | 12 +- tests/test_synth_reed_piano.py | 4 +- tests/test_synth_reed_piano200a.py | 4 +- tests/test_synth_rhythm606.py | 4 +- tests/test_synth_rhythm78.py | 4 +- tests/test_synth_rhythm808.py | 4 +- tests/test_synth_rhythm909.py | 4 +- tests/test_synth_semi32.py | 6 +- tests/test_synth_state_variable_mono.py | 6 +- tests/test_synth_string_ensemble.py | 22 +- tests/test_synth_tonewheel3.py | 18 +- tests/test_synth_vocoder330.py | 26 +- tests/test_synth_west_coast0.py | 4 +- tests/test_synth_west_coast_synth.py | 6 +- tests/test_traits.py | 2 +- tools/ci_scope.py | 38 ++- tools/graph_phrase.py | 2 +- tools/live_patch.py | 10 +- tools/make_faces.py | 12 +- tools/modal_phrase.py | 6 +- tools/pump_swap.py | 8 +- tools/traits.py | 29 +- tools/traits_render.py | 2 +- tools/west_coast_phrase.py | 6 +- 486 files changed, 1331 insertions(+), 1263 deletions(-) rename lib/{vrack => audioinstruments/_rack}/__init__.py (87%) rename lib/{vrack => audioinstruments/_rack}/_component.py (99%) rename lib/{vrack => audioinstruments/_rack}/_gm.py (100%) rename lib/{vrack => audioinstruments/_rack}/_lazy.py (100%) rename lib/{vrack => audioinstruments/_rack}/_sf2.py (100%) rename lib/{vrack => audioinstruments/_rack}/adapter.py (98%) rename lib/{vrack => audioinstruments/_rack}/core/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/core/bridge.py (96%) rename lib/{vrack => audioinstruments/_rack}/core/cable.py (100%) rename lib/{vrack => audioinstruments/_rack}/core/graph.py (100%) rename lib/{vrack => audioinstruments/_rack}/core/jack.py (100%) rename lib/{vrack => audioinstruments/_rack}/core/module.py (98%) rename lib/{vrack => audioinstruments/_rack}/core/poly.py (100%) rename lib/{vrack => audioinstruments/_rack}/core/rack.py (100%) rename lib/{vrack => audioinstruments/_rack}/core/signal.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/amplifier.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/biquad.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/blocks.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/chorus.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/delay.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/drums.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/ensemble.py (99%) rename lib/{vrack => audioinstruments/_rack}/dsp/envelope.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/function_generator.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/glide.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/granular.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/keys.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/ladder.py (99%) rename lib/{vrack => audioinstruments/_rack}/dsp/lfo.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/lpg.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/macro_oscillator.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/modal.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/noise.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/organ.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/oscillator.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/reverb.py (96%) rename lib/{vrack => audioinstruments/_rack}/dsp/rotary.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/saturation.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/sequencers.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/tremolo.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/twopole.py (98%) rename lib/{vrack => audioinstruments/_rack}/dsp/utility.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/wavefolder.py (100%) rename lib/{vrack => audioinstruments/_rack}/dsp/west_coast.py (100%) rename lib/{vrack => audioinstruments/_rack}/hmi.py (99%) rename lib/{vrack => audioinstruments/_rack}/instruments/__init__.py (70%) rename lib/{vrack => audioinstruments/_rack}/instruments/bass_pedals.py (98%) rename lib/{vrack => audioinstruments/_rack}/instruments/clavichord.py (97%) rename lib/{vrack => audioinstruments/_rack}/instruments/combo_organ.py (98%) rename lib/{vrack => audioinstruments/_rack}/instruments/compact_organ.py (98%) rename lib/{vrack => audioinstruments/_rack}/instruments/duosynth.py (98%) rename lib/{vrack => audioinstruments/_rack}/instruments/electric_grand70.py (97%) rename lib/{vrack => audioinstruments/_rack}/instruments/electric_piano.py (97%) rename lib/{vrack => audioinstruments/_rack}/instruments/electronic_drums5.py (98%) rename lib/{vrack => audioinstruments/_rack}/instruments/mono101.py (98%) rename lib/{vrack => audioinstruments/_rack}/instruments/mono_synth.py (99%) rename lib/{vrack => audioinstruments/_rack}/instruments/poly106.py (98%) rename lib/{vrack => audioinstruments/_rack}/instruments/poly5.py (98%) rename lib/{vrack => audioinstruments/_rack}/instruments/poly6.py (98%) rename lib/{vrack => audioinstruments/_rack}/instruments/poly8.py (99%) rename lib/{vrack => audioinstruments/_rack}/instruments/poly80.py (99%) rename lib/{vrack => audioinstruments/_rack}/instruments/polya6.py (99%) rename lib/{vrack => audioinstruments/_rack}/instruments/polyxa.py (99%) rename lib/{vrack => audioinstruments/_rack}/instruments/reed_piano.py (97%) rename lib/{vrack => audioinstruments/_rack}/instruments/reed_piano200a.py (97%) rename lib/{vrack => audioinstruments/_rack}/instruments/rhythm606.py (98%) rename lib/{vrack => audioinstruments/_rack}/instruments/rhythm78.py (98%) rename lib/{vrack => audioinstruments/_rack}/instruments/rhythm808.py (98%) rename lib/{vrack => audioinstruments/_rack}/instruments/rhythm909.py (98%) rename lib/{vrack => audioinstruments/_rack}/instruments/string_ensemble.py (98%) rename lib/{vrack => audioinstruments/_rack}/instruments/tonewheel3.py (98%) rename lib/{vrack => audioinstruments/_rack}/instruments/vocoder330.py (98%) rename lib/{vrack => audioinstruments/_rack}/midi_cc.py (99%) rename lib/{vrack => audioinstruments/_rack}/midi_dispatch.py (99%) rename lib/{vrack => audioinstruments/_rack}/modules/__init__.py (98%) rename lib/{vrack => audioinstruments/_rack}/modules/_util.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/acid303.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/bass_pedals/__init__.py (86%) rename lib/{vrack => audioinstruments/_rack}/modules/bass_pedals/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/clavichord/__init__.py (88%) rename lib/{vrack => audioinstruments/_rack}/modules/clavichord/output.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/clavichord/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/combo_organ/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/combo_organ/output.py (99%) rename lib/{vrack => audioinstruments/_rack}/modules/combo_organ/vibrato.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/combo_organ/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/compact_organ/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/compact_organ/output.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/compact_organ/vibrato.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/compact_organ/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/drum_synth.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/duo20.py (99%) rename lib/{vrack => audioinstruments/_rack}/modules/duosynth/__init__.py (90%) rename lib/{vrack => audioinstruments/_rack}/modules/duosynth/lfo.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/duosynth/output.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/duosynth/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/electric_grand70/__init__.py (89%) rename lib/{vrack => audioinstruments/_rack}/modules/electric_grand70/output.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/electric_grand70/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/electric_piano/__init__.py (89%) rename lib/{vrack => audioinstruments/_rack}/modules/electric_piano/output.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/electric_piano/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/electronic_drums5/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/electronic_drums5/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/function_generator.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/granular_processor.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/hybrid_mono.py (99%) rename lib/{vrack => audioinstruments/_rack}/modules/macro_oscillator.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/midi_cv.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/modal_resonator.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/modular100/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/modular100/env_lfo.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/modular100/utility.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/modular100/vca.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/modular100/vcf.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/modular100/vco.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/modular55/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/modular55/envelope.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/modular55/highpass.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/modular55/lowpass.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/modular55/vca.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/modular55/vco.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/mono101/__init__.py (88%) rename lib/{vrack => audioinstruments/_rack}/modules/mono101/lfo.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/mono101/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/mono_one.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/mono_synth/__init__.py (88%) rename lib/{vrack => audioinstruments/_rack}/modules/mono_synth/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/paraphonic_synth.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/patch2600.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/pin_matrix3.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/poly106/__init__.py (88%) rename lib/{vrack => audioinstruments/_rack}/modules/poly106/lfo.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/poly106/output.py (97%) rename lib/{vrack => audioinstruments/_rack}/modules/poly106/voice.py (99%) rename lib/{vrack => audioinstruments/_rack}/modules/poly5/__init__.py (88%) rename lib/{vrack => audioinstruments/_rack}/modules/poly5/lfo.py (98%) rename lib/{vrack => audioinstruments/_rack}/modules/poly5/voice.py (99%) rename lib/{vrack => audioinstruments/_rack}/modules/poly6/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/poly6/lfo.py (98%) rename lib/{vrack => audioinstruments/_rack}/modules/poly6/output.py (99%) rename lib/{vrack => audioinstruments/_rack}/modules/poly6/voice.py (99%) rename lib/{vrack => audioinstruments/_rack}/modules/poly8/__init__.py (88%) rename lib/{vrack => audioinstruments/_rack}/modules/poly8/lfo.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/poly8/voice.py (99%) rename lib/{vrack => audioinstruments/_rack}/modules/poly80/__init__.py (89%) rename lib/{vrack => audioinstruments/_rack}/modules/poly80/mod.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/poly80/output.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/poly80/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/poly_midi_cv.py (99%) rename lib/{vrack => audioinstruments/_rack}/modules/polya6/__init__.py (89%) rename lib/{vrack => audioinstruments/_rack}/modules/polya6/noise.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/polya6/output.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/polya6/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/polyrhythm_synth.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/polyxa/__init__.py (88%) rename lib/{vrack => audioinstruments/_rack}/modules/polyxa/lfo.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/polyxa/output.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/polyxa/voice.py (99%) rename lib/{vrack => audioinstruments/_rack}/modules/random_sequencer.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/reed_piano/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/reed_piano/output.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/reed_piano/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/reed_piano200a/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/reed_piano200a/output.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/reed_piano200a/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm606/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm606/bass_drum.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm606/cymbal.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm606/hats.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm606/snare.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm606/tom.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm78/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm78/cowbell.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm78/drum.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm78/guiro.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm78/hats.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm78/output.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm78/snare.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm78/tambourine.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm808/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm808/bass_drum.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm808/clap.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm808/claves.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm808/conga.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm808/cowbell.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm808/cymbal.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm808/hats.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm808/maracas.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm808/output.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm808/rim_shot.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm808/snare.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm808/tom.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm909/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm909/bass_drum.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm909/clap.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm909/cymbal.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm909/hats.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm909/rim_shot.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm909/snare.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rhythm909/tom.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/rotary_speaker.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/semi32.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/state_variable_mono.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/string_ensemble/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/string_ensemble/bass.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/string_ensemble/envelope.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/string_ensemble/output.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/string_ensemble/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/tonewheel3/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/tonewheel3/expression.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/tonewheel3/percussion.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/tonewheel3/rotary.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/tonewheel3/vibrato.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/tonewheel3/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/tremolo.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/vocoder330/__init__.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/vocoder330/bank.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/vocoder330/filters.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/vocoder330/keys.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/vocoder330/mic.py (99%) rename lib/{vrack => audioinstruments/_rack}/modules/vocoder330/output.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/vocoder330/voice.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/voice_mix.py (99%) rename lib/{vrack => audioinstruments/_rack}/modules/west_coast0.py (100%) rename lib/{vrack => audioinstruments/_rack}/modules/west_coast_synth.py (100%) rename lib/{vrack => audioinstruments/_rack}/sequencer.py (99%) rename lib/{vrack => audioinstruments/_rack}/soundfont.py (99%) rename lib/{vrack => audioinstruments/_rack}/soundfont_drums.py (97%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7341a62..71f8b1e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,9 +1,10 @@ # Contributing to audioinstruments -Every instrument here is a patch on `vrack`, a virtual Eurorack modular -synthesizer in Python, built on [audiodsp](https://github.com/PyDevices/audiodsp). Twenty modules inspired by -classic instruments live in `lib/vrack/modules/`, their DSP in `lib/vrack/dsp/`, the -rack and its kernels in `lib/vrack/core/`. These are the rules of the repo. +Every instrument here is a patch on the rack in `lib/audioinstruments/_rack/`, +a virtual Eurorack modular synthesizer in Python, built on +[audiodsp](https://github.com/PyDevices/audiodsp). Twenty modules inspired by +classic instruments live in `_rack/modules/`, their DSP in `_rack/dsp/`, the +rack and its kernels in `_rack/core/`. These are the rules of the repo. ## Run the tests @@ -72,9 +73,10 @@ control (sync, linear FM, PWM, cutoff under a fast envelope), a model audiodsp lacks, or a feedback loop inside the module. [docs/audiodsp.md](docs/audiodsp.md) lists what is on which side and why. Do not write a pure-Python copy of a node audiodsp has. Nothing under -`lib/vrack` imports `audioinstruments` or `audioeffects`: vrack depends on -audiodsp alone. The `audioinstruments` package in this repo is the public -face over vrack, so it imports vrack and never the other way round. +`lib/audioinstruments/_rack` imports the faces or `audioeffects`: the rack +depends on audiodsp alone. The faces, `lib/audioinstruments/.py`, are +the public side over the rack, so they import the rack and never the other way +round. The face is generated. Each `lib/audioinstruments/.py` is written by `tools/make_faces.py` from the instrument's class, so change `MACRO_LABELS`, @@ -109,7 +111,7 @@ the bend, for a glide. It is all in An instrument in `adapter.ALL` follows the Poly106 ([docs/instruments.md](docs/instruments.md)): a family of modules, a voice -patch with its presets in `lib/vrack/instruments/`, a built-in effect as a +patch with its presets in `lib/audioinstruments/_rack/instruments/`, a built-in effect as a core in `dsp/`, a spec in `tools/traits.py` and a trait file, and tests on both interpreters. Its sound is built from the original's controls; there is nothing to match. @@ -141,7 +143,7 @@ format: bump `PATCH_FORMAT_VERSION` once a release has carried the old name. Ideas and planned work: `ROADMAP.md`, three lines at most each. Bugs and things a user should be able to do but cannot: issues. Docs are for people -using vrack: second person, short paragraphs, say where a number came from. +using the rack: second person, short paragraphs, say where a number came from. ## Releasing diff --git a/ROADMAP.md b/ROADMAP.md index 339486b..c7bfd83 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -4,10 +4,6 @@ audioinstruments is a set of playable instruments, each a patch of modules on a virtual rack, rendered a block at a time on audiodsp. Planned work, in rough order: -- Every instrument by name: `audioinstruments.create("poly106", 48000)`, with - each instrument's macros and patches readable from its module. -- The rack moves to `audioinstruments._rack` before 0.5.0. Instruments are the - public surface; the rack's modules and jacks stay free to change. - The MicroPython tests in CI, on a unix build with audiodsp and ulab; today they are run by hand before a merge. - The docs in the shared five sections: Get started, Guides, Reference, How it @@ -45,7 +41,7 @@ Planned work, in rough order: - An arpeggiator. A note source in front of a `VoicePool` that holds the keys you play and steps through them as a pattern (up, down, random, over several octaves), so any polyphonic instrument can arpeggiate; a voice patch has no hook on incoming notes today. -- A tempo that follows the host's transport in `vrack.adapter`. +- A tempo that follows the host's transport in `audioinstruments._rack.adapter`. - The trait tool for fixed-sound instruments: render a SoundFont preset through the sampler with the same phrase and measure it the same way, so an electric piano or a drum kit can be set beside a reference. diff --git a/VERSION b/VERSION index 8adb4af..4557e6f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.5.0.dev1 +0.5.0.dev2 diff --git a/docs/allocation.md b/docs/allocation.md index 95f35a4..d75fbed 100644 --- a/docs/allocation.md +++ b/docs/allocation.md @@ -72,7 +72,7 @@ frames, and `tests/mpy/test_bridge.py` pulls an audiodsp node under These cores loop per sample in Python, because no vector form exists for a recursion with a nonlinearity inside it and audiodsp has no node that does -the job at vrack's block size (see [audiodsp.md](audiodsp.md)): +the job at the rack's block size (see [audiodsp.md](audiodsp.md)): - `dsp/oscillator.py`: the wave blender, and the PolyBLEP VCO when audiodsp has no `audiooscillator` (with it, the VCO is a node); diff --git a/docs/architecture.md b/docs/architecture.md index 8583c8a..2f7ff9d 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,8 +1,8 @@ -# How vrack is put together +# How the rack is put together ## Layout - lib/vrack/ + lib/audioinstruments/_rack/ core/ the rack, jacks and cables, the signal kernels, the audiodsp bridge, and poly.py: the voice allocator and the voice pool adapter.py create() and ALL: a polyphonic rack, or a SoundFont, as an audio component @@ -66,7 +66,7 @@ may write into; the rack resizes jacks and scratch buffers together when its block size changes. `set_macro`/`get_macro` address parameters by index through `MACRO_LABELS`. `get_state`/`set_state` carry the parameters, and `Rack.save_patch`/`load_patch` the whole patch as a dict. -`Rack.save_patch_file(path)` writes that dict to a JSON file, and `Rack.load_patch_file(path)` or `vrack.load_rack(path)` reads it back and installs the modules it names. +`Rack.save_patch_file(path)` writes that dict to a JSON file, and `Rack.load_patch_file(path)` or `audioinstruments._rack.load_rack(path)` reads it back and installs the modules it names. A patch stores its format version (`PATCH_FORMAT_VERSION`, now 2). Version 2 renamed every module, so a version 1 patch is refused with a message that says so; rebuild it with the current names. @@ -81,8 +81,8 @@ large panels use named parameters throughout. On a build with `audiograph` (CPython from audiodsp 0.6.9.dev4, and the MicroPython and CircuitPython builds), a rack can be compiled into one C graph and rendered by one call a block, which is what holds real time on an -ESP32-P4. `vrack.core.graph.compile_rack(rack)` does it, and -`vrack.adapter.create()` does it for an instrument whenever every module in +ESP32-P4. `audioinstruments._rack.core.graph.compile_rack(rack)` does it, and +`audioinstruments._rack.adapter.create()` does it for an instrument whenever every module in the patch can take part. A module takes part by declaring its **fragment**: the nodes it is made of, how they hang on its jacks, and how its parameters reach them. diff --git a/docs/audiodsp.md b/docs/audiodsp.md index a3d4a4f..6e94c51 100644 --- a/docs/audiodsp.md +++ b/docs/audiodsp.md @@ -1,8 +1,8 @@ -# vrack and audiodsp +# The rack and audiodsp -vrack depends on [audiodsp](https://github.com/PyDevices/audiodsp), the +the rack depends on [audiodsp](https://github.com/PyDevices/audiodsp), the PyDevices port of CircuitPython's audio nodes plus its own, and on nothing -else beyond numpy or ulab. This page says what vrack runs on audiodsp, what +else beyond numpy or ulab. This page says what the rack runs on audiodsp, what it keeps in Python and why, and what it would still like audiodsp to have. Everything here was checked against audiodsp 0.6.7 on CPython 3.12 and on the unix build of MicroPython 1.29 with the same modules compiled in. @@ -10,10 +10,10 @@ the unix build of MicroPython 1.29 with the same modules compiled in. ## How the two fit together audiodsp nodes pull signed 16-bit PCM from a source and hand out a block of -their own. vrack's jacks carry blocks of floats, because control voltage, +their own. The rack's jacks carry blocks of floats, because control voltage, gates and audio share one scale and get added and multiplied freely. The `NodeBridge` in `core/bridge.py` joins the two: a looping -`audiocore.RawSample` over an int16 array the size of one vrack block, a +`audiocore.RawSample` over an int16 array the size of one block, a passthrough `audiofilters.Filter` so no node sees the RawSample's end-of-sample flag, then the nodes. Per block it converts the float input in, pulls one block from the last node and converts back. With every node's @@ -24,7 +24,7 @@ pull goes through `audiopump.pull` and `drain` and allocates nothing. Two things decide what can live on audiodsp: - **Block size.** The CircuitPython-ported effects take a `buffer_size`, so - they run at vrack's 64 frames, and so do `audioladder.Ladder` and + they run at the rack's 64 frames, and so do `audioladder.Ladder` and `audiooscillator.Oscillator` from audiodsp 0.6.8. synthio and the rest of audiodsp's own nodes (`audioshaper`, `audioverb`, `audiomodal`, `audiomath`, `audioecho`) always hand out 256 frames. `ChunkedBridge` runs those by @@ -37,7 +37,7 @@ Two things decide what can live on audiodsp: ## What runs on audiodsp -| vrack core | audiodsp | Used by | +| Rack core | audiodsp | Used by | | --- | --- | --- | | `OscillatorCore`, the PolyBLEP VCO (saw, pulse, triangle, sine) | `audiooscillator.Oscillator` through `OscillatorBridge`: pitch, linear FM, PWM and sync go in as audio and are read per sample; one stereo node gives two waveforms from one phase | Patch2600, Modular100 VCO, Modular55 VCO, StateVariableMono, Duo20, WestCoastSynth, PinMatrix3, MonoOne, HybridMono, Semi32, DrumSynth, PolyrhythmSynth, Acid303 | | `LadderCore` (transistor, clean, Curtis, diode) | `audioladder.Ladder` at the rack's block size; the cutoff CV goes into the node's cutoff input and is read per sample (a node without that input, audiodsp 0.6.8 and earlier, moves the cutoff once a block); the clean preset's high-pass is a biquad in front; `render(poles=2)` takes the output two stages in, a 12 dB slope that still self-oscillates | Semi32, DrumSynth, PolyrhythmSynth, Modular55 low-pass, Patch2600, Modular100, PinMatrix3, MonoOne | @@ -53,7 +53,7 @@ Two things decide what can live on audiodsp: ## What stays in Python, and why -| vrack core | Why not a node | +| Rack core | Why not a node | | --- | --- | | The wave blender (`dsp/oscillator.py`) | Its morph between shapes has no node. | | The acid filter (`dsp/ladder.py`) | Its feedback is high-passed inside the loop, which `audioladder.Ladder` has no place for. | @@ -66,7 +66,7 @@ Two things decide what can live on audiodsp: | Granular engine | `audiodelays.GranularPitchShift` shifts live input; it has no buffer position, size or freeze. | | MacroOscillator's other six models, the WestCoast0 triangle core | Per-sample phase arithmetic (FM, folding, formants, a kick's pitch drop). | -## What vrack still lacks in audiodsp +## What the rack still lacks in audiodsp The ladder at the caller's buffer size and the band-limited oscillator were the first two asks; both are in audiodsp 0.6.8. With an older audiodsp, or a @@ -101,4 +101,4 @@ imported: its West Coast instrument drives a vactrol-style gate from a synthio envelope and a one-shot LFO; its chorus and vibrato model a clocked bucket-brigade line, which is where `BBDDelayCore`'s use of `Echo`'s `freq_shift` comes from; `acoustickit` is a modal synthesizer like ModalResonator's -model 0. vrack depends on audiodsp alone. +model 0. The rack depends on audiodsp alone. diff --git a/docs/instruments.md b/docs/instruments.md index 7cfe951..683fbca 100644 --- a/docs/instruments.md +++ b/docs/instruments.md @@ -1,7 +1,7 @@ # Instruments An instrument is a voice patch with its presets, ready to play from -`vrack.adapter.create(48000, voice=name)`. Its name is in `adapter.ALL`, it +`audioinstruments._rack.adapter.create(48000, voice=name)`. Its name is in `adapter.ALL`, it answers the audio component contract ([polyphony.md](polyphony.md)), and its sound is pinned by a trait file. The Poly106 is the first one, and the pattern the others follow. @@ -12,7 +12,7 @@ The Poly106 is a six-voice poly synth inspired by the Roland Juno-106: one DCO, one filter, one envelope and the chorus. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="poly106") # six voices; voices=8 for more audio_out.play(inst.output) @@ -38,7 +38,7 @@ Sub Bass, Pluck Bass, Lead, Square Lead, Organ, Sweep Pad and Poly Keys. frequency, resonance and envelope amount, the four envelope sliders, the LFO rate, PWM and the chorus. `inst.patch.set_control(inst.voice_pool, name, value)` sets any other control by the name it has in -`vrack.instruments.poly106.SETTINGS`, with sliders from 0 to 10 as on the +`audioinstruments._rack.instruments.poly106.SETTINGS`, with sliders from 0 to 10 as on the original. The chorus delays everything by 256 frames, 5.3 ms at 48 kHz, so @@ -115,7 +115,7 @@ The Poly5 is a five-voice poly synth inspired by the Sequential Prophet-5 it rings, two envelopes, and Poly-Mod, which lets each voice modulate itself. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="poly5") # five voices; voices=N for more audio_out.play(inst.output) @@ -174,7 +174,7 @@ and Noise Wind. The ten macros are `vcf_cutoff`, `vcf_resonance`, `vcf_env_amount`, `vcf_decay`, `amp_attack`, `amp_release`, `pm_filter_env`, `pm_osc_b`, `osc_b_fine` and `glide`. `inst.patch.set_control(inst.voice_pool, name, value)` sets any other control by its name in -`vrack.instruments.poly5.SETTINGS`. There is no built-in effect, so +`audioinstruments._rack.instruments.poly5.SETTINGS`. There is no built-in effect, so `inst.latency_samples` is 0. `examples/poly5_demo.py` plays all eleven musical patches, writes @@ -260,7 +260,7 @@ thins its bass as it rings, one envelope for the filter and the amp, chord memory, and an ensemble that makes one oscillator sound wide. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="poly6") # six voices; voices=N for more audio_out.play(inst.output) @@ -351,7 +351,7 @@ Organ, Phase Sweep, Vibrato Lead and Noise Surf. The ten macros are `vcf_cutoff`, `vcf_resonance`, `vcf_eg_int`, `eg_attack`, `eg_decay`, `eg_release`, `pw_pwm`, `mg_freq`, `fx_speed` and `fx_intensity`. `inst.patch.set_control(inst.voice_pool, name, value)` sets any other control -by its name in `vrack.instruments.poly6.SETTINGS`. +by its name in `audioinstruments._rack.instruments.poly6.SETTINGS`. `examples/poly6_demo.py` plays eleven of the patches, stores a chord from held keys, switches the effect on a held chord, writes `wav_exports/poly6_demo.wav` @@ -415,7 +415,7 @@ a low-pass you can set to 12 or 24 dB per octave, two envelopes that can follow the key, and assign modes that stack the voices into a unison or a solo line. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="poly8") # eight voices; voices=N for more audio_out.play(inst.output) @@ -494,7 +494,7 @@ Noise Wind. The ten macros are `vcf_cutoff`, `vcf_resonance`, `vcf_env`, `hpf_cutoff`, `env1_decay`, `env2_attack`, `env2_release`, `cross_mod`, `lfo_rate` and `portamento`, each covering its control's whole range. `inst.patch.set_control(inst.voice_pool, name, value)` sets any other control -by its name in `vrack.instruments.poly8.SETTINGS`. There is no built-in effect, +by its name in `audioinstruments._rack.instruments.poly8.SETTINGS`. There is no built-in effect, so `inst.latency_samples` is 0. One thing differs from the original: its 12 dB slope does not self-oscillate, @@ -574,7 +574,7 @@ envelopes; and one LFO with a wheel and a bend lever to play it with. It sounds bright, fat and brassy, and its chord stabs cut. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="polyxa") # eight voices; voices=N for more audio_out.play(inst.output) @@ -631,7 +631,7 @@ volume envelope drives the VCA and nothing else. `volume` is the VCA's level, and `VCA_GAIN` (0.7) leaves the headroom for eight voices. One **LFO** for the whole instrument runs from 0.1 Hz to 20 Hz (`RATE_RANGE` in -`lib/vrack/modules/polyxa/lfo.py`), free and with no delay. `lfo_wave` is +`lib/audioinstruments/_rack/modules/polyxa/lfo.py`), free and with no delay. `lfo_wave` is `"sine"`, `"square"` or `"random"`, a new level from white noise on each cycle of the square. There is one `lfo_depth` for every destination, and four switches say where it goes: `lfo_vco1` and `lfo_vco2` (pitch, up to one octave @@ -653,7 +653,7 @@ whatever `lfo_vcf` says. The bend never glides. `assign` is `"poly"` (each new note takes the next of the eight voices round and round, so a release rings until its turn comes) or `"unison"` (one note at a time, on every voice). `unison_detune` spreads the stacked voices, `UNISON_DETUNE` -(0.03) semitones a knob unit in `lib/vrack/instruments/polyxa.py`: 0.09 in a fresh +(0.03) semitones a knob unit in `lib/audioinstruments/_rack/instruments/polyxa.py`: 0.09 in a fresh patch, 0.3 at 10, about 30 cents at the outer voices; it does nothing in `"poly"`. `portamento` is the time of one glide, from 5 ms to 5 s (`PORTA_RANGE`), in both modes. Every voice glides from its own last note, and @@ -675,7 +675,7 @@ AC-coupled. It adds no latency, and there is no built-in effect. Every source and destination the PolyXa wires, the control that sets it, its depth with the knob at 10, and the constant that sets that depth (all in -`lib/vrack/modules/polyxa/voice.py` unless named). CV is 0.1 for an octave, so +`lib/audioinstruments/_rack/modules/polyxa/voice.py` unless named). CV is 0.1 for an octave, so 0.7 is seven octaves. | Source | Destination | Control | Depth at 10 | Constant | @@ -690,8 +690,8 @@ depth with the knob at 10, and the constant that sets that depth (all in | Wheel | VCO-1 pitch | `mod_vco` | 1 octave either way at full wheel | `LFO_PITCH` | | Wheel | VCO-2 pitch | `mod_vco` | 1 octave either way at full wheel | `LFO_PITCH` | | Wheel | Cutoff | `mod_vcf` | 2 octaves either way at full wheel | `LFO_FILTER` | -| Bend lever | VCO-1 pitch | `bend_dest` `"both"`, `bend_range` | `bend_range` semitones (2 fresh) | `bend_range` in `lib/vrack/modules/poly_midi_cv.py` | -| Bend lever | VCO-2 pitch | `bend_dest`, `bend_range` | `bend_range` semitones | `bend_range` in `lib/vrack/modules/poly_midi_cv.py` | +| Bend lever | VCO-1 pitch | `bend_dest` `"both"`, `bend_range` | `bend_range` semitones (2 fresh) | `bend_range` in `lib/audioinstruments/_rack/modules/poly_midi_cv.py` | +| Bend lever | VCO-2 pitch | `bend_dest`, `bend_range` | `bend_range` semitones | `bend_range` in `lib/audioinstruments/_rack/modules/poly_midi_cv.py` | | Bend lever | Cutoff | `bend_vcf` | 2 octaves either way at full bend (0.4 CV x 0.5) | `BEND_FILTER` | | VCO-2 | Cutoff | `vco2_vcf` | 2 octaves either way (0.4 CV x 0.5) | `VCO2_VCF_DEPTH` | | Keyboard | Cutoff | `vcf_kybd` | 1 octave per octave, from C4 | `VCF_KYBD` | @@ -714,7 +714,7 @@ Lead and Noise Wind. The ten macros are `vcf_cutoff`, `vcf_resonance`, `vcf_env` `fenv_decay`, `aenv_attack`, `aenv_release`, `vco2_detune`, `vco2_vcf`, `lfo_rate` and `portamento`, each covering its control's whole range. `inst.patch.set_control(inst.voice_pool, name, value)` sets any other control by -its name in `vrack.instruments.polyxa.SETTINGS`. +its name in `audioinstruments._rack.instruments.polyxa.SETTINGS`. The program memory with its split and double modes, the chord memory, the per-voice trims, auto-tune, the cassette interface, the sequencer, master tune @@ -790,7 +790,7 @@ and hold, unison, portamento and an analogue-style distortion. It adds no latency. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="polya6") # sixteen voices; voices=N for fewer or more audio_out.play(inst.output) @@ -858,7 +858,7 @@ The original is very deep: a 79-source modulation matrix, seven-stage envelopes, 16-stage tracking generator and 28 digital effects. The PolyA6 keeps its voice and folds the rest. -| Original | In vrack | Why | +| Original | In the rack | Why | | --- | --- | --- | | 16 voices, poly or mono, unison 2-16, detune, assign lowest or rotary | Kept: `kb_mode`, `unison`, `unison_detune`, `assign` | The voice pool has all four [M p.55-57] | | UNISON STACK, MONOVX, mix mode | Left out | Stack changes with the notes held; the rest is per-part routing | @@ -916,7 +916,7 @@ These are not on the panel. They are the numbers to change if the sound is wrong ### The routing table Every route, the control that sets it, its depth at 100 and the constants in -`lib/vrack/modules/polya6/voice.py` (or `output.py`, for the distortion) that set it. +`lib/audioinstruments/_rack/modules/polya6/voice.py` (or `output.py`, for the distortion) that set it. Pitch is a CV of 0.1 per octave. An LFO, the S&H or a VCO's sum is taken as +-1 and an envelope as 0 to 1 before the amount applies. Each rule has a test of its own in `tests/test_synth_polya6.py`. @@ -979,7 +979,7 @@ that brightens under your fingers, strings, bells and a soaring lead you bend from the ribbon. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="poly80") # eight voices; voices=N for fewer or more audio_out.play(inst.output) @@ -990,7 +990,7 @@ inst.poly_pressure(58, 100) # press into the top note: only ``` Sliders run from 0 to 10, centred levers from -5 to +5. Every control is in -`vrack.instruments.poly80.SETTINGS` under the name used here, and +`audioinstruments._rack.instruments.poly80.SETTINGS` under the name used here, and `inst.patch.set_control(inst.voice_pool, name, value)` sets any of them. A control with a `ch1_` or `ch2_` in front belongs to channel I or II. Where a number below comes from the original's owner's manual (the Yamaha *CS-80 @@ -1034,7 +1034,7 @@ a low-pass, each with its own cutoff and its own resonance, so together they make a band-pass [M p.16, 42]. A biquad is 12 dB per octave and does not self-oscillate, which is how the original's filters are described in Sound On Sound's review of a later instrument built on its voice; the manual gives no -slope. It is also the cheapest filter in vrack that gives the high-pass a +slope. It is also the cheapest filter in the rack that gives the high-pass a resonance of its own, and it settles to exact silence, where a `synthio` biquad leaves an offset behind a VCA. Nothing here says the pair models the original's filter. @@ -1140,7 +1140,7 @@ and a second finger to slide from are not built. becomes the larger of its own and the sustain time, from 0.1 s to 10 s (`SUSTAIN_RANGE` (0.1, 10.0)) [M p.11, 39]; it never shortens a long one. Sustain is always on, as the original's is with no foot switch plugged in; CC 64 holds keys as on -every vrack instrument. `sustain_mode` `"II"` ends the sustain of the earlier +every instrument. `sustain_mode` `"II"` ends the sustain of the earlier notes each time a new key goes down: they return to their own releases. `portamento` is the time of one glide, 5 ms to 2 s (`GLIDE_RANGE` (0.005, 2.0)), constant @@ -1186,7 +1186,7 @@ steal `"none"`. Where each source goes, the control that sets it, its depth with the control at 10 (or at +-5 for a lever), and the constant that sets it, all in -`lib/vrack/modules/poly80/voice.py` unless the row names another file. CV is 0.1 +`lib/audioinstruments/_rack/modules/poly80/voice.py` unless the row names another file. CV is 0.1 for an octave, so a semitone is 1/120. | Rule | Source | Destination | Control | Depth at 10 | Constant | Test | @@ -1264,7 +1264,7 @@ names, 2026-10-21): The factor is seconds of audio per second of work, one run on an otherwise idle machine; it moves by about 15% from run to run. Two channels of eight voices is -the most any vrack instrument asks. At the default block CPython does not keep +the most any instrument asks. At the default block CPython does not keep up with eight notes held and MicroPython does; at 64 frames neither does. The time on CPython goes to audiodsp's stand-ins for the biquad pair, about two fifths of a buffer. A channel that `mix` leaves silent is not rendered, so @@ -1281,7 +1281,7 @@ LFO and a portamento. It sounds thin, bright and biting: a lead that cuts, sync sweeps, S&H bleeps. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="duosynth") audio_out.play(inst.output) @@ -1364,7 +1364,7 @@ semitone at 10 [M p.18]. ### Where each source goes Every rule the voice follows, the control that sets it, its depth at 10 and -the constant in `lib/vrack/modules/duosynth/voice.py` that sets it. CV is 0.1 +the constant in `lib/audioinstruments/_rack/modules/duosynth/voice.py` that sets it. CV is 0.1 for an octave, so 0.7 is seven octaves, and every sum goes through unclamped except where the row says so. @@ -1404,7 +1404,7 @@ except where the row says so. | `vca` | Gain, VCA | `vca_gain` | gain 2 | `VCA_GAIN_SCALE` (2.0) | | `vca` | Envelope, VCA | `vca_level` | gain 2.4 | `VCA_LEVEL_SCALE` (2.4) | | `vca` | Velocity, VCA | `vel_vca` | silent at velocity 0 | `VEL_VCA` (1.0) | -| `bend_and_wheel` | Bend, VCO-1 and VCO-2 | `bend_range` | 8 semitones fresh, never glides | `bend_range` in `lib/vrack/instruments/duosynth.py` | +| `bend_and_wheel` | Bend, VCO-1 and VCO-2 | `bend_range` | 8 semitones fresh, never glides | `bend_range` in `lib/audioinstruments/_rack/instruments/duosynth.py` | | `bend_and_wheel` | Wheel, VCO-1 and VCO-2 | mod wheel | a semitone either way | `WHEEL_VIBRATO_SEMITONES` (1.0) | | `hpf_position` | Filter, high-pass, VCA | `hpf_cutoff` | 16 Hz to 16 kHz | `HPF_RANGE` (16.0, 16000.0) | | `transpose` | Lever, VCO-1, VCO-2 and keyboard follow | `transpose` | 2 octaves either way | none | @@ -1442,12 +1442,12 @@ the routing of mixer, filter, high-pass and VCA, and the envelope times (pages - The levels: the noise slider at 3 times a VCO slider's gain (`NOISE_LEVEL` (3.0)), so a noise patch sits with the others, and the VCA's `VCA_GAIN_SCALE` (2.0) and `VCA_LEVEL_SCALE` (2.4), so a chord sits within a dB or so of the other - vrack instruments. + the instruments. - The resonance reaching 1.05 of the ladder's at 10 (`RES_TOP` (1.05)), so that it sustains a tone, with a short kick into a quiet filter set above 9 to start it singing, as an analogue filter's own noise would (`SELF_OSC_FROM` (0.92), `SEED_LEVEL` (0.05)). -- A 10 Hz DC block on the output (`DC_HZ` in `lib/vrack/modules/duosynth/output.py`), +- A 10 Hz DC block on the output (`DC_HZ` in `lib/audioinstruments/_rack/modules/duosynth/output.py`), because sync and the ring modulator leave an offset that moves with the pitches and the widths. @@ -1462,7 +1462,7 @@ audio-rate FM and sync. And one filter keeps the controls to the original's: the filter type switch exists only on the reissue. No source describes the Rev 3 filter's circuit, so it is not modelled. It runs -on vrack's least saturating ladder, `LadderCore(sample_rate, "clean")` with +on the rack's least saturating ladder, `LadderCore(sample_rate, "clean")` with four poles, which thins out in the bass as the resonance rises, as the Rev 3 is said to. Treat it as a stand-in. @@ -1479,7 +1479,7 @@ and FM Clang. The ten macros are `vcf_cutoff`, `vcf_resonance`, `vcf_env`, `adsr_attack`, `adsr_decay`, `adsr_sustain`, `adsr_release`, `vco2_fine`, `lfo_rate` and `portamento`, each covering its control's whole range. `inst.patch.set_control(inst.voice_pool, name, value)` sets any other control by -its name in `vrack.instruments.duosynth.SETTINGS`. +its name in `audioinstruments._rack.instruments.duosynth.SETTINGS`. `examples/duosynth_demo.py` plays all eleven musical patches in order, writes `wav_exports/duosynth_demo.wav`, and checks that every section sounds, nothing @@ -1563,7 +1563,7 @@ divided-down tones into a wide, slow shimmer. It is made for chords, played with two hands. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="string_ensemble") # 49 voices, one a key audio_out.play(inst.output) @@ -1668,7 +1668,7 @@ the ensemble, so it stays tight and does not chorus. `tune`, the pitch bend and ### The ensemble -`ensemble` sends the upper tones through `EnsembleCore` in `lib/vrack/dsp/ensemble.py`: +`ensemble` sends the upper tones through `EnsembleCore` in `lib/audioinstruments/_rack/dsp/ensemble.py`: three delay lines swept by the sum of two low-frequency oscillators, the three 120 degrees apart. The slow sweep runs at `SLOW_HZ` (0.6) with a depth of `SLOW_DEPTH_MS` (1.6) milliseconds, the fast one at `FAST_HZ` (6.0) with @@ -1702,7 +1702,7 @@ applied in each key, so every key in one chord keeps its own velocity. `tune` is bend reaches every key and the bass. The mod wheel does nothing. Sliders run 0 to 10, buttons are on or off, and `inst.patch.set_control(inst.voice_pool, name, value)` sets any control by its name in -`vrack.instruments.string_ensemble.SETTINGS`. +`audioinstruments._rack.instruments.string_ensemble.SETTINGS`. **Sources.** The panel follows the SE-II's: Gordon Reid, "Eminent 310 String Synthesizer", *Sound On Sound*, May 2007 (six tones, "the Contra-Bass and Cello @@ -1737,9 +1737,9 @@ expression pedal input, external audio and the reissue's phaser are not built. Every route the instrument wires, the control that sets it, its depth or time, and the constant in the code that sets that (in the voice, envelope, bass and -output modules under `lib/vrack/modules/string_ensemble/`, the patch in -`lib/vrack/instruments/string_ensemble.py`, and the ensemble in -`lib/vrack/dsp/ensemble.py`). CV is 0.1 for an octave. +output modules under `lib/audioinstruments/_rack/modules/string_ensemble/`, the patch in +`lib/audioinstruments/_rack/instruments/string_ensemble.py`, and the ensemble in +`lib/audioinstruments/_rack/dsp/ensemble.py`). CV is 0.1 for an octave. | Route | Control | Depth or time | Constant | Test | | --- | --- | --- | --- | --- | @@ -1805,8 +1805,8 @@ splits at middle C, and a four-line ensemble makes it wide. It is made for chords, played with two hands. ```python -import vrack.adapter as adapter -from vrack.instruments.vocoder330 import attach_mic +import audioinstruments._rack.adapter as adapter +from audioinstruments._rack.instruments.vocoder330 import attach_mic inst = adapter.create(48000, voice="vocoder330") # 12 voices; voices=N up to 49 audio_out.play(inst.output) @@ -1895,7 +1895,7 @@ are not built. ### The voice the vocoder listens to -vrack's instruments have no audio input, so the mic is built in, and +the rack's instruments have no audio input, so the mic is built in, and `mic_source` picks where it comes from: - `"test"` (fresh): a built-in talker, so the vocoder sounds in tests and @@ -2036,8 +2036,8 @@ the mono output, the output level switch and the phones; there is no graph form. Every route the instrument wires, the control that sets it, its range or depth and the constant that sets that (in the modules under -`lib/vrack/modules/vocoder330/`, the patch in `lib/vrack/instruments/vocoder330.py` -and the lines in `lib/vrack/dsp/ensemble.py`). Pitch is CV at 0.1 per octave. +`lib/audioinstruments/_rack/modules/vocoder330/`, the patch in `lib/audioinstruments/_rack/instruments/vocoder330.py` +and the lines in `lib/audioinstruments/_rack/dsp/ensemble.py`). Pitch is CV at 0.1 per octave. | Route | Control | Range or depth | Constant | Test | | --- | --- | --- | --- | --- | @@ -2072,7 +2072,7 @@ key down; the other vocoder patches are silent without one. The ten macros are `strings_attack`, `strings_tone`, `voice_attack`, `release`, `vocoder_tone`, `mic_level`, `vib_depth`, `vib_rate`, `vib_delay` and `pitch_time`, each covering its control's whole range. Set any control by its name in -`vrack.instruments.vocoder330.SETTINGS` with +`audioinstruments._rack.instruments.vocoder330.SETTINGS` with `inst.patch.set_control(inst.voice_pool, name, value)`. `examples/vocoder330_demo.py` plays all twelve in order, mostly with two hands @@ -2106,7 +2106,7 @@ pickups, damper felts, active concentric preamp tone controls and the classic stereo auto-pan "vibrato". ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="electric_piano") # ten voices audio_out.play(inst.output) @@ -2119,7 +2119,7 @@ for note in (48, 55, 60, 64): ### The mechanism Each voice is one key of the piano, built from three shared cores in -`lib/vrack/dsp/keys.py` that the other electromechanical keyboards reuse: +`lib/audioinstruments/_rack/dsp/keys.py` that the other electromechanical keyboards reuse: - **The hammer** (`HammerExciter`) pushes the tine with a half-sine pulse. A soft blow is a long, round push of about 2.7 ms that mostly moves the @@ -2203,7 +2203,7 @@ slightly hollow plink. A preamp with a tone control and a stereo auto-pan follow the voices. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="reed_piano") # ten voices audio_out.play(inst.output) @@ -2240,7 +2240,7 @@ values. The level does not follow the swing: bass and treble play at the same level. - **The pad landing** damps a released key, 20 dB down in about 50 ms, with a short low thud whose level is `pad_thud`. The original has no sustain pedal; - the pedal (CC 64) works here as on every vrack instrument, an addition. + the pedal (CC 64) works here as on every instrument, an addition. - **The preamp and auto-pan** (`ReedPianoOutput`) block the pickup's slow offset below 15 Hz, tilt the tone with a low shelf at 200 Hz and a high shelf at 3 kHz moved in opposite directions (12 dB either way), and pan the sound @@ -2297,7 +2297,7 @@ is sweet, almost a sine; played hard it snaps into a nasal, buzzy bark. A small amplifier and a fixed-rate tremolo follow the voices. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="reed_piano200a") # ten voices audio_out.play(inst.output) @@ -2393,7 +2393,7 @@ pickups, four tone tabs and a foam mute shape a sound that is bright, percussive and nasal, and it gets much brighter the harder you play. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="clavichord") # ten voices audio_out.play(inst.output) @@ -2428,7 +2428,7 @@ filter after the string. about 30 ms. The release pluck is the string's first three partials, as the chosen pickup hears them, about 15 dB under the note's attack and gone in about 50 ms. The original has no sustain pedal; the pedal (CC 64) works here -as on every vrack instrument. +as on every instrument. **The mute.** `mute` (0 to 10) presses foam against the strings: it shortens every note, down to about 0.15 s at 10, rather than only making it quieter. @@ -2513,7 +2513,7 @@ shimmer of two strings slightly out of tune with each other, and a short, twangy bass. A three-band EQ and a tremolo follow the voices. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="electric_grand70") # ten voices audio_out.play(inst.output) @@ -2614,20 +2614,20 @@ Chorused and held chords on Tremolo) and writes ## How an instrument is built -- **Modules** in `lib/vrack/modules//`, a family that shares the +- **Modules** in `lib/audioinstruments/_rack/modules//`, a family that shares the instrument's name: the voice (`Poly106Voice`), and whatever exists once for the whole instrument (`Poly106LFO`, `Poly106Output`). They are ordinary modules with jacks and parameters, registered in `modules/__init__.py`, and they can be patched by hand like any other. -- **A built-in effect is a core** in `lib/vrack/dsp/` with a module over it: +- **A built-in effect is a core** in `lib/audioinstruments/_rack/dsp/` with a module over it: `BBDChorusCore` in `dsp/chorus.py`, which another instrument can reuse. -- **The voice patch** in `lib/vrack/instruments/.py`: a `VoicePatch` +- **The voice patch** in `lib/audioinstruments/_rack/instruments/.py`: a `VoicePatch` whose `build_global()` adds the modules every voice reads, `build()` one voice, `build_shared()` the section after the voices. `SETTINGS` names every control with its fresh value, `PRESETS` the patches as the controls that differ, `apply_patch()` sets a whole patch, and `VOICES` and `LATENCY_SAMPLES` say how many voices to build and what the patch delays. -- **Registered** in `vrack.adapter._PATCH_VOICES`, which puts the name in +- **Registered** in `audioinstruments._rack.adapter._PATCH_VOICES`, which puts the name in `adapter.ALL`; `tests/test_adapter.py` then checks the contract for it. - **Tests**: `tests/test_synth_.py` that each control does what its name says, `tests/mpy/test_.py` that it plays on MicroPython, and a @@ -2680,7 +2680,7 @@ a four-pole transistor ladder filter, one ADSR envelope, one free-running LFO and a constant-time portamento ([polyphony.md](polyphony.md#one-voice-mono-mode)). ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="mono101") audio_out.play(inst.output) @@ -2783,7 +2783,7 @@ holding an upper note while tapping a lower key trills between them without re-firing the contours. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="mono_synth", priority="low") audio_out.play(inst.output) @@ -2913,7 +2913,7 @@ loudness and filter. It plays a huge, round low end: the Roar patch opens with a growl and closes to a soft purr. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="bass_pedals") audio_out.play(inst.output) @@ -3040,7 +3040,7 @@ at the top to sit with the others. #### Where each source goes Every rule the voice follows, the control that sets it, its depth at 10 and the -constant in `lib/vrack/modules/bass_pedals/voice.py` that sets it. A cutoff or +constant in `lib/audioinstruments/_rack/modules/bass_pedals/voice.py` that sets it. A cutoff or pitch CV is 0.1 for an octave (`OCTAVE_CV` (0.1)), and every sum goes through unclamped except where the row says so. @@ -3070,7 +3070,7 @@ unclamped except where the row says so. | `low_note_priority` | Pedals, pitch | none | the lowest sounds, one trigger | none | | `pitch_hold` | Last pedal, pitch | none | held through the release and the rest | none | | `glide` | Note, oscillators | `glide`, `glide_amount` | 5 ms to 0.5 s time constant | `GLIDE_TAU` (0.005, 0.5) | -| `bend` | Bend, both oscillators | `bend_range` | 2 semitones fresh, never glides | `bend_range` in `lib/vrack/instruments/bass_pedals.py` | +| `bend` | Bend, both oscillators | `bend_range` | 2 semitones fresh, never glides | `bend_range` in `lib/audioinstruments/_rack/instruments/bass_pedals.py` | | `foot_loudness` | LOUDNESS foot slider, VCA | CC 11 | 18 dB range | `FOOT_LOUD_R301` (15.0), `FOOT_LOUD_R302` (100.0) | | `foot_filter` | FILTER foot slider, cutoff | CC 4 | 3 octaves | `FOOT_FILTER_OCT` (3.0) | @@ -3135,7 +3135,7 @@ The ten macros are `filter_cutoff`, `filter_emphasis`, `filter_amount`, `filter_decay`, `loud_decay`, `loud_attack`, `mix`, `osc_b_freq`, `beat` and `glide_amount`, each covering its control's whole range (`beat` from -5 to +5). `inst.patch.set_control(inst.voice_pool, name, value)` sets any control by its -name in `vrack.instruments.bass_pedals.SETTINGS`. +name in `audioinstruments._rack.instruments.bass_pedals.SETTINGS`. `examples/bass_pedals_demo.py` plays all eleven musical patches as pedal lines, writes `wav_exports/bass_pedals_demo.wav`, and checks that every section sounds, @@ -3169,7 +3169,7 @@ nine drawbars, single-trigger percussion, contact key click, a scanner vibrato and chorus, and an overdriven rotary speaker. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="tonewheel3") # 16 voices audio_out.play(inst.output) @@ -3181,7 +3181,7 @@ The keyboard has 16 voices (`VOICES = 16`). An organ key is a switch, so velocity changes nothing: every note plays at the same level. A note sounds from key down to key up, with a 3 ms ramp each way for the contacts and no envelope beyond that. The sustain pedal (CC 64) holds notes, and the pitch -bend works as on every vrack instrument, though the original had none. +bend works as on every instrument, though the original had none. ### The drawbars and foldback @@ -3276,7 +3276,7 @@ footage buses, a bass section on the lowest octave, and one pitch vibrato for the whole keyboard. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="compact_organ") # 49 voices audio_out.play(inst.output) @@ -3289,7 +3289,7 @@ the whole keyboard never steals a note. The keys are switches: velocity is ignored and every note sounds at the same level. A 3 ms ramp at the start and end of a note keeps it from clicking, and a voice with nothing to play parks. The sustain pedal (CC 64) holds notes, and pitch bend works as on every other -vrack instrument. +instrument. ### Footages and voice tabs @@ -3390,7 +3390,7 @@ vibrato for the whole keyboard. Next to the CompactOrgan it is breathier and brighter, and the mixture gives it a shimmer on top. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="combo_organ") # 49 voices audio_out.play(inst.output) @@ -3402,7 +3402,7 @@ There is a voice for every key from C2 to C6 at 8' (MIDI 48 to 96), so holding the whole keyboard never steals a note. Keys are switches: velocity does nothing and every note sounds at the same level, with a 3 ms ramp on and off so they don't click. Silent voices park. The sustain pedal (CC 64) holds notes, and -pitch bend works as on the other vrack instruments. +pitch bend works as on the other instruments. ### Footage drawbars and the IV mixture @@ -3493,7 +3493,7 @@ generated by analog-style circuitry: bridged-T resonators, filtered noise bursts and a six-oscillator metallic bank for cymbals and hats. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="rhythm808") # 16 voices, 1 per sound audio_out.play(inst.output) @@ -3631,7 +3631,7 @@ a thin dual-tone body under high-passed white noise for the snare, and a six-oscillator metallic bank voiced thin and bright for the hats and cymbal. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="rhythm606") # 7 voices, 1 per sound audio_out.play(inst.output) @@ -3738,7 +3738,7 @@ through the 909's resonant filters and a 6-bit quantization and sample-and-hold at 32 kHz, capturing the iconic aliased sheen of the hardware. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="rhythm909") # 11 voices, 1 per sound audio_out.play(inst.output) @@ -3838,7 +3838,7 @@ pure white-noise hiss for the hi-hats and cymbal, and a rich palette of analog L percussion including maracas, claves, cowbell, tambourine, and guiro. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="rhythm78") # 14 voices, 1 per sound audio_out.play(inst.output) @@ -3953,7 +3953,7 @@ A drummer plays it dynamically from velocity-sensitive pads, with separate analo voice channels for the bass drum, snare drum, and three toms. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="electronic_drums5") # 5 voices, 1 per channel audio_out.play(inst.output) @@ -4083,7 +4083,7 @@ Name a bank by the start of its md5, then the preset: The bank is found through its record in `~/soundfonts/reference/`, and the file must still match the record, so a renamed or replaced file cannot pass -for the one that was studied. The phrase is played through `vrack.soundfont` +for the one that was studied. The phrase is played through `audioinstruments._rack.soundfont` and written as the same traits under `ref.`; with `--vs NAME[:PATCH]` the tool prints how the instrument's accepted traits differ from the reference's. Banks live in `~/soundfonts/` and never in this repository, and diff --git a/docs/overview.md b/docs/overview.md index 81bf53d..e2a937e 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -3,7 +3,7 @@ The whole tour: what is in the rack, how a patch is rendered, and where each part is documented. The [README](../README.md) has the short version. -Every instrument in audioinstruments is a patch on `vrack`, a virtual Eurorack +Every instrument in audioinstruments is a patch on `audioinstruments._rack`, a virtual Eurorack modular synthesizer in Python. Twenty modules inspired by classic instruments, with their panels, patch cables between them, and a rack that renders a block at a time on @@ -15,7 +15,7 @@ an ESP32-P4; the Poly106, the Poly5 and the Poly6 do today ([docs/architecture.md](architecture.md#a-graph-fragment)). ```python -from vrack import MidiCVModule, Modular100VCO, Modular100VCF, Modular100VCA, Modular100EnvLFO, Rack +from audioinstruments._rack import MidiCVModule, Modular100VCO, Modular100VCF, Modular100VCA, Modular100EnvLFO, Rack rack = Rack(sample_rate=48000, block_size=64) midi = rack.add_module(MidiCVModule("midi")) @@ -43,12 +43,12 @@ for _ in range(100): `VoicePool` copies a voice patch N times, gives each note a free copy (it steals one without a click when none is free), and sums the copies into a -section that exists once. `vrack.adapter` wraps the pool as an audio +section that exists once. `audioinstruments._rack.adapter` wraps the pool as an audio component with `note_on()`, `set_macro()`, a settable `voices` and an `output` that `audio_out.play()` takes: ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="mono_one", voices=8) audio_out.play(inst.output) @@ -87,13 +87,13 @@ from a bank in `~/soundfonts/`. ## Loading only what you use -`import vrack` loads the rack and its core and nothing else. A synth is -imported the first time you name it, whether as `from vrack import PinMatrix3`, -`from vrack.modules.pin_matrix3 import PinMatrix3` or `vrack.modules.PinMatrix3`, and +`import audioinstruments._rack` loads the rack and its core and nothing else. A synth is +imported the first time you name it, whether as `from audioinstruments._rack import PinMatrix3`, +`from audioinstruments._rack.modules.pin_matrix3 import PinMatrix3` or `audioinstruments._rack.modules.PinMatrix3`, and `load_rack()` imports only the modules a patch contains. On a board that keeps -the others out of RAM. `dir(vrack)` lists every name on CPython; on -MicroPython it lists the ones loaded so far, and `vrack.__all__` always has -them all. `vrack.modules.MODULE_TYPES` is the one thing that loads every synth. +the others out of RAM. `dir(audioinstruments._rack)` lists every name on CPython; on +MicroPython it lists the ones loaded so far, and `audioinstruments._rack.__all__` always has +them all. `audioinstruments._rack.modules.MODULE_TYPES` is the one thing that loads every synth. ## What is in the rack @@ -151,7 +151,7 @@ and fall back to Python cores on an older one. The wave blender, the acid filter and a few other cores stay in Python because they need control inside the block or a model audiodsp does not have; the CV generators are vector code. [docs/audiodsp.md](audiodsp.md) says what -is where and what vrack would still like from audiodsp. Nothing allocates in +is where and what the rack would still like from audiodsp. Nothing allocates in a block path, in the sense [docs/allocation.md](allocation.md) spells out for each interpreter, and the tests enforce it. [docs/performance.md](performance.md) has measured block times, and diff --git a/docs/performance.md b/docs/performance.md index 0c7c439..1566db0 100644 --- a/docs/performance.md +++ b/docs/performance.md @@ -76,7 +76,7 @@ predate. Measured on 2026-10-19 on a Waveshare ESP32-P4-WIFI6-DEV-KIT: the P4 at 400 MHz, a 32 MB MicroPython heap in PSRAM, MicroPython 1.30.0-preview (single-precision floats) built with ulab and audiodsp (0.6.7 plus five -commits) compiled in. vrack's `lib/` and `examples/` ran as source files +commits) compiled in. The rack's `lib/` and `examples/` ran as source files from the board's flash, after a soft reset that skipped `main.py`, so nothing else was running. The oscillator and ladder cases below are the Python cores, as they were named then `OscillatorCore` and `LadderCore`; @@ -117,7 +117,7 @@ with the collector off. Cases (a), (b) and (c) repeat: further runs, each with a fresh core or rack, agreed with these to within 1%. Two results did not. The first run of (a), -taken straight after importing vrack, gave 14 936, 4 932 and 47 195 µs. +taken straight after importing the rack, gave 14 936, 4 932 and 47 195 µs. And whole movements vary from one build of the rack to the next: two builds of movement 4 at 64 frames in the same session had medians of 27 ms and 48 ms, each steady across 1 000 blocks and allocating a different amount @@ -193,5 +193,5 @@ blocks of both speeds. ## A polyphonic instrument [polyphony.md](polyphony.md#what-it-costs) has what eight MonoOne voices cost -through `vrack.adapter`, at 64, 128 and 256 frames, on both interpreters. +through `audioinstruments._rack.adapter`, at 64, 128 and 256 frames, on both interpreters. `examples/poly_bench.py` measures it on yours. diff --git a/docs/polyphony.md b/docs/polyphony.md index 67766c8..3b06fa3 100644 --- a/docs/polyphony.md +++ b/docs/polyphony.md @@ -3,7 +3,7 @@ A module in the rack plays one note. To play chords you build the voice once and let `VoicePool` copy it: it makes N copies, hands each note to a free copy, adds the copies together and sends the sum through a section that -exists once, for effects and the master output. `vrack.adapter` wraps that +exists once, for effects and the master output. `audioinstruments._rack.adapter` wraps that pool as an audio component, so anything that drives an instrument through `note_on()`, `set_macro()` and `output` can drive a rack. @@ -14,7 +14,7 @@ them from the voice's own `pitch`, `gate` and `velocity` jacks, and returns the jack that carries the voice's audio. ```python -from vrack import Rack, VoicePool +from audioinstruments._rack import Rack, VoicePool rack = Rack(sample_rate=48000, block_size=128) @@ -71,7 +71,7 @@ their sum as two jacks, adds whatever modules it likes to the rack, and returns the two jacks to patch to the master: ```python -from vrack import Modular55LowPass +from audioinstruments._rack import Modular55LowPass def build_shared(rack, left, right): lowpass = rack.add_module(Modular55LowPass("shared_lowpass")) @@ -276,7 +276,7 @@ other voice. Portamento is a voice-side control: the pool does not slide anything, because what the oscillator should do during a glide is the voice's business. -`vrack.dsp.glide.GlideCore` is the slide, for a voice module to hold: +`audioinstruments._rack.dsp.glide.GlideCore` is the slide, for a voice module to hold: ```python glide = GlideCore(sample_rate, block_size) @@ -366,7 +366,7 @@ skipped by `Rack.step()`, which is how parking works. ## An audio component -`vrack.adapter.create(sample_rate, channel_count=2, transport=None, **options)` +`audioinstruments._rack.adapter.create(sample_rate, channel_count=2, transport=None, **options)` returns an `Instrument` with the surface audiocomponents' instruments have: `output`, `sample_rate`, `channel_count`, `latency_samples` (0), `tail_samples` (None), `capabilities`, `patch_index`, `note_on()`, @@ -378,7 +378,7 @@ returns an `Instrument` with the surface audiocomponents' instruments have: note off; macros take 0 to 127. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter inst = adapter.create(48000, voice="mono_one", voices=8) audio_out.play(inst.output) diff --git a/docs/soundfont.md b/docs/soundfont.md index 41c2c1f..a59b230 100644 --- a/docs/soundfont.md +++ b/docs/soundfont.md @@ -1,12 +1,12 @@ # SoundFont instruments `soundfont` plays a SoundFont 2 bank's melodic presets, and `soundfont_drums` -plays its kits. Both go through `vrack.adapter.create()` like the synths, and +plays its kits. Both go through `audioinstruments._rack.adapter.create()` like the synths, and both answer the audio component contract, so a host that plays one plays the other. ```python -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter piano = adapter.create(48000, voice="soundfont", bank="/path/to/bank.sf2") kit = adapter.create(48000, voice="soundfont_drums", bank="/path/to/bank.sf2") @@ -15,7 +15,7 @@ piano.program_change(48) # Strings, in a General MIDI bank piano.note_on(60, 100) ``` -vrack does not ship a bank, and nothing here downloads one. Without `bank=` +the rack does not ship a bank, and nothing here downloads one. Without `bank=` the instrument looks at `$SOUNDFONT`, then at the first `.sf2` in `~/soundfonts/` (`%USERPROFILE%\soundfonts` on Windows), and raises an `OSError` that names all three when it finds none. Two instruments made from @@ -35,7 +35,7 @@ For room to grow, make the pool yourself and share it, which is also how a General MIDI module's sixteen parts share one sampler: ```python -from vrack.soundfont import Voices +from audioinstruments._rack.soundfont import Voices pool = Voices(48000, 2, voice_count=16, max_voice_count=128) piano = adapter.create(48000, voice="soundfont", bank=path, voice_pool=pool) @@ -73,14 +73,14 @@ cannot be combined, and `kit.strict_pads` reports which you have. ## Playing from MIDI -`vrack.midi_dispatch.MidiDispatcher` turns MIDI bytes, `adafruit_midi` +`audioinstruments._rack.midi_dispatch.MidiDispatcher` turns MIDI bytes, `adafruit_midi` messages or a Standard MIDI File into calls on instruments, by channel. General MIDI's percussion channel is channel 10 (9 when counted from 0), and `drums=` takes that channel; nothing treats a controller's default channel specially. ```python -from vrack.midi_dispatch import MidiDispatcher, read_midi_file +from audioinstruments._rack.midi_dispatch import MidiDispatcher, read_midi_file midi = MidiDispatcher(piano, drums=kit) # channel 10 to the kit, the rest to the piano midi.feed(buf, n) # raw bytes, as a port's readinto() leaves them @@ -92,17 +92,17 @@ for seconds, status, data1, data2 in read_midi_file("song.mid"): `examples/soundfont_demo.py` plays two bars of drums on channel 10 and then brings a piano in, and writes `wav_exports/soundfont_demo.wav`. -`vrack.midi_cc` holds a scaler for each of the 128 Control Change codes, from +`audioinstruments._rack.midi_cc` holds a scaler for each of the 128 Control Change codes, from the host's 0.0 to 1.0 to the control's own terms: floats for magnitudes, exactly 0.0 at the middle for bipolar controls, booleans for switches and integers for bank and program numbers. ## Step sequencing -`vrack.sequencer.Sequencer` keeps a pattern's time in frames on an +`audioinstruments._rack.sequencer.Sequencer` keeps a pattern's time in frames on an `audiopump.Events` queue, so the groove does not move when your timer is late. It plays instruments that can put a note on a frame (`inst.schedulable`), and -none of vrack's can yet: the SoundFont instruments and the synths take +none of the rack's can yet: the SoundFont instruments and the synths take their notes at a block boundary, through `sample_position`. The sequencer's tests run against a stand-in that answers the same calls. diff --git a/examples/bass_pedals_demo.py b/examples/bass_pedals_demo.py index 7fc7202..c2ea60f 100644 --- a/examples/bass_pedals_demo.py +++ b/examples/bass_pedals_demo.py @@ -31,7 +31,7 @@ sys.path.insert(0, "examples") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import section_ok, shares RATE = 48000 diff --git a/examples/clavichord_demo.py b/examples/clavichord_demo.py index 17402bc..19bd383 100644 --- a/examples/clavichord_demo.py +++ b/examples/clavichord_demo.py @@ -17,14 +17,14 @@ sys.path.insert(0, "lib") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import Audible, section_ok RATE = 48000 BUFFER = 256 CLIP = 27000 -# Presets from vrack.instruments.clavichord +# Presets from audioinstruments._rack.instruments.clavichord FUNK = 0 BRILLIANT_UPPER = 1 WARM_LOWER = 2 diff --git a/examples/combo_organ_demo.py b/examples/combo_organ_demo.py index 68756c9..c5b9e36 100644 --- a/examples/combo_organ_demo.py +++ b/examples/combo_organ_demo.py @@ -22,7 +22,7 @@ sys.path.insert(0, "examples") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import section_ok, shares RATE = 48000 diff --git a/examples/compact_organ_demo.py b/examples/compact_organ_demo.py index 89557cd..c3911f0 100644 --- a/examples/compact_organ_demo.py +++ b/examples/compact_organ_demo.py @@ -22,7 +22,7 @@ sys.path.insert(0, "examples") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import section_ok, shares RATE = 48000 diff --git a/examples/duosynth_demo.py b/examples/duosynth_demo.py index 3ed4748..2ed6c80 100644 --- a/examples/duosynth_demo.py +++ b/examples/duosynth_demo.py @@ -23,7 +23,7 @@ sys.path.insert(0, "examples") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import section_ok, shares RATE = 48000 diff --git a/examples/electric_grand70_demo.py b/examples/electric_grand70_demo.py index 1482e83..0209bd7 100644 --- a/examples/electric_grand70_demo.py +++ b/examples/electric_grand70_demo.py @@ -18,14 +18,14 @@ sys.path.insert(0, "lib") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import Audible, section_ok RATE = 48000 BUFFER = 256 CLIP = 27000 -# Presets from vrack.instruments.electric_grand70 +# Presets from audioinstruments._rack.instruments.electric_grand70 STAGE_GRAND = 0 BALLAD = 1 BRIGHT_ROCK = 2 diff --git a/examples/electric_piano_demo.py b/examples/electric_piano_demo.py index 91695ec..342de65 100644 --- a/examples/electric_piano_demo.py +++ b/examples/electric_piano_demo.py @@ -18,14 +18,14 @@ sys.path.insert(0, "lib") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import Audible, section_ok RATE = 48000 BUFFER = 256 CLIP = 27000 -# Presets from vrack.instruments.electric_piano +# Presets from audioinstruments._rack.instruments.electric_piano SUITCASE = 0 BARK = 2 GLASS_TINES = 3 diff --git a/examples/electronic_drums5_demo.py b/examples/electronic_drums5_demo.py index b183b31..4c812c2 100644 --- a/examples/electronic_drums5_demo.py +++ b/examples/electronic_drums5_demo.py @@ -24,7 +24,7 @@ sys.path.insert(0, "lib") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import Section, feed_sections, open_wav, section_ok RATE = 48000 diff --git a/examples/headless_synth.py b/examples/headless_synth.py index ba4922e..1a06a6d 100644 --- a/examples/headless_synth.py +++ b/examples/headless_synth.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2026 Brad Barnett # SPDX-License-Identifier: MIT -"""Example: Headless Modular100 modular synthesizer in vrack. +"""Example: Headless Modular100 modular synthesizer in the rack. Demonstrates: - Creating a virtual Eurorack frame (Rack). @@ -13,8 +13,8 @@ import math -from vrack import Rack -from vrack.modules import ( +from audioinstruments._rack import Rack +from audioinstruments._rack.modules import ( MidiCVModule, Modular100EnvLFO, Modular100Utility, @@ -25,7 +25,7 @@ def main(): - print("=== vrack: Virtual Eurorack Synthesizer ===") + print("=== audioinstruments: Virtual Eurorack Synthesizer ===") rack = Rack(sample_rate=48000, block_size=64) # 1. Install Modules into the Rack @@ -91,7 +91,7 @@ def main(): # 5. Serialize patch saved_state = rack.save_patch("Modular100 Lead") print("\nPatch state serialized successfully (%d bytes in dict)" % len(str(saved_state))) - print("=== vrack demo completed successfully ===") + print("=== audioinstruments demo completed successfully ===") if __name__ == "__main__": diff --git a/examples/hmi_board.py b/examples/hmi_board.py index e4117ce..c7171e0 100644 --- a/examples/hmi_board.py +++ b/examples/hmi_board.py @@ -22,8 +22,7 @@ sys.path.append("/lib") import hmilink # noqa: E402 -from hmilink import local, tcp # noqa: E402 -from vrack import ( # noqa: E402 +from audioinstruments._rack import ( # noqa: E402 MidiCVModule, Modular55Envelope, Modular55LowPass, @@ -31,8 +30,9 @@ Modular55VCO, Rack, ) -from vrack.core.signal import INT16_DTYPE, np # noqa: E402 -from vrack.hmi import RackModel # noqa: E402 +from audioinstruments._rack.core.signal import INT16_DTYPE, np # noqa: E402 +from audioinstruments._rack.hmi import RackModel # noqa: E402 +from hmilink import local, tcp # noqa: E402 SAMPLE_RATE = 48000 BLOCK_SIZE = 64 diff --git a/examples/hmi_instrument.py b/examples/hmi_instrument.py index 35bee76..cf5f381 100644 --- a/examples/hmi_instrument.py +++ b/examples/hmi_instrument.py @@ -10,7 +10,7 @@ The demo module (``poly6_demo`` by default) supplies the instrument's name and its sections: real patches in audible ranges, the same phrase ``examples/.py`` renders and checks. They loop here. The instrument -is an ``hmilink`` model (``vrack.hmi.InstrumentModel``): a panel changes +is an ``hmilink`` model (``audioinstruments._rack.hmi.InstrumentModel``): a panel changes the patch, moves the macros, turns the knobs inside the voices, plugs and unplugs cables, and every other panel follows. A phone opens http://:9000/. @@ -37,10 +37,10 @@ sys.path.append(p) import audiocore # noqa: E402 +import audioinstruments._rack.adapter as adapter # noqa: E402 import hmilink # noqa: E402 -import vrack.adapter as adapter # noqa: E402 +from audioinstruments._rack.hmi import InstrumentModel # noqa: E402 from hmilink import tcp # noqa: E402 -from vrack.hmi import InstrumentModel # noqa: E402 try: from time import sleep_ms, ticks_diff, ticks_us diff --git a/examples/mono101_demo.py b/examples/mono101_demo.py index 094d3ad..c11b3a1 100644 --- a/examples/mono101_demo.py +++ b/examples/mono101_demo.py @@ -17,7 +17,7 @@ sys.path.insert(0, "lib") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import Section, feed_sections, open_wav, section_ok RATE = 48000 diff --git a/examples/mono_synth_demo.py b/examples/mono_synth_demo.py index e36d27c..bcf5c5e 100644 --- a/examples/mono_synth_demo.py +++ b/examples/mono_synth_demo.py @@ -21,7 +21,7 @@ sys.path.insert(0, "lib") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import Audible, section_ok RATE = 48000 diff --git a/examples/patch2600_demo.py b/examples/patch2600_demo.py index eaaffc2..b754fab 100644 --- a/examples/patch2600_demo.py +++ b/examples/patch2600_demo.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2026 Brad Barnett # SPDX-License-Identifier: MIT -"""Example: Patch2600, the semi-modular voice inspired by the ARP 2600, in vrack. +"""Example: Patch2600, the semi-modular voice inspired by the ARP 2600, in the rack. Demonstrates: 1. Playing Patch2600 out of the box with pre-normalled default routing (no cables needed). @@ -11,11 +11,11 @@ import math -from vrack import MidiCVModule, Patch2600, Rack +from audioinstruments._rack import MidiCVModule, Patch2600, Rack def main(): - print("=== vrack: Patch2600 semi-modular synthesizer ===") + print("=== audioinstruments: Patch2600 semi-modular synthesizer ===") rack = Rack(sample_rate=48000, block_size=64) # Install MIDI-to-CV and Patch2600 diff --git a/examples/play_audio.py b/examples/play_audio.py index fa40c5f..c674045 100644 --- a/examples/play_audio.py +++ b/examples/play_audio.py @@ -36,7 +36,7 @@ def sleep_ms(ms): time.sleep(ms / 1000.0) -from vrack import ( # noqa: E402 +from audioinstruments._rack import ( # noqa: E402 Acid303, DrumSynth, Duo20, @@ -66,7 +66,7 @@ def sleep_ms(ms): WestCoast0, WestCoastSynth, ) -from vrack.core.signal import INT16_DTYPE, np # noqa: E402 +from audioinstruments._rack.core.signal import INT16_DTYPE, np # noqa: E402 SAMPLE_RATE = 48000 BLOCK_SIZE = 64 diff --git a/examples/poly106_demo.py b/examples/poly106_demo.py index ea5a8f4..bd4e3e7 100644 --- a/examples/poly106_demo.py +++ b/examples/poly106_demo.py @@ -19,7 +19,7 @@ sys.path.insert(0, "lib") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import Audible, section_ok RATE = 48000 @@ -27,7 +27,7 @@ CLIP = 27000 BEAT = 0.5 # 120 bpm -# Patches, by number: see vrack.instruments.poly106.PRESETS. +# Patches, by number: see audioinstruments._rack.instruments.poly106.PRESETS. WARM_PAD, BRASS, BRASS_STAB, PLUCK_BASS, LEAD = 1, 3, 4, 6, 7 diff --git a/examples/poly5_demo.py b/examples/poly5_demo.py index 7f8a4bc..c9178da 100644 --- a/examples/poly5_demo.py +++ b/examples/poly5_demo.py @@ -19,7 +19,7 @@ sys.path.insert(0, "lib") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import Section, feed_sections, open_wav, section_ok RATE = 48000 diff --git a/examples/poly6_demo.py b/examples/poly6_demo.py index ccd1718..aef73ed 100644 --- a/examples/poly6_demo.py +++ b/examples/poly6_demo.py @@ -18,7 +18,7 @@ sys.path.insert(0, "lib") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import Section, feed_sections, open_wav, section_ok RATE = 48000 diff --git a/examples/poly80_bench_mix.py b/examples/poly80_bench_mix.py index ca70cc0..76f61d3 100644 --- a/examples/poly80_bench_mix.py +++ b/examples/poly80_bench_mix.py @@ -18,7 +18,7 @@ import time -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter try: _now = time.perf_counter diff --git a/examples/poly80_demo.py b/examples/poly80_demo.py index 14a8569..6235198 100644 --- a/examples/poly80_demo.py +++ b/examples/poly80_demo.py @@ -23,7 +23,7 @@ sys.path.insert(0, "examples") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import section_ok, shares RATE = 48000 diff --git a/examples/poly8_demo.py b/examples/poly8_demo.py index 968cc40..d91fd40 100644 --- a/examples/poly8_demo.py +++ b/examples/poly8_demo.py @@ -17,7 +17,7 @@ sys.path.insert(0, "lib") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import Section, feed_sections, open_wav, section_ok RATE = 48000 diff --git a/examples/poly_bench.py b/examples/poly_bench.py index cab5930..1344782 100644 --- a/examples/poly_bench.py +++ b/examples/poly_bench.py @@ -25,7 +25,7 @@ import time -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter try: _now = time.perf_counter diff --git a/examples/poly_render.py b/examples/poly_render.py index ee08d9f..7d889a7 100644 --- a/examples/poly_render.py +++ b/examples/poly_render.py @@ -37,7 +37,7 @@ sys.path.insert(0, "lib") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter SAMPLE_RATE = 48000 BUFFER = 256 diff --git a/examples/polya6_demo.py b/examples/polya6_demo.py index 9b5b5a4..dd36274 100644 --- a/examples/polya6_demo.py +++ b/examples/polya6_demo.py @@ -22,7 +22,7 @@ sys.path.insert(0, "examples") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import section_ok, shares RATE = 48000 diff --git a/examples/polyxa_demo.py b/examples/polyxa_demo.py index dc06d7e..19cd926 100644 --- a/examples/polyxa_demo.py +++ b/examples/polyxa_demo.py @@ -23,7 +23,7 @@ sys.path.insert(0, "examples") import audiocore -import vrack.adapter as adapter +import audioinstruments._rack.adapter as adapter from demo_check import section_ok, shares RATE = 48000 diff --git a/examples/rackview/__init__.py b/examples/rackview/__init__.py index 8cea2af..83e04bd 100644 --- a/examples/rackview/__init__.py +++ b/examples/rackview/__init__.py @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: 2026 Brad Barnett # SPDX-License-Identifier: MIT -"""Front panels for a vrack rack, on hmilink. +"""Front panels for a rack, on hmilink. - ``index.html``: the panel a phone's browser gets from the device. - ``lvgl_panel.py``: the same panel in LVGL, for the device's own screen diff --git a/examples/rackview/index.html b/examples/rackview/index.html index 558d6f4..bd12858 100644 --- a/examples/rackview/index.html +++ b/examples/rackview/index.html @@ -3,7 +3,7 @@ -vrack panel +Rack panel