Clack

repository·main·Indexed 42 days ago

https://github.com/prokopyl/clack

A collection of low-level, safe Rust wrappers for the CLAP audio plugin API. It provides crates for building audio plugins (clack-plugin), hosts (clack-host), and utilities for plugin discovery (clack-finder) and standard CLAP extensions (clack-extensions), all designed for minimal overhead and high memory safety.

Tokens
49.5K
Snippets
126
Records
180
Agent score
53%

What's inside Clack

  1. Overview of Clack

    main

    Clack is a set of Rust crates providing low-level, safe, and lightweight wrappers for the CLAP audio plugin API. It is built on top of clap-sys and is designed to be a foundation for higher-level audio tools, though it can be used directly for full plugin or host implementation.

    The project is split into two primary crates:

    • clack-plugin: For implementing CLAP plugins.
    • clack-host: For implementing CLAP hosts.
    • clack-extensions: A separate crate providing implementations for standard and stable CLAP extensions.
  2. Overview of clack-common

    main

    The clack-common crate provides lightweight, low-level Rust wrappers for the CLAP audio plugin API, supporting both plugin and host development. It is built on top of clap-sys.

    Usage Patterns

    • Indirect Usage (Recommended): Most developers should use clack-common through the higher-level clack-plugin or clack-host crates, as all modules from clack-common are re-exported there.
    • Direct Usage: This crate can be used as a standalone dependency in any project without requiring other Clack crates.
  3. Features demonstrated in clack-plugin-gain

    main

    The clack-plugin-gain project serves as a reference implementation for the clack-plugin and clack-extensions crates. It demonstrates the following Clack API capabilities:

    • General Clack plugin structure: Implementation of the Plugin trait and its sub-traits: PluginMainThread, PluginAudioProcessor, and PluginShared.
    • Audio input/output: Declaring audio ports via the audio-ports CLAP extension and accessing audio buffers within the process call.
    • Parameter management: Using the params CLAP extension to declare parameters, format them for user display, and handle updates from automation or DAW UIs.
    • State management: Using the state CLAP extension to persist parameter values for later restoration.
    • GUI integration: Using the gui CLAP extension to facilitate interaction with a graphical user interface.
  4. Create an audio plugin host with clack-host

    main

    The clack-host crate provides low-level, memory-safe Rust wrappers for building audio plugin hosts using the CLAP API. It is designed to be lightweight and close to the underlying C API while providing ergonomic Rust features like Option, Result, and iterators.

    To implement a host, you must define your own handler structures that implement the SharedHandler and HostHandlers traits. This allows you to manage host-side logic across different threads (e.g., the main thread and the audio processing thread).

    use clack_host::events::event_types::*;
    use clack_host::prelude::*;
    
    // 1. Define shared state/logic for the host
    struct MyHostShared;
    impl<'a> SharedHandler<'a> for MyHostShared {
        fn request_restart(&self) { /* ... */ }
        fn request_process(&self) { /* ... */ }
        fn request_callback(&self) { /* ... */ }
    }
    
    // 2. Define the host configuration
    struct MyHost;
    impl HostHandlers for MyHost {
        type Shared<'a> = MyHostShared;
        type MainThread<'a> = ();
        type AudioProcessor<'a> = ();
    }
  5. What clack-plugin-gain demonstrates

    main

    The clack-plugin-gain project serves as a reference implementation for the clack-plugin and clack-extensions crates. It demonstrates how to implement the following core Clack/CLAP capabilities:

    • General Clack plugin structure: Implementation of the Plugin trait and its sub-traits: PluginMainThread, PluginAudioProcessor, and PluginShared.
    • Audio input/output: Using the audio-ports CLAP extension to declare ports and accessing audio buffers within the process call.
    • Parameter management: Using the params CLAP extension to declare parameters, format them for UI display, and handle updates from automation or DAW UIs.
    • State management: Using the state CLAP extension to persist parameter values for later restoration.
  6. Core Clack plugin implementation patterns in clack-plugin-gain

    main

    The clack-plugin-gain example demonstrates how to implement a standard CLAP plugin using the clack-plugin and clack-extensions crates. It showcases the following implementation patterns:

    • Plugin Lifecycle: Implementation of the Plugin trait, along with its sub-traits: PluginMainThread, PluginAudioProcessor, and PluginShared.
    • Audio I/O: Using the audio-ports CLAP extension to declare audio ports and accessing audio buffers within the process method.
    • Parameter Management: Using the params CLAP extension to declare parameters, handle user-facing formatting, and process updates from automation or DAW UIs.
    • State Persistence: Using the state CLAP extension to save and restore parameter values.
  7. Implementation patterns in clack-plugin-polysynth

    main

    The clack-plugin-polysynth project serves as a reference implementation for the clack-plugin and clack-extensions crates. It demonstrates how to implement the following core Clack/CLAP features:

    • Plugin Lifecycle: Implementing the Plugin trait along with its sub-traits: PluginMainThread, PluginAudioProcessor, and PluginShared.
    • Audio Ports: Using the audio-ports extension to declare audio ports and accessing audio buffers within the process call.
    • Note Ports: Using the note-ports extension to declare note ports and processing input events during the process call.
    • Parameters: Using the params extension to declare parameters, handle user-facing formatting, and receive updates from automation or DAW UIs.
    • State Management: Using the state extension to save and restore parameter values.
  8. Use clack-host-cpal to load and run a CLAP plugin

    main

    The clack-host-cpal CLI tool is a functional CLAP host that uses CPAL for audio output. It can load a plugin, display its GUI, and forward MIDI input.

    To run a plugin, you must provide either a specific file path or a plugin ID.

    Important Limitations:

    • Output Only: Due to CPAL limitations, this host only supports audio output. It does not handle audio input, meaning audio effect plugins will only receive silence. It is best suited for synthesizers.
    • Latency: CPAL is not a real-time audio library; expect potential delays in audio or event processing depending on your OS.
    • Duplex Mode: The host cannot open a stream in duplex-mode (simultaneous input and output).
    # Load a plugin by its specific file path
    clack-host-cpal --file-path /path/to/plugin.clap
    
    # Load a plugin by its unique ID (scans standard CLAP paths)
    clack-host-cpal --plugin-id com.example.synth
    
    # Load a specific plugin ID from a specific file
    clack-host-cpal --file-path /path/to/plugin.clap --plugin-id com.example.synth
  9. Use clack-extensions with clack-plugin or clack-host

    main

    The clack-extensions crate provides wrappers and utilities for standard CLAP extensions. To use them, you must follow a two-step configuration in your Cargo.toml:

    1. Enable the specific extension features you need (e.g., gui, audio-ports, latency).
    2. Enable the integration feature corresponding to your project type:
      • If you are building a plugin, enable the clack-plugin feature.
      • If you are building a host, enable the clack-host feature.

    Once enabled, the clack_extensions namespace will expose the extension types and utilities required for your implementation.

  10. Create an audio plugin with `clack-plugin`

    main

    To create a CLAP-compliant audio plugin, implement the Plugin and DefaultPluginFactory traits for your plugin struct, and the PluginAudioProcessor trait for your audio processor. Use the clack_export_entry! macro to export the plugin entry point.

    To produce a valid CLAP dynamic library, you must compile your project as a cdylib library crate in your Cargo.toml.

    Key components to implement:

    • Plugin: Defines the associated types for AudioProcessor, Shared (data shared between threads), and MainThread (data for the main thread).
    • DefaultPluginFactory: Provides the PluginDescriptor (metadata) and handles the instantiation of Shared and MainThread state.
    • PluginAudioProcessor: Implements the activate and process methods. The process method is where audio manipulation occurs using the Audio and Process types.
    use clack_plugin::prelude::*;
    
    pub struct MyGainPlugin;
    
    impl Plugin for MyGainPlugin {
        type AudioProcessor<'a> = MyGainPluginAudioProcessor;
        type Shared<'a> = ();
        type MainThread<'a> = ();
    }
    
    impl DefaultPluginFactory for MyGainPlugin {
        fn get_descriptor() -> PluginDescriptor {
            PluginDescriptor::new("org.rust-audio.clack.gain", "Clack Gain Example")
        }
    
        fn new_shared(_host: HostSharedHandle<'_>) -> Result<Self::Shared<'_>, PluginError> {
            Ok(())
        }
    
        fn new_main_thread<'a>(
            _host: HostMainThreadHandle<'a>,
            _shared: &'a Self::Shared<'a>,
        ) -> Result<Self::MainThread<'a>, PluginError> {
            Ok(())
        }
    }
    
    pub struct MyGainPluginAudioProcessor;
    
    impl<'a> PluginAudioProcessor<'a, (), ()> for MyGainPluginAudioProcessor {
        fn activate(_host: HostAudioProcessorHandle<'a>, _main_thread: &(), _shared: &'a (), _audio_config: PluginAudioConfiguration) -> Result<Self, PluginError> {
            Ok(Self)
        }
    
        fn process(&mut self, _process: Process, mut audio: Audio, _events: Events) -> Result<ProcessStatus, PluginError> {
            for mut port_pair in &mut audio {
                let Some(channel_pairs) = port_pair.channels()?.into_f32() else { continue; };
    
                for channel_pair in channel_pairs {
                    match channel_pair {
                        ChannelPair::InputOnly(_) => {}
                        ChannelPair::OutputOnly(buf) => buf.fill(0.0),
                        ChannelPair::InputOutput(input, output) => {
                            for (input, output) in input.iter().zip(output) {
                                *output = input * 2.0
                            }
                        }
                        ChannelPair::InPlace(buf) => {
                            for sample in buf {
                                *sample *= 2.0
                            }
                        }
                    }
                }
            }
    
            Ok(ProcessStatus::Continue)
        }
    }
    
    clack_export_entry!(SinglePluginEntry<MyGainPlugin>);
  11. Build and install clack-plugin-gain from source

    main

    To build the clack-plugin-gain-gui example, navigate to the root directory containing the Clack source code and use cargo build with the --release flag.

    After building, locate the resulting library file in target/release. To use it in a CLAP-compatible DAW or host, copy or link the file to your plugin directory and ensure it has a .clap extension (e.g., clack_plugin_gain.clap).

    cargo build -p clack-plugin-gain-gui --release
  12. Use the `bundle-plugins` helper command

    main

    The bundle-plugins helper command is a utility used to build platform-appropriate CLAP bundles for every clack-plugin example in the workspace. When executed, it compiles all plugins and generates their respective .clap bundles in the target/dist directory at the root of the workspace.

    You can run this command from any directory within the repository/workspace as a cargo subcommand.

    cargo bundle-plugins