Salta ai contenuti

Choose backends and wire the composition root

Questa pagina è in inglese: in italiano ci sono per ora i tutorial e il glossario; menu, ricerca e navigazione sono in italiano.

The cores need a few capabilities from the outside world: a control thread, a clock, files, devices, sockets. Each capability is a port (an abstract class declared by a core). You pick an adapter for each port, create it in your main, and pass it to the cores that need it.

The adapters without JUCE are part of shuffle_engine. The JUCE and Tracktion adapters (juce_*, te_*) come from the shuffle_juce satellite and the audio.cpp adapter (ai_audiocpp) from shuffle_ai; see satellites.

Capability Ports Without JUCE (shuffle_engine) With JUCE or Tracktion (shuffle_juce)
control thread, timer, workers, clock, files, settings, appearance, displays TaskQueue, WorkerPool, Clock, FileStore, SettingsStore, SystemAppearance, DisplayInfo platform (ControlLoop, ControlTimer, ThreadWorkerPool, SteadyClock, OsFileStore, JsonSettingsStore, OsSystemAppearance, OsDisplayInfo) juce_platform (JuceTaskQueue, ControlTimer, JuceWorkerPool, JuceClock, JuceFileStore, JuceSettingsStore)
audio devices and the engine’s device callback AudioDeviceChannels, AudioDeviceSwitch, DeviceEvents, ProcessingThreads miniaudio (MiniaudioDevices, EngineAudioCallback, OsProcessingThreads) juce_audio (JuceAudioDevices, EngineAudioCallback, JuceProcessingThreads)
MIDI devices, MIDI 1.0 and 2.0 MidiInputDirectory, MidiOutputDirectory, DeviceEvents libremidi (LibremidiDevices) juce_midi (JuceMidiDevices, MIDI 1.0)
plugin hosting PluginHost, PluginInstance, InsertProcessor plugin_host (NativePluginHost: CLAP, VST3, LV2, AU) juce_plugins (JucePluginHost: VST3, AU, LV2)
an embedded web UI WebUiHost a WebSocket server for pages in a separate browser: net_websocket (WebSocketServer) juce_webui (JuceWebUiHost)
UDP, TCP, network interfaces DatagramNetwork, StreamNetwork, NetworkInterfaces net_sockets (OsDatagramNetwork, OsStreamNetwork, OsNetworkInterfaces)
audio codecs CodecProvider codecs (OpenCodecs: FLAC, Vorbis, Opus, MP3 read); AAC and ALAC through platform codecs: coming
songs as a multitrack engine SongSessionFactory, Transport, MixControl, … the framework’s own core/engine (EngineSongSessionFactory, …) te_engine, te_devices on Tracktion Engine
AI audio tasks: speech, recognition, stems, audio to MIDI SpeechSource, SpeechToText, StemSeparator, AudioToMidi (core/ai_tasks in shuffle_ai) ai_audiocpp on audio.cpp, in shuffle_ai (desktop only, opt-in)

Each adapter’s README lists its classes, threads, declared limits and the platforms it was verified on: platform, miniaudio, libremidi, net_sockets, net_websocket, codecs, plugin_host; in the satellites, audiocpp, juce, tracktion. The complete port list is in ports.md.

You can mix them: JUCE for devices, net_sockets for OSC, codecs for files; or no JUCE at all, with platform, miniaudio and libremidi. In tests, use the fakes instead (Test your app).

List the modules after USES; the names are the ones above and the core names (setlist, routing, engine, …). Without JUCE:

shuffle_engine_add_app (MyShow SOURCES src/main.cpp
USES platform miniaudio setlist routing)

With JUCE, add shuffle_juce after shuffle_engine so its modules exist:

add_subdirectory (../shuffle_engine shuffle_engine)
add_subdirectory (../shuffle_juce shuffle_juce)
shuffle_engine_add_app (MyShow BACKEND juce GUI SOURCES src/main.cpp
USES juce_platform juce_audio juce_midi setlist routing)

When shuffle_engine is a subproject of your build, adapters are off unless you switch them on at configure time:

Option Default as a subproject Builds
SHUFFLE_ENGINE_WITH_PLATFORM ON platform
SHUFFLE_ENGINE_WITH_MINIAUDIO ON miniaudio (fetches miniaudio)
SHUFFLE_ENGINE_WITH_NET_SOCKETS ON (not on WebAssembly) net_sockets
SHUFFLE_ENGINE_WITH_LIBREMIDI OFF libremidi (fetches libremidi)
SHUFFLE_ENGINE_WITH_CODECS OFF codecs (fetches the codec libraries)
SHUFFLE_ENGINE_WITH_PLUGIN_HOST OFF plugin_host (desktop only; fetches the CLAP and VST3 interface headers)

The satellites have options of their own: shuffle_juce builds the juce_* modules (JUCE found through JUCE_HOME or downloaded at 9.0.3) and the te_* modules with SHUFFLE_JUCE_WITH_TRACKTION (TRACKTION_HOME or a download); shuffle_ai builds ai_audiocpp with SHUFFLE_AI_WITH_AUDIOCPP (fetches and builds audio.cpp; desktop only).

BACKEND juce or tracktion in shuffle_engine_add_app makes the executable a JUCE application; BACKEND core is a plain C++ program and can still use the plain adapters (net_sockets, codecs, miniaudio).

Create the adapters first, then the cores that take them, then what uses the cores. Destroy in reverse. Members of one struct give you that order for free. This is the JUCE wiring from shuffle_juce’s adapters/juce/README.md:

const juce::ScopedJuceInitialiser_GUI juce; // or a JUCEApplication
JuceTaskQueue tasks; // build in this order,
JuceWorkerPool workers; // destroy in reverse
JuceClock clock;
JuceFileStore showFiles (showFolder);
JuceSettingsStore settings (appData.getChildFile ("machine.settings"), tasks, workers);
JuceMidiDevices midiDevices (clock, tasks);
ControlTimer timer ([&] { /* the cores' periodic calls */ });
JucePluginHost plugins (tasks, workers, settings, appFiles); // appFiles: a JuceFileStore on the app data folder
JuceProcessingThreads threads (JuceProcessingThreads::countForThisMachine());
engine::Engine engine ({ clock, &threads });
EngineAudioCallback callback (engine, tasks, &threads);
JuceAudioDevices audioDevices (callback, tasks); // opens nothing yet
audioDevices.requestDevice (audioDevices.defaultDeviceKey());

Then the cores, each with the ports its constructor lists, for example a set list on the engine’s sessions or a routing core on the devices:

routing::RoutingCore routing ({ audioDevices, audioDevices, settings, clock });

Rules that keep it working on every platform:

  • create and destroy everything on the control thread;
  • stop audio (close the devices) before destroying the callback, the engine and the threads it uses;
  • never block in a constructor: devices, scans and loads answer later through a listener or a completion posted to the task queue.

Complete, runnable roots: examples/miniaudio/play (audio, no JUCE), examples/platform/settings (platform ports), examples/net/osc_udp (sockets), examples/libremidi/ports (MIDI, no JUCE); in shuffle_juce, examples/juce/play (audio on JUCE), examples/juce/platform (platform ports on JUCE) and examples/tracktion/song (Tracktion).

Why it is built this way: Cores, ports and adapters, Why no globals.