Changelog¶
All notable changes to this project are documented here.
This project adheres to Keep a Changelog 1.1.0 and to Semantic Versioning.
Unreleased¶
0.1.0 - 2026-07-27¶
Added¶
Parallel rendering with threads.
render,render_file,load_preset,load_jsonandto_jsonnow release the GIL, so Python threads that each own a separateSynthrender on separate cores. See Parallel rendering and the newexamples/multithreading_presetsexample.Synth.set_sample_rateto configure the render sample rate (#6).Documentation site built with Sphinx and published to GitHub Pages, covering a quickstart, parallel rendering, and a generated API reference.
This changelog.
Wheels for CPython 3.14.
Type stubs. Wheels now ship
vita.pyiandpy.typed, generated from the compiled module by nanobind’s stub generator, so editors and type checkers understand the API.tests/test_threading.py, covering concurrent rendering, concurrent preset loading, shared-Synthserialization, and recovery from failed loads.tests/test_control_options.py, which reads the metadata of every control rather than a hand-picked few.
Changed¶
Building from source is now just
pip install .on every platform.setup.pycompiles the extension itself, deriving the Python paths from the interpreter running the build, so no environment variables have to be set beforehand.ControlValue.setandValue.setregister their integer and enum overloads ahead of the float one. Runtime behaviour is unchanged – nanobind tries every overload without implicit conversion first – but the generated stubs no longer look like the float overload shadows the others.Upgraded nanobind from 2.7.0 to 2.13.0. Among other things this fixes an extra copy of the return value when a
call_guardis in use, and speeds up handing NumPy arrays back to Python.FFT::transform()now returns a thread-localFourierTransform. The IPP and kissfft backends keep mutable scratch state in the object, which concurrent wavetable work would have corrupted.RandomGenerator::next_seed_is nowstd::atomic<int>, so constructing synths on several threads at once is not a data race.Docstrings across the bindings were filled in and corrected;
renderpreviously described itself as writing a file and returning a bool.CI builds and tests against manylinux_2_28 rather than manylinux2014, and tests the built wheel rather than a stray extension module in the source tree.
Fixed¶
macOS wheels no longer carry a dead x86-64 slice. The Xcode project defaults to
ARCHS = "arm64 x86_64", so it built both andlipo’d them together, while nanobind’s static library is built for one architecture only. The x86-64 slice therefore linked without any nanobind symbols – Python extensions defer undefined symbols to load time, so nothing failed – anddelocate --require-archs arm64was satisfied by the good slice. The build now targets only the architecture being built for, which also halves the macOS build.The oscillator and sample
destinationcontrols accepted one value more than there are routings. Vital sizes them askNumSourceDestinations + kNumEffectswhere every comparable parameter usescount - 1, so the trailing value named no destination and silently routed the source nowhere;set_normalized(1.0)landed on it. Theirmaxis now 13 rather than 14, so the full normalized range maps onto real routings. Presets are unaffected – they store raw values, and nothing clamps on load.get_control_details(...).optionsandget_control_textno longer read past the end of a control’s name table. Several controls accept more values than Vital has names for –view_2dandview_spectrogramspan 0-2 with two names,filter_*_stylespans 0-9 with five – and the count was taken from the value range rather than the table. In 0.0.5 reading.optionson any of those seven controls crashed the interpreter; four more (osc_{1,2,3}_destination,sample_destination) silently reported a fifteenth option,'Shelf', that does not exist. Both now stop at the last real name, so a control’soptionsmay be shorter thanmax - min + 1when the surplus values are unnamed.A failed
load_jsonorload_presetno longer leaves the synth’s critical section held. Because rendering releases the GIL, a subsequentrenderwould have hung uninterruptibly instead of raising.The render buffer is no longer leaked if an exception unwinds out of the DSP loop.
The modulation source and destination caches are now initialized through a thread-safe function-local static instead of an unsynchronized flag.
examples/multiprocessing_presetspassed MIDI velocity as100where the API expects 0-1, and ignored its own--render-durationargument.
Removed¶
build_linux.shandbuild_macos.sh, and the separate msbuild step in CI. All three are now handled bysetup.py.DistortionType.None, renamed toDistortionType.Off. Breaking, though only nominally:Noneis a Python keyword, soDistortionType.Nonewas a syntax error and the member could only ever be reached throughgetattr(DistortionType, "None").macOS x86-64 wheels, so Intel Macs are no longer supported. The
macos-13runner image is retired and those CI jobs no longer start. There is no source distribution to fall back on, sopip install vitafails with “no matching distribution found” on an Intel Mac; stay on 0.0.5 there. Apple silicon, Linux x86-64 and Windows x86-64 are unaffected.librosafrom the test requirements; nothing used it, and it was the main source of dependency-resolution failures on new Python versions.
0.0.5 - 2025-06-03¶
Added¶
Normalized parameter access via
ControlValue.set_normalizedandget_normalized, plus parameter introspection throughSynth.get_control_detailsandSynth.get_control_text(#3).
0.0.4 - 2025-02-13¶
Added¶
Pickle support for
Synth, which makes it usable acrossmultiprocessingworkers (#1).
0.0.3 - 2025-01-08¶
Fixed¶
Wheel-building workflow corrections.
0.0.2 - 2025-01-08¶
Fixed¶
Wheel-building workflow corrections.
0.0.1 - 2025-01-08¶
Added¶
Initial release: Python bindings for the Vital synthesizer, covering preset loading and saving, control access, modulation routing, and rendering to a NumPy array or a wav file.