Search guides and the APICtrl K
  1. API reference
  2. Formats and wrappers

standalone.h

The standalone application.

#include <sonore/standalone.h>
On this page

Design notes

The standalone stays on the webview deliberately, where the plugin formats now default to the native editor. Two reasons, both specific to this program: it is ONE instance in ONE process, so the browser's 100-300 MB is paid once rather than thirty times; and its page carries the audio and MIDI device pickers, which are not plugin parameters and have no native widget behind them. A native standalone shell is worth building, and building it means building those pickers first.

The same plugin, runnable without a DAW: a window showing the SAME webview faceplate, audio through the OS, and the same Instance/bridge machinery the plugin formats use, so what you audition standalone IS what loads in a host.

Include it AFTER clap_wrapper.h (it reuses Instance, the bridge wiring and the UI queue) in a translation unit compiled with SONORE_BUILD_STANDALONE. It defines main().

Modes, because a standalone that can only be judged by ear cannot be tested:

(no flags)            window + live audio: the actual product
--verify              render offline, assert basic health, exit code = verdict
--render out.wav [s]  render `s` seconds offline to a WAV (no device needed)
--play [s]            live audio without a window, exits after `s` seconds
--shot out.bmp [s]    (Windows) open the window, wait, screenshot, exit
--test-signal         feed an effect the built-in arpeggio (see below)
--help                every option: devices, ASIO, MIDI, capture, track info

The offline modes need no audio device and no display, which is what makes the standalone testable in CI on all three platforms.

Effects have an internal test source (a slow saw arpeggio with an envelope): harmonically rich enough that filters, saturation and reverbs are audible on it. It is a TEST signal and it plays when a test asks for it -- the offline modes, --play, or --test-signal. It used to play whenever nothing else fed the effect, which meant somebody who double-clicked their delay heard a looping arpeggio coming out of a plugin they had just downloaded, with no way to reach a device picker (the window shows the PLUGIN's faceplate, not a host's chrome) and nothing on screen saying where the sound came from. An app that opens on silence and says how to feed it is the lesser surprise. Instruments start silent and are played from the on-screen keyboard, or programmatically in the offline modes.

Types 6

What runDspStandalone needs beyond the DSP: silence for a key input nobody routes, somewhere for aux buses to go, and the MIDI and expression buffers of one block. Sized by prepare(), on the thread that starts the audio.

These were thread_local vectors grown on first use -- so the first block on every NEW audio thread allocated, which is the first block after every device switch and driver reset, inside the callback that must not.

A file played into an effect, instead of the built-in test tone.

"An effect gets its input from the standalone's internal source (or a WAV, once the user drops one)" is what the audio backend's own header said, and the parenthesis never happened. A distortion you can only feed a sine to is a demo of a sine, and rendering a plugin over real material is the first thing anyone wants from a standalone build.

Whatever readAudioFile() accepts works, so this is WAV, AIFF, FLAC, MP3 and Ogg without a line of format code here.

Resampling is NOT done. A 44.1k file played at 48k would come out a semitone-ish sharp and slightly short, and silently doing that to somebody's material is worse than saying the rate does not match -- especially when the answer is to pass the file the plugin will actually run at.

The internal source an effect processes when nothing else is playing: a slow two-octave saw arpeggio with a soft envelope. Chosen over a sine because a sine through a filter is inaudible as a demo, and over noise because noise through a reverb is just more noise.

prepare() first, with the run's rate; then next() once per sample, on the audio thread, for a mono sample peaking about 13 dB below full scale. Allocates nothing.

Everything a running standalone owns.

The standalone's command line, as parseOptions() reads it and main acts on it: an offline mode (verify, render, capture, dump the page) or the live window, which devices to open, and what to feed the plugin. Plain data, filled once at startup; printUsage() lists every flag. Pointers point into argv and live as long as the program.

The audio device, and the ability to change it while the program runs.

Holds REFERENCES to the two outputs the run path already owns rather than owning them itself: an interface that is open has a window sitting on it, and a hub that owned the device would have to outlive or be outlived by that window in some order nobody would remember.

UI thread only: select() and the service calls are driven from the page's messages and the UI tick. A switch stops the audio, or clears the flag the callback checks, before it changes anything the callback reads.

Functions 43

Aliases 1

Constants 2

↑↓ to move↵ to openEvery public name in the headers, and every guide