Schwung Framework for Ableton Move

repository·main·Indexed 18 days ago

https://github.com/charlesvestal/schwung

An unofficial framework for Ableton Move that implements a 'Shadow UI' to run custom instruments, effects, and controllers in parallel with the stock interface. Features include a Signal Chain module for routing MIDI and audio, a customizable MIDI Controller module with 16 banks, and support for native and JavaScript MIDI FX. Includes tools for installation via CLI or desktop installer, and a pytest-schwung plugin for end-to-end testing via a dedicated daemon.

Tokens
164.6K
Snippets
390
Records
656
Agent score
64%

What's inside Schwung

  1. Overview of the Midiverb Module

    main

    The midiverb module is a Schwung audio_fx component that emulates Alesis Midiverb, Midifex, and Midiverb II rack reverbs. It uses a lo-fi 23.4 kHz DSP loop with polyphase resamplers to maintain compatibility with the original 16-bit DASP DSP hardware.

    Current Implementation (v1): Uses Path A (decompiled), which provides pre-decompiled C effect functions. This version is self-contained and does not require external ROM files, though it lacks flanger LFO triggers and input/output filter modeling found in the original hardware.

    Planned Implementation (v0.2): Will introduce Path B (ROM interpreter), which allows for cycle-accurate emulation by running original ROMs through a dasp16 µ-code interpreter. This will be additive and will not break the existing parameter surface.

  2. Overview of the MrSample module

    main

    MrSample is a chromatic single-sample player module for Schwung, designed as an external module (schwung-mrsample). It is modeled after the Ableton Move Sampler and provides polyphonic playback of a single WAV file.

    Key features include:

    • Pitch Tracking: Polyphonic playback across a keyboard with pitch tracking.
    • Envelope: AHDSR amplitude envelope.
    • Filter: A multimode filter (LP/BP/HP) with envelope amount and a filter LFO.
    • Looping: Loop points with equal-power crossfading for sustained playback.
    • Polyphony: Supports up to 16 voices (configurable via polyphony).
  3. Master FX LFO Implementation Plan Overview

    main

    The Master FX LFO implementation adds two LFOs to the master FX settings. These LFOs can modulate parameters of any loaded master FX module. The system is designed to share a generic LFO architecture with existing slot LFOs to ensure consistency in waveform generation, division tables, and UI presentation.

    Architecture Summary

    • C-Side: Uses a shared lfo_common.h for waveform computation and state management. The LFO engine resides in shadow_chain_mgmt.c and operates via direct set_param calls.
    • JS-Side: Employs a generic context pattern in shadow_ui.js so that both slot LFOs and master FX LFOs use the same UI views and functions.
    • Persistence: LFO configurations are persisted via master preset JSON and boot configuration.
  4. What is the schwung-breath module?

    main

    schwung-breath is a breath-controller MIDI FX module. It allows a performer to use a microphone (e.g., via a mouthpiece) to gate note-on/off events for held pads or an external keyboard.

    In this interaction model, pitch is provided by an upstream MIDI source, while the timing (when a note sounds) and dynamics (how loud it is) are controlled by breath. This mimics an EWI or wind controller interaction using pad fingering.

  5. Overview of Gooderer controls and modes

    main

    Gooderer is a 3-band multiband processor for Schwung featuring an OTT-style upward branch. It provides three primary controls:

    • amount: Controls the LMH mix (dry vs. band-processed into master).
    • ottness: Controls the upward-comp blend (0 = downward-only, faithful to Soundgoodizer).
    • mode: Selects between modes A, B, C, or D, which determine crossovers, thresholds, ratios, timings, and saturation drives.

    Detailed design rationale and mode tables are located in the main Schwung repository under docs/plans/2026-04-28-gooderer-design.md.

  6. Overview of Schwung 2.0 Core Domains

    main

    The Schwung 2.0 architecture is organized into several functional domains that provide services to modules and the system:

    • knob_engine: Handles knob acceleration curves and delta-to-value conversions.
    • param_system: Man/stores parameters from params.json, validates ranges, and handles change notifications.
    • clock_manager: Manages BPM, external MIDI clock (24 PPQN), and transport state (play/stop/continue).
    • input_manager: Digests raw MIDI from cable 0 into clean events (pads, knobs, jog, buttons, shift state).
    • signal_chain: Manages the pipeline of midi_fxsynthaudio_fx.
    • audio_engine: Manages the buffer pool, per-slot mixing, and master mix.
    • audio_input: Provides circular capture buffers, source selection, and quantized recording.
    • slot_manager: Manages per-slot settings like volume, mute, solo, and signal chain assignment.
    • screen_manager: Owns the framebuffer and handles dirty region tracking and layer compositing.
    • led_manager: Handles LED batching (60-packet limit) and caching to prevent redundant updates.
    • menu_toolkit: Provides unified rendering for parameter editors, preset browsers, and knob overlays.
  7. Overview of the Microcosm Audio FX Plugin architecture

    main

    The Microcosm Audio FX Plugin is a granular audio effects processor designed as a single-file C DSP plugin. It utilizes the audio_fx_api_v2 for instance-based, in-place stereo int16 processing.

    Core Components:

    • Capture Buffer: A circular buffer that feeds the grain engine.
    • Grain Engine: A shared engine with per-algorithm scheduling policies.
    • Delay Engine: A separate multi-tap delay engine used specifically by Bank 4.
    • Post-Processing Chain: The signal flow follows this sequence: LFO pitch modulation $\rightarrow$ SVF lowpass filter $\rightarrow$ 4-line FDN stereo reverb $\rightarrow$ dry/wet mix.

    Technical Specifications:

    • Language: C99
    • API: audio_fx_api_v2
    • Sample Rate: 44100 Hz
    • Block Size: 128 frames/block
    • Format: Stereo int16 processing
  8. Routing MIDI to Move tracks or Schwung slots

    main

    To send MIDI from a module to Move's native track instruments or Schwung shadow-chain slot synths, you must follow the routing rule:

    Module's output channel = Move track's MIDI In channel (or Schwung slot's Receive Channel)

    • Move Tracks: Default to track N ↔ channel N (e.g., Track 1 uses Channel 1). Users can reassign these.
    • Schwung Slots: Use the recv setting to match the channel you are sending on.
    • Channel Range: Channels 1–4 reach Move's four tracks by default. Channels 5–16 can reach Schwung slots configured to receive on those channels.
  9. Understand virtual module entry properties for RNBO packs

    main

    When a module uses scan_packs, each discovered .rnbopack file is treated as a virtual module_info_t with the following properties:

    PropertyValue / Logic
    id"{parent_id}-{pack_filename}" (extension removed)
    nameExtracted from the pack's internal set JSON (cached in a .name sidecar)
    dsp_pathInherited from the parent module's dsp.so
    ui_scriptInherited from the parent module's ui.js
    module_dirInherited from the parent module directory
    defaults_json{"pack": "/full/path/to/pack.rnbopack"}
    component_typeInherited from the parent
    capabilitiesAll capabilities inherited from the parent
  10. USB Host Mode Compatibility and Limitations

    main

    When using the Move in USB Host mode via the USB-C port, be aware of the following hardware and driver constraints:

    Supported Devices:

    • USB Mass Storage: Flash drives, SSDs, HDDs (drivers usb-storage, uas, sd_mod are built-in).
    • USB HID: Keyboards and controllers (usbhid is built-in).
    • USB MIDI: Standard USB MIDI class devices.

    Unsupported/Limitations:

    • USB Audio: No snd-usb-audio kernel module or ALSA subsystem is available. USB audio interfaces will not work.
    • Bus-powered devices: The USB-C port does not output VBUS power. All connected devices must be self-powered or powered via a powered hub.
    • USB-A Port: The physical USB-A port on the Move is connected to an XMOS chip for MIDI only and cannot be used for host mode/mass storage without firmware modification.
  11. How seamless set switching works via slot-level fade envelopes

    main

    To prevent audible clicks and pops when switching patches (sets) in shadow mode, Schwung uses slot-level fade envelopes instead of hard muting.

    When a patch switch is requested, the system follows this lifecycle:

    1. Fade-out: The current patch continues to render while its gain is ramped down to 0.0 over approximately 50ms.
    2. Silent Loading: Once the gain reaches 0.0, the system performs the blocking I/O operations (e.g., dlopen, file I/O) to load the new patch. Because the slot is silent, these operations do not cause audible discontinuities.
    3. Fade-in: After the new patch is loaded, its gain is ramped up from 0.0 to 1.0 over approximately 50ms.

    This design replaces the previous g_mute_countdown / MUTE_BLOCKS_AFTER_SWITCH logic which relied on hard-muting for a fixed number of blocks.

  12. How MIDI FX plugins access mic audio

    main

    MIDI FX plugins in the Schwung ecosystem can access real-time microphone audio by reading from the host's mapped memory. During the tick() callback, the plugin should access the audio input via the host_api_v1 pointer using the following calculation:

    audio_in = (const int16_t *)(host->mapped_memory + host->audio_in_offset)

    Critical Requirement: The host must ensure that subplugin_host_api.mapped_memory is correctly populated from the real SPI mailbox before each chain tick. If mapped_memory is null or invalid, the plugin will not receive audio data.

    const int16_t *audio_in = (const int16_t *)(s->host->mapped_memory + s->host->audio_in_offset);