C++ RAII Wrapper

bbx_graph.h provides a header-only C++ wrapper for safe resource management.

Overview

The bbx::Graph class wraps the C FFI with RAII semantics:

  • Constructor calls bbx_graph_create()
  • Destructor calls bbx_graph_destroy()
  • Move semantics prevent accidental copies

Usage

Include the header:

#include <bbx_graph.h>

Create and use a graph:

bbx::Graph dsp;  // Automatically creates handle

if (dsp.IsValid()) {
    dsp.Prepare(sampleRate, bufferSize, numChannels);
    dsp.Process(inputs, outputs, numChannels, numSamples, params, numParams);
}

Class Reference

Constructor

Graph();

Creates a new DSP graph. Check IsValid() to verify allocation succeeded.

Destructor

~Graph();

Destroys the graph and frees all resources.

Move Operations

Graph(Graph&& other) noexcept;
Graph& operator=(Graph&& other) noexcept;

The class is movable but not copyable:

bbx::Graph a;
bbx::Graph b = std::move(a);  // OK - a is now invalid
// bbx::Graph c = b;          // Error - not copyable

Prepare

BbxError Prepare(double sampleRate, uint32_t bufferSize, uint32_t numChannels);

Prepare for playback. Call from prepareToPlay().

Reset

BbxError Reset();

Reset DSP state. Call from releaseResources().

Process

void Process(
    const float* const* inputs,
    float* const* outputs,
    uint32_t numChannels,
    uint32_t numSamples,
    const float* params,
    uint32_t numParams,
    const BbxMidiEvent* midiEvents = nullptr,
    uint32_t numMidiEvents = 0
);

Process audio with optional MIDI events. Call from processBlock().

For effects (no MIDI):

dsp.Process(inputs, outputs, numChannels, numSamples, params, numParams);

For synthesizers (with MIDI):

dsp.Process(inputs, outputs, numChannels, numSamples, params, numParams, midiEvents, numMidiEvents);

IsValid

bool IsValid() const;

Returns true if the handle is valid.

handle

BbxGraph* handle();
const BbxGraph* handle() const;

Access the raw handle for advanced use.

Complete Header

#pragma once

#include "bbx_ffi.h"

namespace bbx {

class Graph {
public:
    Graph()
        : m_handle(bbx_graph_create())
    {
    }

    ~Graph()
    {
        if (m_handle) {
            bbx_graph_destroy(m_handle);
        }
    }

    // Non-copyable
    Graph(const Graph&) = delete;
    Graph& operator=(const Graph&) = delete;

    // Movable
    Graph(Graph&& other) noexcept
        : m_handle(other.m_handle)
    {
        other.m_handle = nullptr;
    }

    Graph& operator=(Graph&& other) noexcept
    {
        if (this != &other) {
            if (m_handle) {
                bbx_graph_destroy(m_handle);
            }
            m_handle = other.m_handle;
            other.m_handle = nullptr;
        }
        return *this;
    }

    BbxError Prepare(double sampleRate, uint32_t bufferSize, uint32_t numChannels)
    {
        if (!m_handle) {
            return BBX_ERROR_NULL_POINTER;
        }
        return bbx_graph_prepare(m_handle, sampleRate, bufferSize, numChannels);
    }

    BbxError Reset()
    {
        if (!m_handle) {
            return BBX_ERROR_NULL_POINTER;
        }
        return bbx_graph_reset(m_handle);
    }

    void Process(const float* const* inputs,
        float* const* outputs,
        uint32_t numChannels,
        uint32_t numSamples,
        const float* params,
        uint32_t numParams,
        const BbxMidiEvent* midiEvents = nullptr,
        uint32_t numMidiEvents = 0)
    {
        if (m_handle) {
            bbx_graph_process(m_handle, inputs, outputs, numChannels, numSamples,
                              params, numParams, midiEvents, numMidiEvents);
        }
    }

    bool IsValid() const { return m_handle != nullptr; }

    BbxGraph* handle() { return m_handle; }
    const BbxGraph* handle() const { return m_handle; }

private:
    BbxGraph* m_handle { nullptr };
};

} // namespace bbx