Tracktion Engine

repository·develop·Indexed 23 days ago

https://github.com/tracktion/tracktion_engine

A high-level data model and set of classes for building sequence-based audio applications, from simple file players to full Digital Audio Workstations (DAWs). Supplied as a JUCE module requiring C++20, it supports macOS, Windows, Linux, Raspberry PI, iOS, and Android. The repository also includes the CHOC header-only utility library for audio, MIDI, networking, and GUI tasks, as well as farbot for realtime-safe concurrency and data sharing.

Tokens
13.7K
Snippets
24
Records
76
Agent score
80%

What's inside tracktion_engine

  1. Overview of Tracktion Engine features

    develop

    Tracktion Engine provides a comprehensive suite of features for audio and MIDI production, including:

    • Playback Engine: High-performance multi-CPU audio engine with minimal latency, full plugin delay compensation, and transport control (including scrubbing).
    • Audio Capabilities: Support for multiple formats (WAV, AIFF, Flac, OGG, MP3, CAF, Rex), time/pitch stretching (via Elastique, Rubber-band, or SoundTouch), and advanced clip warping.
    • MIDI Capabilities: Support for note, controller, and sysex events, MIDI pattern generation, MPE, and synchronization via MIDI Time Code (MTC).
    • Clip Launching: Non-linear performance features including scenes, follow actions, and clip slot recording.
    • Plugin System: Includes built-in utility and effect plugins, a rack patching environment, and an API to register custom plugins. The engine can also be wrapped inside a plugin to sync with a host.
    • Automation: Bezier curve automation for plugin parameters, automation recording, and various modifiers (LFO, envelope follower, etc.).
    • Rendering: Background thread rendering for edits, track/clip/MIDI rendering, and MIDI file exporting.
    • Utilities: Undo/redo, clipboard, selection, and save/load compatibility with Tracktion Waveform projects.
  2. Overview of CHOC library features

    develop

    CHOC provides a wide range of utility headers categorized by functionality:

    Text and Files

    • choc/text/choc_StringUtilities.h: String trimming, splitting, joining, and URI encoding.
    • choc/text/choc_UTF8.h: UTF8 validation and iteration.
    • choc/text/choc_Files.h: Loading/saving files and managing temp files.
    • choc/text/choc_Wildcard.h: File wildcard matching.
    • choc/text/choc_HTML.h: HTML DOM tree generation.

    Containers and Memory

    • choc/containers/choc_Span.h: A std::span replacement.
    • choc/containers/choc_SmallVector.h: A vector with pre-allocated internal storage.
    • choc/memory/choc_PoolAllocator.h: Fast memory pool allocator.
    • choc/memory/choc_Base64.h: Base64 encoding/decoding.
    • choc/memory/choc_xxHash.h: Fast hashing algorithm.

    Audio and MIDI

    • choc/audio/io/choc_AudioMIDIPlayer.h: Cross-platform audio/MIDI device abstraction.
    • choc/audio/choc_SampleBuffers.h: Multi-channel sample data management.
    • choc/audio/choc_AudioFileFormat.h: Reading/writing WAV, FLAC, Ogg-Vorbis, and MP3.

    GUI and Web

    • choc/gui/choc_WebView.h: A dependency-free single-header WebView for embedded browser views.
    • choc/network/choc_HTTPServer.h: Simple HTTP and WebSocket server.

    Threading

    • choc/threading/choc_SpinLock.h: Spin-lock implementation.
    • choc/containers/choc_VariableSizeFIFO.h: Variable size object FIFO for lock-free queues.
  3. Overview of CHOC library capabilities

    develop

    The CHOC library provides a wide range of utilities for high-performance C++ applications. The following categories summarize the core functionalities demonstrated in the examples:

    • Audio & MIDI: Audio buffer manipulation (multi-channel, resampling, I/O) and complete MIDI toolkit (parsing, generation, note utilities).
    • Core Data Structures & Concurrency: Dynamic choc::value objects with JSON support, lock-free FIFO structures (SPSC/MPMC), and advanced threading patterns (SpinLock, TaskThread, producer-consumer with backpressure).
    • Web & Networking: Embedded JavaScript engines (QuickJS) with C++ bindings, WebView-based desktop GUIs, and HTTP/WebSocket servers (requires Boost).
    • System & Text: Cross-platform file system monitoring (recursive directory watching) and comprehensive text processing (UTF-8, wildcard matching, HTML/Code generation).
    • Performance: Fast hashing via xxhash (32/64-bit, streaming) and real-time audio processing patterns.
  4. Overview of the Tracktion Engine Audio Graph Rewrite

    develop
    The Tracktion Engine is undergoing a complete overhaul of its audio processing graph to address long-standing limitations in the legacy architecture. The rewrite aims to improve Plugin Delay Compensation (PDC), multi-threaded CPU utilization, and processing flexibility. This overhaul is being implemented incrementally, starting with the rebuilding of the Rack processing system to validate core principles before expanding to the full engine model.
  5. License for the CHOC library

    develop

    The CHOC library is released under the permissive ISC license. You are permitted to use, copy, modify, and/or distribute this software for any purpose, with or without fee, provided that the copyright notice and this permission notice appear in all copies.

    Note for Windows users: If you use the choc::ui::WebView class on Windows, it embeds Microsoft redistributable code which is subject to its own permissive license. For specific details regarding this dependency, refer to the choc_WebView.h header file.

  6. Understand the Tracktion Engine core concept

    develop

    Tracktion Engine is a framework designed for creating timeline-based Digital Audio Workstation (DAW) applications. It provides the heavy-lifting audio and MIDI logic required for professional music software, such as high-performance playback, plugin management, and automation.

    Important Limitation: Tracktion Engine does not provide a User Interface (UI). Developers are responsible for implementing the visual representation of arrangements, tracks, clips, mixers, piano rolls, and other UI elements. This allows for complete creative freedom in designing the application's look and feel.

  7. Share data between realtime and non-realtime threads with RealtimeObject

    develop

    Use RealtimeObject<T, RealtimeObjectOptions> to safely share data of type T between a single realtime thread and non-realtime threads. The RealtimeObjectOptions template parameter determines which thread is allowed to mutate the data.

    Mutation Modes

    • RealtimeObjectOptions::nonRealtimeMutatable: Only the non-realtime thread can modify the data. The realtime thread can only read it.
    • RealtimeObjectOptions::realtimeMutatable: Only the realtime thread can modify the data. The non-realtime thread can only read it.

    Accessing Data

    To access the data, use the ScopedAccess<ThreadType> nested class. You must provide the correct ThreadType (ThreadType::realtime or ThreadType::nonRealtime) corresponding to the thread currently executing the code.

    struct BiquadCoeffecients  {  float b0, b1, b2, a1, a2; };
    RealtimeObject<BiquadCoeffecients, RealtimeObjectOptions::nonRealtimeMutatable> biquadCoeffs;
    
    /* called on realtime thread */
    void processAudio (float* buffer)
    {
        RealtimeObject<BiquadCoeffecients, RealtimeObjectOptions::nonRealtimeMutatable>::ScopedAccess<ThreadType::realtime> coeffs(biquadCoeffs);
        processBiquad (*coeffs, buffer);
    }
    
    /* called on non-realtime thread */
    void changeBiquadParameters (BiquadCoeffecients newCoeffs)
    {
        RealtimeObject<BiquadCoeffecients, RealtimeObjectOptions::nonRealtimeMutatable>::ScopedAccess<ThreadType::nonRealtime> coeffs(biquadCoeffs);
        *coeffs = newCoeffs;
    }
  8. Understand the Tracktion Engine 2.0 module division

    develop

    Tracktion Engine 2.0 is split into three distinct modules, all accessible via the tracktion:: namespace. While most applications will use the high-level engine module, you can use the others for lower-level requirements:

    • tracktion::core: Contains primitive types for low-level operations, such as defining time/beat positions and durations. It is designed to be header-only and is used extensively throughout the framework.
    • tracktion::graph: The low-level audio processing library. It provides base classes for processing nodes and graph construction, utilizing lock-free multi-threaded playback.
    • tracktion::engine: The high-level framework containing the Edit model and classes for rapid application building.

    Note: A Tracktion Engine license is required to use any of these modules.

  9. Use new Time and Beat primitives

    develop

    The framework now uses distinct types to disambiguate between beats and time (seconds), as well as between positions and durations. This prevents misuse via the type system.

    Key Features:

    • Literals: Use _tp (Time Position), _td (Time Duration), _bp (Beat Position), and _bd (Beat Duration) to construct types.
    • Chrono Integration: TimePosition and TimeDuration can be constructed from std::chrono types or a number of samples (given a sample rate). This allows usage like setDelay(1ms) using std::chrono::literals.
    • Type Aliases: Position classes include a DurationType alias to retrieve the corresponding duration type.
    • Conversions: To convert between time and beats, you must use a TempoSequence via the toTime or toBeats methods.
    • Variant Types:
      • EditTime: A variant that can hold either time or beat types. Requires a TempoSequence to extract the underlying value.
      • EditTimeRange: A variant that can hold either time or beat ranges.
  10. Implement a custom Plugin class

    develop

    To create a custom audio effect, inherit from Plugin. A typical implementation includes:

    1. State Management: Use CachedValue to refer to plugin state properties (e.g., gain) using the Edit's UndoManager.
    2. Parameter Registration: Use addParam to register AutomatableParameter objects with the plugin. Use attachToCurrentValue to link the parameter to your CachedValue.
    3. Audio Processing: Override applyToBuffer(const PluginRenderContext& fc). Inside this method, check isEnabled() before processing. You can access the destination buffer via fc.destBuffer and the number of samples via fc.bufferNumSamples.
    4. Lifecycle: In the destructor, call notifyListenersOfDeletion() and detachFromCurrentValue() for all parameters.
    // In Constructor
    auto um = getUndoManager();
    gainValue.referTo (state, IDs::gain, um, 1.0f);
    
    gainParam = addParam ("gain", TRANS("Gain"), { 0.1f, 20.0f });
    gainParam->attachToCurrentValue (gainValue);
    
    // In Destructor
    notifyListenersOfDeletion();
    gainParam->detachFromCurrentValue();
    
    // In applyToBuffer
    if (! isEnabled())
        return;
    
    for (int channel = 0; channel < fc.destBuffer->getNumChannels(); ++channel)
    {
        auto dest = fc.destBuffer->getWritePointer (channel);
        for (int i = 0; i < fc.bufferNumSamples; ++i)
            dest[i] = std::tanh (gainValue * dest[i]);
    }
  11. Use fifo for realtime-safe ringbuffers

    develop

    The fifo class is a realtime-safe ringbuffer that never locks or blocks. It allows for various concurrency and failure mode configurations via template parameters.

    Concurrency Options

    Controlled by farbot::fifo_options::concurrency::single or farbot::fifo_options::concurrency::multiple. You can configure the producer and consumer independently (e.g., a multi-producer, single-consumer FIFO).

    Failure Modes

    Controlled by farbot::fifo_options::full_empty_failure_mode:

    • return_false_on_full_or_empty: Returns false on a push if full, or on a pop if empty.
    • overwrite_or_return_default: Overwrites the oldest data on a full push, or returns a default-constructed element on an empty pop. Note: Using this mode may cause the ordering of the FIFO to be lost.

    Wait-free Guarante

    Operations are wait-free if:

    1. The producer/consumer is accessed from a single thread, OR
    2. The producer/consumer uses the overwrite_or_return_default failure mode.
    fifo<std::function<void()>*,
          fifo_options::concurrency::single,
          fifo_options::concurrency::multiple, 
          fifo_options::full_empty_failure_mode::return_false_on_full_or_empty,
          fifo_options::full_empty_failure_mode::overwrite_or_return_default> my_fifo; // <- this fifo is wait-free on push and pop
    
    my_fifo.push(mylambda);
    std::function<void()>* almabda;
    my_fifo.pop(alambda);
  12. Implement multichannel support in v3.5

    develop

    v3.5 introduced arbitrary channel counts across devices, plugins, racks, clips, recording, and rendering.

    Key classes and methods for multichannel workflows:

    • ChannelConfiguration: A class used to describe channel layouts.
    • Plugin::getBusses(): Plugins must use this method to explicitly declare their bus layouts.
    • Device channel groupings: Supports groupings of any size with savable I/O layout presets.