Cmajor Language Documentation

repository·main·Indexed 20 days ago

https://github.com/cmajor-lang/cmajor

Cmajor is a C-family programming language optimized for fast, portable audio and DSP signal processing across CPU, DSP, and GPU architectures. This documentation covers core language usage, the implementation of digital instruments and physical models, and advanced features such as Source Transformers for compiling external languages like Faust. It includes guides on prototyping plugins with AI, integrating machine learning inference via RTNeural and TensorFlow, and running patches through VS Code, CLI, or VST/AU plugins.

Tokens
35.8K
Snippets
100
Records
161
Agent score
72%

What's inside Cmajor

  1. Overview of the Fixed Filter Bank patch

    main

    The Fixed Filter Bank is a patch inspired by classic synthesizer modules like the Serge Resonant Equalizer and the Buchla Spectral Processor.

    Key features include:

    • Bandpass Filters: A series of filters spaced at intervals of a major seventh to avoid accentuated resonance in specific keys.
    • Panning Controls: Individual panning controls for each filter.
    • Frequency Analysis: Demonstrates the use of Cmajor's realOnlyForwardFFT function for real-time frequency analysis.
    • Custom GUI: Utilizes a React.js-based interface.
  2. Understand the Cmajor C++ API structure

    main

    The Cmajor C++ API is designed to allow embedding the Cmajor compiler and JIT engine into native applications or plugins. This enables you to build and run Cmajor code and patches within your own C++ environment. The API is organized into three layers of abstraction:

    1. Low-level Compiler Interaction: Use the classes in include/cmajor/API to interact with the compiler directly at a low level.
    2. High-level Task Helpers: Use the classes in include/cmajor/helpers for common tasks such as loading and running patches.
    3. COM Interop (Internal): The include/cmajor/COM directory contains the lowest-level COM classes. Note: You should generally avoid using these directly; instead, use the C++ wrapper classes in include/cmajor/API which provide a much easier-to-use layer that hides COM implementation details.
  3. Get started with Cmajor

    main

    Cmajor is a C-family programming language specifically designed for high-performance, portable DSP (Digital Signal Processing) code. It aims to match C/C++ performance while being portable across CPUs, DSPs, GPUs, and TPUs.

    To get started, you can use the Cmajor VSCode extension for a one-click installation process, or refer to the Quick Start Guide for command-line tool usage.

  4. Ring-Modulation Demo Overview

    main

    The Ring-Modulation Demo is an example implementation of a classic ring-mod effect. It serves two primary purposes for developers:

    1. DSP Implementation: Demonstrates how to implement a ring modulator using a simplified digital model based on research by Julian Parker.
    2. GUI and Event Handling: Demonstrates how to build a custom GUI that sends custom events to a patch to trigger specific actions, such as sample playback.
  5. Manage Processor State Variables

    main

    Variables declared inside a processor definition persist for the lifetime of that processor instance. They can be accessed and modified by any function within that processor.

    Variables without an initial value are automatically set to zero.

    processor NumberGenerator
    {
        output stream float out;
        float value;
        int counter = 10;
        let increment = 2.5f;
    
        void main()
        {
            value = 100.0f;
            while (--counter > 0)
            {
                emitNextNumber();
                advance();
            }
        }
    
        void emitNextNumber()
        {
            out <- value;
            value += increment;
        }
    }
  6. Use Aggregate and Null Literals

    main

    Aggregate Literals

    Use parentheses () to initialize structures or arrays.

    Null Literals

    An empty pair of parentheses () represents a null or zero value for any type. This can be used to initialize an array to zero, reset a structure to its default state, or reset a numeric type to zero.

    // Aggregate initialization
    int[5] x = (2, 3, 4, 5, 6);
    MyStruct y = (3, 6.5f, "hello", (3, 4, false));
    var z = bool<4> (true, false, false, true);
    
    // Null/Zero initialization
    var x = int[5](); // creates an array of 5 zeros
    x = ();           // sets all 5 elements of x to 0
    
    var y = MyStruct(1, "hello", 3);
    y = ();           // resets all elements of the object to zero or null
    
    int[] slice = (1, 2, 3);
    slice = ();       // resets variable to an empty slice with size 0
    
    float64 i = 123.0;
    i = ();           // works for numeric types too
  7. Organize code using Namespaces

    main

    Namespaces are used to group types, processors, graphs, variables, and functions. They prevent name collisions and provide logical structure.

    • Nesting: You can nest namespaces using blocks or the shorthand namespace A::B::C syntax.
    • Accessing Symbols: Use the double-colon :: operator to specify a qualified path to a symbol.
    • Contents: Namespaces can contain other namespaces, processor or graph declarations, function definitions, global constants, and struct or using type declarations.
    namespace A::B::C
    {
        void myFunction() {}
    }
    
    // Accessing
    A::B::C::myFunction();
  8. Choose a Cmajor license

    main

    Cmajor is distributed under a dual licensing scheme. You can choose between the open-source GPLv3 license or a Commercial license depending on your project requirements.

    GPLv3 License

    You can use Cmajor without contacting the developers if you fully comply with the terms of the GPLv3 (or later). This is suitable for open-source projects that adhere to GPL requirements.

    Commercial License

    If your project requires any of the following, you should obtain a commercial license:

    • Embedding the Cmajor engine in a closed-source product.
    • Mixing Cmajor with non-GPL-compliant code.
    • Any other custom requirements that conflict with GPLv3 terms.
  9. Explore the PatchConnection API surface

    main

    The PatchConnection object serves as the primary interface between the Cmajor engine and your JavaScript GUI/worker code. It contains:

    • Direct Methods: Methods like getCmajorVersion() and access to the midi object.
    • utilities Property: A container for various JavaScript objects and classes provided by the API (e.g., PianoKeyboard).

    For a complete list of available methods and utility classes, refer to the source files in the cmajor/javascript/cmaj_api/ directory.

  10. How the Ring Modulator DSP model works

    main

    The demo implements a ring modulator by multiplying an input signal with a carrier signal (such as a sine wave).

    To replicate the characteristic distortion found in vintage analogue ring modulators (like those used in Doctor Who), this implementation uses a simplified digital model that includes diode blocks. The model follows these steps:

    1. Inputs: Uses a sine wave as the modulating signal (Vin) and the user's audio input (Vc).
    2. Diode Blocks: Two distinct diode blocks are created. Each utilizes a phase-inverted signal that is combined with the distorted signal.
    3. Summation: Both diode blocks are summed together to complete the ring modulation effect, resulting in the characteristic hard-clipping distortion.
  11. Understand Stream, Value, and Event Endpoints

    main

    Cmajor provides three primary endpoint types, each suited for different data characteristics:

    Stream Endpoints

    • Behavior: Transmits a continuous sequence of sample-accurate values (one per frame).
    • Best Use: Continuously changing signals like audio data.
    • Constraint: Currently, types must be scalar (float, integer, or vectors of floats/ints) to allow summing.
    • Cost: Higher overhead due to storing and updating values every frame.

    Value Endpoints

    • Behavior: Holds a fixed value that is not sample-accurate. Updates have effectively zero overhead when not changing.
    • Best Use: Parameters that change infrequently, such as master volume.
    • Feature: Supports automatic ramping for scalar values via the Cmaj API (smooth interpolation over a specified number of frames).

    Event Endpoints

    • Behavior: Triggers specific logic when data arrives. Requires an event handler function in the processor/graph.
    • Best Use: Asynchronous notifications or discrete data changes.
    • Handlers: The handler name and type must match the endpoint. If an endpoint has multiple types, you must declare a handler for each type.
    processor P
    {
        input event float<2> myInput;
    
        // Handler for the event
        event myInput (float<2> e)
        {
            // logic here
        }
    }
  12. How GuitarLSTM machine learning inference works

    main

    The GuitarLSTM workflow demonstrates how Cmajor integrates with machine learning models through the following pipeline:

    1. Training: Uses TensorFlow to build and train the model.
    2. Export: The trained TensorFlow model is exported to an RTNeural .json file using scripts from the RTNeural package.
    3. Cmajor Generation: The script executes a Python utility located at cmajor/tools/rtneural which consumes the RTNeural .json file to generate a native Cmajor file for inference.