Skip to content

Releases: shakfu/numchuck

numchuck

26 Feb 00:45

Choose a tag to compare

Python bindings for the ChucK audio programming language using nanobind.

The numchuck library provides interactive control over ChucK, enabling live coding workflows, bidirectional Python/ChucK communication, and comprehensive VM introspection—all while maintaining the existing real-time and offline audio capabilities.

Changes since the last Release

Added

  • OTF shred tracking (session.py, common.py, repl.py, editor.py): Shreds added or removed via on-the-fly programming (chuck --add, chuck --remove) are now automatically tracked in the REPL and editor. A new sync_shreds() method on ChuckSession diffs session.shreds against chuck.get_all_shred_ids() and reconciles: externally-added shreds appear in the topbar, ?/shreds table, and status bar count; finished or externally-removed shreds are pruned. Sync runs on each render tick (via the bottom toolbar callback in the REPL, status bar callback in the editor), guarded by _otf_enable so there is zero cost when OTF is off. OTF-discovered shreds are tagged with shred_type="otf".

  • OTF CLI flags (cli/main.py, tui/tui.py, tui/repl.py, tui/editor.py, tui/common.py): Added --otf and --otf-port flags to the repl and edit subcommands. --otf enables the ChucK OTF listener (default port 8888), allowing external chuck commands to add/remove shreds. The OTF port is shown in the REPL status bar when enabled.

  • REPL real-time stereo level meters (repl.py, waveform.py): The wave command now displays live L/R peak meters below the shreds panel. Uses a background daemon thread polling get_audio_meters() at 100ms intervals with Application(refresh_interval=0.1) for redraws. Meters appear/disappear via ConditionalContainer tied to session.show_waveform. Styled green-on-dark-green (class:waveform-area).

  • REPL word aliases for symbol commands (parser.py, lang/constants.py): Every terse symbol command now has a readable word equivalent. New aliases: shreds (?), shred <id> (? <id>), globals (?g), audio (?a), start (>), stop (||), shutdown (X), compile <file> (: <file>), exec "code" (! "code"), shell <cmd> ($ <cmd>), snippet <name> (@<name>), get <var> (<var>?), set <var> <val> (<var>::<val>), signal <ev> (<ev>!), broadcast <ev> (<ev>!!). All word aliases are tab-completable.

  • REPL command cheatsheet (docs/repl-commands.md): Standalone reference documenting every REPL command organized by category, showing both symbol and word forms.

  • Wheel RECORD Validation (scripts/check_wheel_record.py, scripts/repair_wheel.py):

    • New standalone script validates wheel RECORD files against actual ZIP contents -- checks for smuggled files, dangling entries, hash mismatches, and size mismatches
    • Addresses wheel archive confusion attacks
    • Integrated into repair_wheel.py as a post-repair validation step for both chugin and non-chugin wheels
    • Added RECORD validation steps to CI wheels workflow (per-platform and combined artifact stages)
    • make check now runs RECORD validation alongside twine check
  • CLAP, PdPatch, and VST3 Chugins (thirdparty/chugins/{CLAP,PdPatch,VST3}/):

    • Added three new chugins from my-chugins: CLAP (host CLAP plugins), PdPatch (embed Pure Data patches), and VST3 (host VST3 plugins)
    • All three are enabled by default via option(CM_CLAP/CM_PDPATCH/CM_VST3 ... ON) and bundled in release wheels
    • External dependencies (clap SDK 1.2.6, libpd, VST3 SDK v3.8.0) fetched automatically via CMake FetchContent
    • Can be individually disabled with -DCM_CLAP=OFF, -DCM_PDPATCH=OFF, or -DCM_VST3=OFF
    • macOS and Linux only -- all three depend on POSIX APIs (dlfcn.h, dirent.h, pthreads) not available on Windows
    • Fixed VST3 SDK option names (SMTG_ENABLE_VST3_PLUGIN_EXAMPLES/SMTG_ENABLE_VST3_HOSTING_EXAMPLES) to correctly disable SDK samples that require gtk+-3.0 on Linux
    • The numchuck package now includes:
      • AbletonLink.chug
      • ABSaturator.chug
      • AmbPan.chug
      • AudioUnit.chug
      • Binaural.chug
      • Bitcrusher.chug
      • CLAP.chug
      • ConvRev.chug
      • Elliptic.chug
      • ExpDelay.chug
      • ExpEnv.chug
      • FIR.chug
      • FoldbackSaturator.chug
      • GVerb.chug
      • KasFilter.chug
      • Ladspa.chug
      • Line.chug
      • MagicSine.chug
      • Mesh2D.chug
      • MIAP.chug
      • Multicomb.chug
      • NHHall.chug
      • Overdrive.chug
      • PanN.chug
      • Patch.chug
      • PdPatch.chug
      • Perlin.chug
      • PitchTrack.chug
      • PowerADSR.chug
      • Random.chug
      • Range.chug
      • RegEx.chug
      • Sigmund.chug
      • Spectacle.chug
      • VST3.chug
      • Wavetable.chug
      • WinFuncEnv.chug
      • WPDiodeLadder.chug
      • WPKorg35.chug
      • XML.chug

Fixed

  • Wheels workflow collect job (.github/workflows/wheels.yml):

    • Reordered collect job steps to run checkout before artifact downloads -- actions/checkout@v4 cleans the working directory by default, which was wiping the downloaded dist/ directory
  • FetchContent install leaking into wheels (thirdparty/chugins/{CLAP,PdPatch}/CMakeLists.txt):

    • Added EXCLUDE_FROM_ALL to FetchContent_Declare for CLAP and PdPatch to prevent their SDK install() rules from polluting the wheel with headers, pkgconfig, and cmake config files (reduced wheel from 186 to 100 files)
  • REPL ghost line on shred removal (common.py): Fixed terminal rendering corruption (ghost line at bottom of screen) when removing shreds while level meters were visible. Root cause: ChucK's VM writes [chuck]: (VM) removing shred: ... directly to stdout via CK_FPRINTF_STDOUT, bypassing prompt_toolkit's full-screen renderer. Fix: setup_output_capture() now also sets the global/static ChucK.set_stdout_callback() and ChucK.set_stderr_callback() to route all VM system messages through the TUI log system. See docs/architecture.md for the two-tier callback architecture.

  • REPL exception handling (repl.py): _submit_input (TUI) and process_line (stdin) now catch exceptions from executor.execute() and spork_code(), displaying graceful error messages instead of crashing with tracebacks.

  • REPL shutdown crash (repl.py): Fixed malloc double-free / segfault on exit. Removed static set_stdout_callback/set_stderr_callback (which outlived the Python objects they referenced) in favor of instance-level callbacks cleaned up by chuck.shutdown(). Reordered cleanup to break all reference cycles before destroying C++ objects.

  • nanobind ref leak on shutdown (common.py): Fixed "leaked 1 instances" / "leaked 1 types" nanobind warnings on exit. Root cause: setup_output_capture() registers a log_callback closure (capturing self) as static class-level callbacks via ChucK.set_stdout_callback() / ChucK.set_stderr_callback(). These outlived the instance and pinned the entire ChuckApplication -> ChucK object graph, preventing C++ destructor from running. Fix: cleanup() now clears static callbacks to None before breaking instance references.

  • Shell command execution (commands.py): $ <cmd> now captures stdout/stderr (routed through _log()), enforces a 30-second timeout, reports nonzero exit codes, and catches TimeoutExpired/OSError instead of running unsanitized with no output capture or error handling.

  • ConvRev segfault on ARM64 macOS CI (test_examples.py): Fixed segfault in test_chugin_convrev_example on macos-14 (ARM64). Root cause: _check_chugin_available() created up to 2 ChucK instances without calling shutdown(), leaving ConvRev's background std::thread active. When the test then created a new instance, concurrent ConvRev destructor/thread cleanup races caused a segfault (ARM64's stricter memory ordering exposed this). Fix: all ChucK instances in test_examples.py now call remove_all_shreds() + shutdown() before going out of scope.

  • Windows CI test failures (test_waveform.py): Fixed 3 TestREPLMeterInfrastructure tests failing on Windows CI with NoConsoleScreenBufferError. These tests instantiate ChuckREPL() which creates a prompt_toolkit.Application requiring a real TTY. Fix: skip the test class when stdout is not a TTY.

Changed

  • REPL help panel (repl.py): F1 help text now shows word / symbol forms side by side for all commands that have both.

  • REPL inline transcript (repl.py): Replaced split input/output layout with a single-buffer Python/Jupyter-style transcript. Input and output are interleaved in one scrollable area -- commands echo as [=>] ..., output is indented, errors show inline with [!] prefix. Removed separate log window (F3 toggle), error bar, and transcript TextArea. Input prompt sits at the bottom of the transcript buffer.

numchuck 0.1.10

24 Feb 00:46

Choose a tag to compare

Python bindings for the ChucK audio programming language using nanobind.

The numchuck library provides interactive control over ChucK, enabling live coding workflows, bidirectional Python/ChucK communication, and comprehensive VM introspection—all while maintaining the existing real-time and offline audio capabilities.

Changes from the last Release

Added

  • Bundled Chugins in Wheels (scripts/cmake/fn_add_chugin.cmake, pyproject.toml, src/numchuck/api.py):

    • Pre-built chugins (~37 .chug files) are now included in distributed wheels
    • NUMCHUCK_INSTALL_CHUGINS CMake option gates installation (ON for wheel builds, OFF for local dev)
    • cmake.args = ["-DNUMCHUCK_INSTALL_CHUGINS=ON"] in pyproject.toml enables bundling during pip install / uv build
    • Bundled chugins directory (<package>/chugins/) automatically added to ChucK search path in Chuck.__init__
    • Users no longer need to build from source to use chugins
  • numchuck info lists bundled chugins (src/numchuck/cli/main.py):

    • Shows count and names of all bundled .chug files from the installed package
    • Displays "none" for editable/dev installs where chugins are in examples/chugins/ instead
  • Cross-Platform Wheel Repair for Chugins (scripts/repair_wheel.py):

    • Wheel repair tools (delocate, auditwheel, delvewheel) skip .chug files since they only scan platform-standard extensions
    • New repair script temporarily renames .chug to the native extension (.dylib/.so/.dll), runs the repair tool, then renames back
    • Ensures chugin shared library dependencies are properly bundled if any chugin gains external (non-system) dependencies
    • Uses only stdlib (zipfile, hashlib, csv) -- no dependency on the wheel CLI package
    • RECORD file correctly regenerated after rename operations
    • Platform-specific handling:
      • macOS: .chug -> .dylib, delocate-wheel with --require-archs
      • Linux: .chug -> .so, auditwheel repair
      • Windows: .chug -> .dll, delvewheel with --analyze-existing --no-mangle
  • README: Bundled chugins documentation (README.md):

    • Categorized table of all 37 bundled chugins (38 on macOS with AudioUnit)
    • Updated "Using Chugins" section to show automatic discovery (no manual path setup)
    • Plugin Support overview links to the bundled chugins table

Changed

  • CI: Chugin tests enabled in wheel builds (.github/workflows/wheels.yml):

    • Removed and not chugin from CIBW_TEST_COMMAND filter
    • All three platforms (Linux, macOS, Windows) now use the custom repair script
    • Windows builds install delvewheel via CIBW_BEFORE_BUILD_WINDOWS
  • Chugin test helpers updated (tests/test_examples.py):

    • New _get_chugins_dir() helper checks bundled package directory first, falls back to examples/chugins/ for dev builds
    • All chugin tests (test_chugin_loading, test_chugin_bitcrusher_strict, test_chugin_gverb_strict, test_chugin_convrev_example) use the unified helper

Fixed

  • TUI tests skipped on Windows (tests/conftest.py):

    • Added pytest_collection_modifyitems hook to skip @pytest.mark.tui tests on Windows
    • prompt_toolkit raises NoConsoleScreenBufferError on Windows in non-console environments
    • 42 TUI tests now skip gracefully on Windows instead of failing
  • WAV file rendering tests (tests/test_wavfile.py):

    • Fixed test_render_sine_to_wav run duration: chuck.run(44100) changed to chuck.run(44100 * 4) to match 4-second ChucK code duration
    • Added del chuck before temp directory cleanup in all tests to release file handles
    • Fixes PermissionError on Windows where files can't be deleted while open
  • Async awaitable race condition (src/numchuck/api.py):

    • get_int_awaitable, get_float_awaitable, get_string_awaitable had a race between run_in_executor completion and call_soon_threadsafe callback delivery
    • Added await asyncio.sleep(0) after run_in_executor to yield to the event loop and process pending callbacks before checking future.done()
    • Fixes intermittent "callback not invoked" failures on slower platforms (macOS x86_64 under Rosetta)

numchuck 0.1.9

27 Jan 20:50

Choose a tag to compare

This release introduces a browser-based Web IDE for ChucK, bringing live coding to the browser with full feature parity to the terminal REPL.

Highlights

Web IDE (numchuck web)

  • Browser-based ChucK IDE with code editor (codemirror) and interactive REPL
    (xterm.js)
  • Real-time audio metering with RMS/peak level visualization
  • Multi-file tabs, globals panel with interactive sliders, shred management
  • Works completely offline - all assets bundled locally
  • Full command parity with TUI REPL

New APIs

  • chuck.stream() - Iterator-based audio processing for real-time applications
  • render() / to_wav() - Offline rendering to numpy arrays or WAV files
  • get_audio_meters() - Real-time RMS/peak audio levels

Developer Experience

  • Automatic chugin discovery from ~/.numchuck/chugins
  • UGen parameter autocomplete (40+ UGens)
  • File watch mode with auto-reload
  • Configurable themes and key bindings
  • User directory template with snippets and examples

ChucK Command Alignment

  • Added abort, = (replace), ^(status) shortcuts
  • Full compatibility with ChucK's command syntax

Breaking Changes

  • Dropped Python 3.8 and 3.9 support (now requires Python 3.10+)
  • Removed chump package manager integration

See the CHANGELOG.md for the complete list of changes.