Ports: the backend contract
Questa pagina è in inglese: in italiano ci sono per ora i tutorial e il glossario; menu, ricerca e navigazione sono in italiano.
A port is an interface that a core declares and a backend implements. This page lists every port, grouped by the capability it abstracts. Together they are the complete contract between shuffle_engine and whatever runs underneath it: implement them all, pass their contract tests, and the framework runs on a new backend.
Status. A port exists only when a core needs it: ports are designed when the first domain that uses them is specified (see modules.md). Until then an entry is planned: its capability and threading contract are fixed, its exact signature is not. Port headers are the source of truth once they exist.
The JUCE (shuffle_juce_*) and Tracktion (shuffle_te_*) implementations
named below live in the shuffle_juce satellite; the AI task
ports (core/ai_tasks) and their audio.cpp adapter live in
shuffle_ai. They are listed here because they complete the
contract.
- Conventions
- 1. Platform ports
- 2. Audio and MIDI I/O ports
- 3. Engine ports
- 4. Media ports
- 5. Network ports
- 6. AI ports
- Contract tests
Conventions
Section titled “Conventions”- Where a port lives: in the core that consumes it, under
core/<domain>/include/shuffle/<domain>/ports/. Ports shared by many domains live incore/base. - Names: a port is named after its capability and ends in
Portonly when the plain name would collide with a core type (MidiOutputPort, butClock,FileStore). - Ownership: a core holds ports by reference (it does not own its
environment). A port that hands out resources returns
std::shared_ptrwhen a resource can outlive the call, for example a device that may be unplugged while a stream still uses it. - Threading column:
control= only on the control thread;any= thread-safe;RT= callable on the audio thread (noexcept, no allocation, no blocking). - Availability: a port never assumes a capability exists on every
platform. It reports what is there (
isAvailable(), empty lists) and the core degrades gracefully.
1. Platform ports
Section titled “1. Platform ports”Declared in core/base. Every backend must provide all of them.
shuffle_platform (adapters/platform)
provides them without any backend, on the C++ standard library and a small
OS layer; shuffle_juce_platform provides them on JUCE.
| Port | Capability | Threading | JUCE implementation | Status |
|---|---|---|---|---|
TaskQueue |
post a task to the control thread; ask whether the caller is on it | post: any; query: any | JuceTaskQueue (shuffle_juce_platform) on juce::MessageManager::callAsync, isThisTheMessageThread |
done (TaskQueue.h); JUCE adapter and ControlLoop (shuffle_platform, a loop of its own on the standard library) pass the contract |
TimerService |
repeating and one-shot callbacks on the control thread; cancellable handles | control | ControlTimer (shuffle_juce_platform) on juce::Timer: the host’s control timer, a concrete class until a core needs the port; the same ControlTimer in shuffle_platform on ControlLoop timers (cancellable TimerId handles) |
planned |
WorkerPool |
run a job on a background thread, get completion on the control thread (engine loading, take writing, preparation; settings saves; environment refreshes) | run: any except RT | JuceWorkerPool (shuffle_juce_platform) on juce::ThreadPool |
done (WorkerPool.h), FakeWorkerPool in core/base; ThreadWorkerPool (shuffle_platform) on std::thread |
Clock |
monotonic time; absolute TAI time with a lock state (network-locked or local only); replaceable by a manual clock in tests | any, RT-safe reads | std::chrono + the netaudio PTP follower when locked |
done (Clock.h); SteadyClock (local only) in shuffle_net_sockets and shuffle_platform; JuceClock (high-resolution ticks, local only) in shuffle_juce_platform |
FileStore |
whole files by path: read, write, atomic replace, copy, remove, exists (durable saves build on these) | worker | JuceFileStore (shuffle_juce_platform) on juce::File, juce::TemporaryFile; atomic writes and replace by rename |
done (FileStore.h), consumed by showcontrol and synth; JUCE adapter and OsFileStore (shuffle_platform: Win32 and POSIX, POSIX-semantics rename on NTFS) pass the contract |
SettingsStore |
key/value text persistence for what belongs to the machine (preferences, the routing machine patch); reads from memory, writes persisted later and atomically | control | JuceSettingsStore (shuffle_juce_platform) on juce::PropertiesFile, saved on a worker, temporary file and rename |
done (SettingsStore.h); JUCE adapter and JsonSettingsStore (shuffle_platform: one JSON file through any FileStore, saved on a worker) pass the contract with reopen |
Logger |
leveled diagnostics; never on the audio thread | any except RT | juce::Logger |
planned (no core consumes it yet; errors are values) |
FeatureGate |
whether a feature may run now (licence, entitlement, user setting) | control | application-defined | planned |
DisplayInfo |
per display: stable identifier, name, full bounds and work area, scale, physical density, refresh rate, safe-area insets; notify on change | control | toolkit or OS display APIs | declared (DisplayInfo.h), fake and contract suite; consumed by ui_model (DisplayAssignments, WindowEnvironment); OsDisplayInfo (shuffle_platform) on Windows and macOS (CoreGraphics), declared unavailable on Linux, iOS and Android, where the window system or toolkit is needed |
SystemAppearance |
dark or light, increased contrast, reduced motion, forced colours; notify on change | control | OS appearance settings | declared (SystemAppearance.h), fake and contract suite; consumed by ui_model (WindowEnvironment) and ui_theme (ThemeController); OsSystemAppearance (shuffle_platform) on Windows, macOS and Linux (gsettings), neutral on iOS and Android |
FrameClock |
frame ticks per display, started and stopped on demand so an idle UI costs nothing | control | display link / vsync callbacks | declared (FrameClock.h), fake and contract suite; consumed by ui_model (WindowEnvironment) and ui_theme (Animator); adapter planned: it belongs to the UI backend that owns a window (display link, swap chain), not to shuffle_platform |
CivilTime |
TAI to and from local civil time in IANA zones, daylight-saving gaps and overlaps, whether the clock is synchronised | control | OS time-zone database (the C++20 one is not available on every platform) | done (CivilTime.h), consumed by cues; moves to core/base when a second core needs it |
2. Audio and MIDI I/O ports
Section titled “2. Audio and MIDI I/O ports”| Port | Capability | Consumers | Threading | JUCE / Tracktion implementation | Status |
|---|---|---|---|---|---|
OutputStage |
processing on the final device output, after the mix and before clipping, on every callback | timecode, voiceover, click | process: RT | Tracktion DeviceManager::setGlobalOutputAudioProcessor (stock API), wrapped by TeOutputStageHost (shuffle_te_devices), a host that chains several stages; on the framework’s engine the Engine is the host, run by EngineAudioCallback (shuffle_juce_audio, or shuffle_miniaudio without JUCE) |
done (OutputStage.h, OutputStageHost.h); Tracktion host adapted (README); engine host passes the contract through the JUCE device callback and through the miniaudio one |
AudioOutputTopology |
the named outputs a user routes to, and per endpoint channel the physical channel it lands on now, with the route (own binding, index guess, declared fallback) | timecode, tuning, mixer | control | provided by the routing core on top of AudioDeviceChannels |
done (AudioOutputTopology.h) |
AudioInputTopology |
the same for inputs | timecode, tuning | control | provided by the routing core |
done (AudioInputTopology.h) |
AudioDeviceChannels |
the audio devices present: stable key, kind, channel counts, driver channel names, sample rate, clock domain, readiness | routing | control | TeAudioDevices (shuffle_te_devices): the juce::AudioIODevices of Tracktion’s DeviceManager; JuceAudioDevices (shuffle_juce_audio) on JUCE’s audio driver types, without Tracktion; MiniaudioDevices (shuffle_miniaudio) on miniaudio’s backends, without JUCE |
done (AudioDeviceChannels.h); Tracktion adapted; JUCE adapted; miniaudio adapted |
AudioDeviceSwitch |
ask for a device to become the active one; the result arrives asynchronously | routing (backup device) | control | TeAudioDevices (shuffle_te_devices) on Tracktion’s DeviceManager; JuceAudioDevices (shuffle_juce_audio): the open posted through TaskQueue, the previous device reopened on failure; MiniaudioDevices (shuffle_miniaudio) the same way on miniaudio |
done (AudioDeviceSwitch.h); Tracktion adapted; JUCE adapted; miniaudio adapted |
InsertProcessor |
a per-block processing slot on a source, bus or output, with named fadeable parameters | routing, cues | process: RT; parameters: control | plugin hosting (rack); PluginInsert (shuffle_juce_plugins) on a hosted JUCE plugin; PluginInsert (shuffle_plugin_host) on a CLAP, VST3, LV2 or AU plugin, without JUCE |
done (InsertProcessor.h); JUCE plugin adapter passes the contract; the plugin host adapter passes it with CLAP, VST3 and LV2 |
MidiOutputPort |
send MIDI bytes now, from any thread; whole UMP packets to a port that takes them (sendUmp) |
timecode, showcontrol, midi, routing, sync (MIDI clock: sendNow from a timing thread, sendAt from the audio thread) |
send: RT | te::MidiOutputDevice, or JuceMidiOutput (shuffle_juce_midi) on juce::MidiOutput without Tracktion: real-time send through a preallocated queue, no UMP; or the outputs of LibremidiDevices (shuffle_libremidi, no JUCE): real-time send through a preallocated queue, UMP packets on Windows MIDI Services, CoreMIDI UMP and ALSA UMP |
done (MidiOutputPort.h) |
MidiOutputDirectory |
list outputs with driver type, persistent unique id, protocol and UMP groups (outputPorts, MidiPortInfo); find one by name; notify on rescan |
routing, showcontrol, midi | control | Tracktion DeviceManager MIDI outputs, or JuceMidiDevices (shuffle_juce_midi) without Tracktion, or LibremidiDevices (shuffle_libremidi) without JUCE |
done (MidiOutputDirectory.h) |
MidiInputDirectory |
list the enabled inputs with driver type, unique id, protocol, groups, aggregate and own-published flags (inputPorts); open one by exact name, never a default or closest one; rescans arrive through DeviceEvents |
midi, showcontrol, routing | control | Tracktion DeviceManager MIDI inputs, or JuceMidiDevices (shuffle_juce_midi) without Tracktion, or LibremidiDevices (shuffle_libremidi) without JUCE |
done (MidiInputDirectory.h) |
MidiInputSource |
one open input of its physical origin: raw MIDI 1.0 bytes or whole UMP packets with a monotonic timestamp, delivered to a core receiver (the midi core parses running status, real-time bytes and SysEx) |
midi, showcontrol | callback: RT or control (declared per subscription) | te::MidiInputDevice, or JuceMidiInput (shuffle_juce_midi) on juce::MidiInput, or the inputs of LibremidiDevices (shuffle_libremidi) on libremidi’s raw callbacks (bytes or whole UMP packets) |
done (MidiInputSource.h) |
MidiEndpointTopology |
list named MIDI inputs and outputs; resolve a named MIDI output to its shared tracked output (or its declared fallback’s), or to nothing when the port is missing (never a default port) | timecode, showcontrol, cues, midi | control | provided by the routing core |
done (MidiEndpointTopology.h) |
VirtualMidiPorts |
publish and unpublish MIDI inputs and outputs visible to other applications; platform capability and health | routing | control; data: MIDI thread | OS virtual MIDI (CoreMIDI, ALSA sequencer, Windows MIDI Services) | done (VirtualMidiPorts.h) |
VirtualAudioDevices |
publish audio inputs and outputs visible to other applications (inter-app audio, loopback); capability, channel limits, added latency | routing | control; data: RT once per block | platform-specific adapters | done (VirtualAudioDevices.h) |
InstrumentChains |
inject MIDI at internal instrument inputs and reset their processors (panic empties tails) | midi | RT | Tracktion plugin chains | done (InstrumentChains.h) |
DeviceEvents |
“the device set changed”: audio device switched, MIDI rescanned | every I/O consumer | control | juce::ChangeListener on the device manager; MIDI rescans: JuceMidiDevices (shuffle_juce_midi) on juce::MidiDeviceListConnection, or LibremidiDevices (shuffle_libremidi) on libremidi’s hotplug observer; audio devices: TeAudioDevices (shuffle_te_devices) on Tracktion’s DeviceManager, or JuceAudioDevices (shuffle_juce_audio) on the drivers’ change notifications, or MiniaudioDevices (shuffle_miniaudio): the device list polled on the control thread (miniaudio has no list notification) and the open device’s own notifications |
done (DeviceEvents.h); MIDI side adapted; audio side adapted on Tracktion, on JUCE and on miniaudio |
3. Engine ports
Section titled “3. Engine ports”What a live show needs from an audio engine. Implemented by the framework’s
own engine (core/engine) and by the Tracktion adapters (adapters/tracktion
in shuffle_juce). These are the ports with the largest surface; their
exact shape is fixed by the setlist and showcontrol specs.
| Port | Capability | Consumers | Threading | Tracktion implementation | Status |
|---|---|---|---|---|---|
Transport |
play and stop now, at a sample, at an absolute instant or on the musical count; locate, loop range, roll-in; set playback rate; end-of-song and jump plans; switch-and-start handover; segments with flags; position is the audible position; reports end reached, looped, jump executed, late start | setlist, showcontrol, timecode, video, ui | control (state readable: any; segments: RT) | TeTransport (shuffle_te_engine): the core TransportCursor in a graph node per song steering Tracktion’s play head (rate 1 only); the engine (engine.md EN-R136) |
done (Transport.h); Tracktion adapted with declared limits |
TempoMap |
beats <-> seconds, tempo and time-signature changes of a song | tempo, showcontrol, ui | any (immutable snapshots) | te::TempoSequence |
planned |
SongSessionFactory |
load a song document into a playable session in the background, report progress, cancel, release, detach a listener, health | setlist | control; loading on workers | TeSongSessionFactory (shuffle_te_engine): te::Edit files read by te::EditLoader |
done (SongSessionFactory.h); Tracktion adapted |
SongSession |
one loaded song: its transport, tempo map and arrangement, arm and disarm without a gap, tempo-follow capability, health | setlist | control | TeSongSession (shuffle_te_engine): one te::Edit per song; tempo map from its tempo sequence, arrangement from its markers |
done (SongSession.h); Tracktion adapted |
TempoSync |
shared musical tempo and phase with other apps and devices: join/leave/rejoin a session, tempo, beat and phase at a time, quantum, number of peers, start/stop sync, peer events | setlist, cues, tempo, ui | control; status RT-safe | the framework’s own sync protocol (UDP multicast/unicast, BLE, USB/serial, WebSocket relay, PTP when available); MIDI clock bridge; optional Ableton Link bridge |
done as the set list consumes it (TempoSync.h); moves to core/base when a second core needs it; protocol specified (sync.md) |
LiveSession |
the live-input session that stays up for the whole show, across songs | setlist | control | TeLiveSession (shuffle_te_engine): a dedicated Tracktion edit for live inputs |
done (LiveSession.h); Tracktion adapted |
ShowStore |
write the songs and the set list marked as changed, on workers, atomically | setlist | control; writing on workers | the show document (package) through FileStore |
done (ShowStore.h) |
ConfirmationHandler |
show and withdraw the requests a person answers on stage (pending switch, peer stop) | setlist | control | the user interface | done (ConfirmationHandler.h) |
TimelineEvents |
events placed on a song timeline, delivered on the audio thread in the block that crosses them, with the block’s segments (starts, jumps, loop wraps) | showcontrol, events, voiceover | events and receiver: control; delivery: RT | Tracktion playback graph node per song feeding the core SegmentTimeline (TeSongSession::timelineEvents(), shuffle_te_engine); the core SegmentTimeline on set-list transport segments |
done (TimelineEvents.h); Tracktion adapted |
TimecodeActions |
start (for a length or until stopped), stop and query a named timecode definition; transport stopped | showcontrol | control | the timecode service |
done (TimecodeActions.h) |
CommandRunner |
run an internal action’s command through the command layer as a show-control trigger | showcontrol | control | the core SessionCommandRunner on a CommandSession |
done (CommandRunner.h) |
MixControl |
gain, pan, mute, solo, sends of the tracks a show exposes; the song output gain for a stop with fade | setlist, mixer view models, assistant | control | song gain: TeMixControl (shuffle_te_engine) applied by each song’s graph node; tracks: te::VolumeAndPanPlugin, te::AuxSendPlugin |
song output gain done (MixControl.h), Tracktion adapted; track gains planned |
CueTarget |
one per output domain: describe and validate, prepare on workers, start at the instant its first sample is audible (from an offset, on normal or audition outputs), cancel a start not yet performed, stop (fade, at once, hard, leave loop), pause and resume, declared panic behaviour, reset; reports ended, failed, late, cancelled and validity changes | cues | control; start instants in TAI | each output domain; audio by the engine (engine.md EN-R142) or by TeAudioCueTarget (shuffle_te_engine, rate 1, one output) |
done (CueTarget.h); audio adapted on Tracktion |
CueParameters |
resolve a parameter by name (range, unit, interpolation space, fader law), read its current value, receive per-block fade values and duck offsets on the audio thread | cues | resolve and read: control; values: RT | routing, the engine (EN-R155), effect and device adapters; the level of Tracktion audio cues (TeAudioCueTarget) |
done (CueParameters.h); cue levels adapted on Tracktion |
MusicPosition |
tempo map and song position of the music a cue list follows, the musical count, loop wraps and jumps (generations), playing state | cues | control (snapshot with a shared tempo map) | setlist |
done (MusicPosition.h) |
PluginHost |
scan, list, instantiate, save and restore plugin state asynchronously; a PluginInstance is prepared, processes blocks with MIDI, parameter changes and transport, reports latency and tail, and names its own bypass parameter |
engine, rack, plugin editors | control; scanning and instantiation on workers; process: RT | JucePluginHost / JucePluginInstance (shuffle_juce_plugins) on JUCE 9’s headless juce::AudioPluginFormatManager (VST3, AU, LV2), without a device manager; NativePluginHost (shuffle_plugin_host) on the formats themselves (CLAP, VST3, LV2 through lilv, AU), without JUCE; or te::PluginManager, te::ExternalPlugin |
done (PluginHost.h), fake and contract suite in core/engine; JUCE adapter passes the contract; the plugin host adapter passes it with CLAP, VST3 and LV2 test plugins |
FourierTransform |
optional faster platform FFT behind the core’s own transform (sizes, normalisation, real and complex) | dsp, mir, spectral | plans: control; execution: any thread, RT-safe, a plan shared by many threads | platform DSP libraries; the core CoreFourierTransform is the default |
done (FourierTransform.h), fake and contract suite in core/dsp |
ProcessingThreads |
a fixed set of real-time worker threads with wake and wait that are safe on the audio thread; 0 means single-threaded | engine | RT | OS real-time threads (audio workgroups where the platform has them): JuceProcessingThreads (shuffle_juce_audio) on juce::Thread::startRealtimeThread, joined to the device’s juce::AudioWorkgroup; OsProcessingThreads (shuffle_miniaudio) on std::thread with MMCSS, QoS or SCHED_FIFO, no workgroup |
done (ProcessingThreads.h), fake and contract suite in core/engine; JUCE adapted; OS threads adapted |
TimeStretcher |
time-stretch and pitch-shift voices from a preallocated pool (TimeStretchers): ratio, pitch, process, reset, latency |
engine | RT | permissive stretch library | done (TimeStretcher.h), fake (resampling) and contract suite in core/engine |
AudioFileProbe |
duration, sample rate, channels, peak overview of an audio file | ui waveform, package | worker | te::AudioFile, juce::AudioThumbnail data |
planned |
Recorder |
record an input to a file with a known start position | voiceover | control; writing on workers | te::InputDevice recording |
the engine’s own Recorder component (Recording.h); a port when a second consumer needs it |
MediaReader, MediaSource |
read decoded frames by position without waiting on the audio thread (missing frames are silence, counted), or with an unlimited wait offline; open files | engine | read: RT (wait none) or worker (unlimited); open: worker | buffered readers of formats |
done as the engine’s reader seam (MediaReader.h), fake and contract suite in core/engine; implemented on the formats buffered reader when it lands |
AudioWriter, AudioWriters |
write takes (recording mode) and renders (atomic mode), report clipping and non-finite counts, tell whether a path is taken |
engine | worker | writers of formats; TeAudioWriters (shuffle_te_engine, WAV through te::AudioFileWriter) |
done as the engine’s writer seam (AudioWriter.h), fake and contract suite in core/engine |
SongReader |
read a song document into tracks, items, tempo map and arrangement | engine | worker | the session document (package) |
done (SongReader.h), fake in core/engine |
4. Media ports
Section titled “4. Media ports”| Port | Capability | Consumers | Threading | Implementation today | Status |
|---|---|---|---|---|---|
VideoDecoder |
open a clip, seek, decode frames at a time, report format | video | worker; frames handed to control | platform decoders in shuffle_media (Media Foundation, AVFoundation, MediaCodec, WebCodecs; no FFmpeg) | specified (shuffle_media specs/video.md) |
DocumentRenderer |
render pages of a document (PDF) to images at a size | documents | worker | platform PDF renderer | planned |
ImageSink |
hand a decoded frame or page to a display surface | video, documents | control | toolkit image | planned |
SpeechSource |
provide audio for a spoken cue (synthesised from text, at the requested rate; voice and language chosen by name) | voiceover | worker (stop token, progress) | audio.cpp TTS models (shuffle_ai_audiocpp); recorded cues via FileStore to come |
done (SpeechSource.h, declared in core/ai_tasks), fake and contract suite; adapted |
5. Network ports
Section titled “5. Network ports”| Port | Capability | Consumers | Threading | Implementation today | Status |
|---|---|---|---|---|---|
DatagramSocket |
UDP send/receive, unicast/multicast/broadcast, interface selection, TTL, DSCP, kernel receive timestamps when available (transmit times user-space) | netaudio, redundancy, osc, showcontrol, remote, lighting | send: any (RT-safe); receive: network thread | OS sockets: OsDatagramNetwork (shuffle_net_sockets) |
done (DatagramSocket.h), adapted |
StreamSocket |
TCP listen, connect with timeout, read and write | netaudio (RTSP), osc (TCP framing), showcontrol (raw TCP actions) | worker | OS sockets: OsStreamNetwork (shuffle_net_sockets) |
done (StreamSocket.h), adapted |
HostResolver |
a host name to an IP address; dotted addresses resolve to themselves | showcontrol | network sender thread (may block) | OS resolver (getaddrinfo) |
done (HostResolver.h) |
NetworkInterfaces |
list interfaces with addresses; timestamping capability and change polling in the adapter (OsNetworkInterfaces::timestamping, InterfaceWatcher) |
netaudio, remote | control | OS APIs: OsNetworkInterfaces (GetAdaptersAddresses, getifaddrs) |
done (NetworkInterfaces.h), adapted |
OscEndpoint |
send and receive OSC messages and bundles, scheduled against the Clock |
showcontrol, remote | receive: control | core component of the osc domain on DatagramSocket and StreamSocket (no backend needed) |
done (OscEndpoint.h) |
HttpClient |
requests with timeouts and cancellation | lyrics lookup, updater, assistant | worker | juce::URL (needs curl on Linux) |
planned |
MessageServer |
bidirectional messaging with remote devices (WebSocket-like) | remote | callbacks: control | to be chosen per backend | planned |
SerialPort |
byte streams to serial devices | lighting (DMX interfaces), hwcontrol | worker | platform adapter | planned |
5a. UI layout ports
Section titled “5a. UI layout ports”| Port | Capability | Consumers | Threading | Implementation | Status |
|---|---|---|---|---|---|
TextMeasurer |
the advance of a run of text in a ui_theme font role at a size (natural, one line), the font’s ascent and descent, optionally the platform’s line-break opportunities | ui_layout (the text-fitting rule, layouts, widgets fitting one label) | the owner’s thread (normally control) | DWriteTextMeasurer (shuffle_text_dwrite, Windows); Core Text, FreeType with HarfBuzz, Android and the browser’s canvas to come |
done (TextMeasurer.h), testing::FakeTextMeasurer and contract suite; DirectWrite adapter passes it |
5b. UI bridge ports
Section titled “5b. UI bridge ports”| Port | Capability | Consumers | Threading | Implementation | Status |
|---|---|---|---|---|---|
BridgeChannel |
the sending side of one UI connection: send text or binary, buffered bytes, close, transport kind | ui_bridge | control | WebUiPage (in-process, over a WebUiHost); WebSocketServer (shuffle_net_websocket); WebAssembly to come |
done (BridgeChannel.h), fake and contract suite |
TopicSource |
a declared topic’s current value for bound parameters, change notification | ui_bridge | control | each domain (ThemeSource in ui_bridge; the example’s set list) |
done (TopicSource.h), fake and contract suite |
StreamSource |
fill one frame of a declared real-time stream from values the audio side already published, without blocking | ui_bridge | control or worker, non-blocking | each domain | done (StreamSource.h), fake and contract suite |
WebUiHost |
an embedded webview: load packaged content under a policy (served paths, CSP, navigation, permissions, developer tools), post and receive messages with frame and origin, report page starts and crashes | ui_bridge (WebUiPage), applications |
control, non-blocking | JuceWebUiHost (shuffle_juce_webui) on juce::WebBrowserComponent: WebView2, WKWebView, WebKitGTK, Android WebView |
done (WebUiHost.h); the JUCE adapter passes the contract suite on WebView2, with declared limits (no permission, developer-tools or crash events in JUCE’s API) |
5c. Native UI ports
Section titled “5c. Native UI ports”Specified by ui_widgets. Renderer2D is declared
by core/ui_render, PlatformWindow and RequestSink by core/ui_widgets,
TextMeasurer by ui_layout.
| Port | Capability | Consumers | Threading | Implementation | Status |
|---|---|---|---|---|---|
Renderer2D |
draw a plain-data draw list (rectangles, rounded rectangles, paths, arcs, gradients, shadows, text runs through the platform text engine, images, clips, opacity layers) into a window surface or an offscreen target, only inside the damage, with buffer age; register fonts, images and static paths; present with frame pacing; read back pixels; report device loss | ui_widgets, applications drawing their own canvases | control (the window’s thread) | D2dRenderer (shuffle_render_d2d: Direct2D, DirectWrite with the fonts of shuffle_text_dwrite, DXGI flip model; WARP fallback); Core Graphics or Metal, Android, Vulkan or GLES, Canvas or WebGPU later |
done (Renderer2D.h), testing::FakeRenderer2D on the CPU reference rasteriser and the contract suite with the reference scenes in core/ui_render; the Direct2D adapter passes it on the GPU and on WARP (spec UW-R143 to UW-R158, UW-R179 to UW-R183) |
PlatformWindow |
one window or mobile surface (main, output, child of a plugin host, offscreen): pointer, wheel, magnify, key, text-input and composition events, focus, size, scale and visibility; cursor, pointer capture, input-method area and soft keyboard, title, chrome hit regions, accessibility tree and announcements, clipboard | ui_widgets (WindowHost) |
control, non-blocking | Win32Window (shuffle_window_win32: per-monitor DPI v2, WM_POINTER pen and touch, IMM32, custom title bars, a UI Automation provider; its Win32FrameClock on the vertical blank); AppKit and UIKit, Android, Wayland and X11, the browser later |
done (PlatformWindow.h), testing::FakePlatformWindow and the contract suite in core/ui_widgets; the Win32 adapter passes it driven by window messages (spec UW-R159 to UW-R169, UW-R184) |
RequestSink |
hand a screen’s command request to the command session or the bridge and return its identifier at once; answer a confirmation | ui_widgets | control, non-blocking | the application’s (over commands::Session in process, over the bridge remotely); testing::RecordingRequestSink |
done (RequestSink.h, UW-R6, UW-R121) |
6. AI ports
Section titled “6. AI ports”| Port | Capability | Consumers | Threading | Implementation today | Status |
|---|---|---|---|---|---|
InferenceRuntime |
load a model graph, report capabilities and execution providers, run tensors, cancel | inference | worker or inference thread | ONNX Runtime, TF Lite, llama.cpp adapters | specified |
ModelFetcher |
fetch model files from https, hf:// or folders with progress and cancel | inference | worker | HTTP adapter | specified |
SpeechToText |
audio to text with segment and word timestamps at the input’s rate, language, optional voice-activity detection | voiceover, assistant, lyrics | worker (stop token, progress) | audio.cpp ASR models with a VAD model (shuffle_ai_audiocpp) |
done (SpeechToText.h), fake and contract suite; adapted |
StemSeparator |
a mix to named stems aligned with it (same rate, channels, frames) | neural, edit, applications | worker (stop token, progress) | audio.cpp separation models (shuffle_ai_audiocpp) |
done (StemSeparator.h), fake and contract suite; adapted |
AudioToMidi |
audio to MIDI notes (midi::Sequence in seconds of clip time), optionally narrowed to instruments |
neural, edit, applications | worker (stop token, progress) | audio.cpp transcription models (shuffle_ai_audiocpp) |
done (AudioToMidi.h), fake and contract suite; adapted |
AudioDecoder, AudioEncoder, PacketDecoder, PacketEncoder, CodecProvider |
decode and encode audio files and packets with exact delay/padding; codec capabilities with licence and patent notes | formats, neural, mir, render, library | worker | OpenCodecs (shuffle_codecs: libFLAC, libvorbis, libopus, dr_mp3); platform codecs and the Apple ALAC and OpenCORE AAC libraries for AAC and ALAC; the core’s ContainerAudioDecoder / ContainerAudioEncoder implement the first two |
done (AudioDecoder.h, AudioEncoder.h, CodecProvider.h); fake and contract suites in core/formats; adapted for FLAC, Vorbis, Opus and MP3 reading (contract suites pass) |
ByteSource, ByteSink |
a file to read (random access, size or stream, optional mapped view) and to write (sequential, header patches, atomic or recording) | formats | worker | OS files (streaming, memory mapping); MemoryBytes.h and FileStoreBytes.h in the core |
done (ByteSource.h, ByteSink.h), contract suites in core/formats |
SecretStore |
store API keys in the operating system keychain, never in files or logs | llm | control | OS keychains | specified |
ProcessLauncher |
start a process without a shell, with pipes (MCP stdio servers) | llm | worker | OS process APIs | specified |
HttpListener |
serve HTTP on loopback by default (MCP server) | llm, generated surfaces | network thread | HTTP adapter | specified |
SecureRandom |
cryptographically secure random bytes (tokens) | llm, remote | any | OS random sources | specified |
LanguageModel |
complete a prompt, optionally constrained by a grammar or a JSON Schema, or with tool calling; report context size and capabilities; cancellable | AI adapters on top of the commands core |
worker; never the audio thread | llama.cpp or ONNX locally; hosted APIs | planned |
The commands core never calls a model: it produces the catalogue, the
schemas, the grammars and the snapshots, and validates whatever comes back.
Which model is used, local or hosted, small or large, is a choice of the
composition root and of the source’s profile.
Contract tests
Section titled “Contract tests”Every port ships a contract test suite: header-only Catch2 test templates
in core/<domain>/contracts/, parameterised by a factory that builds an
implementation. Each adapter’s test runner instantiates the suites for the
ports it implements; the core’s own tests instantiate them for the fakes.
Each core with contract suites exposes them as a CMake target,
shuffle::<core>_contracts (header-only, links the core and Catch2). They
are built with the engine’s tests (SHUFFLE_ENGINE_BUILD_TESTS), or alone
with SHUFFLE_ENGINE_BUILD_CONTRACTS=ON: that is how an adapter tested in
another repository, such as a satellite, runs the suites
without building the engine’s own tests. shuffle_engine_fetch_catch2()
(in cmake/ShuffleEngineDeps.cmake) brings Catch2 and puts its CMake
scripts on the caller’s module path for catch_discover_tests.
flowchart LR
suite["contracts/MidiOutputPortContract.h<br/>(behaviour every implementation must show)"]
fake["core tests:<br/>FakeMidiOutput"]
juce["shuffle_juce tests:<br/>JuceMidiOutput"]
other["adapters/<new backend> tests:<br/>NewMidiOutput"]
suite --> fake
suite --> juce
suite --> other
A contract test checks the behaviour the cores rely on, for example:
TaskQueue: tasks run on the control thread, in posting order, never synchronously insidepost;FileStore: an interrupted write leaves the previous content intact;OutputStagehost: a stage removed from the host is never called again, and is released off the audio thread;Transport:stopfollowed byplayrestarts from the stop position unless a locate happened in between.
This is what turns “replace the backend” from an act of faith into a checklist: when the new adapters pass every contract suite, the cores behave the same on top of them.
© 2026 Umberto Sorbo. Il software è distribuito con la Apache License, Version 2.0, e fornito “così com’è”, senza garanzie o condizioni di alcun tipo; leggi l’esclusione di responsabilità.
SHUFFLE Framework e il logo di SHUFFLE Framework sono marchi di Umberto Sorbo; la licenza non concede alcun diritto su di essi (marchi). Gli altri nomi appartengono ai rispettivi titolari. Caratteri: IBM Plex Sans e JetBrains Mono, SIL Open Font License 1.1.
Contatti: shuffleframework@gmail.com · Instagram @shuffleframework. Problemi di sicurezza: scrivi allo stesso indirizzo con “Security” nell’oggetto, non in una issue pubblica.