Skip to content

API synopsis

This page lists the public C++ surface of libgradient_motion and the gradient-motiond daemon. The generated API reference is published at stagesoft.github.io/gradient-motion-engine, built from the docstrings in the headers themselves.

Every namespace below is rooted at gme::. The header file for each entry is linked.


gme::time

src/time/MtcTickSource.h

namespace gme::time {

enum class MtcStartError { kOk, kNoPortsAvailable, kPortNotFound };

class MtcTickSource {
public:
    MtcTickSource();
    ~MtcTickSource();   // blocks until any in-flight callback returns

    void          setTickCallback(std::function<void(long)> cb);
    MtcStartError start(const std::string& midiPort);
    long          getMtcMs() const;
    bool          isRunning() const;
};

} // namespace gme::time

One instance per process — mtcreceiver uses process-global static state.


gme::gradient

src/gradient/Curve.h — abstract base:

namespace gme::gradient {

class Curve {
public:
    virtual double evaluate(double t) const = 0;
    virtual ~Curve() = default;
    Curve(const Curve&)            = delete;
    Curve& operator=(const Curve&) = delete;
    Curve(Curve&&)            = default;
    Curve& operator=(Curve&&) = default;
};

} // namespace gme::gradient

Concrete curves:

Header Class Parameters
LinearCurve.h LinearCurve
SigmoidCurve.h SigmoidCurve steepness (default 8.0), midpoint (default 0.5)
BezierCurve.h BezierCurve cx1, cy1, cx2, cy2 (defaults 0.25, 0.1, 0.75, 0.9)
EaseInCurve.h EaseInCurve exponent (default 2.0)
EaseOutCurve.h EaseOutCurve exponent (default 2.0)
SCurve.h SCurve
ScaledCurve.h ScaledCurve output min/max scaling
ResampledCurve.h ResampledCurve inner curve + 256-sample LUT
CrossfadePair.h CrossfadePair two curves (deferred motion type)

src/gradient/CurveFactory.h:

namespace gme::gradient {

class CurveFactory {
public:
    static std::optional<std::unique_ptr<Curve>>
    createCurve(const std::string& type,
                const nlohmann::json& params = nlohmann::json::object());

private:
    CurveFactory() = delete;
};

} // namespace gme::gradient

Supported type strings: "linear", "sigmoid", "bezier", "ease_in", "ease_out", "scurve". Unknown types return std::nullopt. The returned curve is always wrapped in a ResampledCurve.


gme::motion

src/motion/IMotion.h:

namespace gme::motion {

class IMotion {
public:
    std::string motion_id;
    std::string osc_key;              // "host:port:path"
    long        start_mtc_ms = 0;
    float       duration_ms = 0.0f;
    bool        completed = false;
    int         consecutive_osc_failures = 0;

    virtual ~IMotion() = default;
    virtual EvalResult evalAndSend(long mtc_ms) = 0;
    virtual void       sendSnapToEnd() = 0;
    virtual void       inheritFrom(const IMotion& prior) = 0;
};

} // namespace gme::motion

src/motion/EvalResult.h:

struct EvalResult {
    bool        completed      = false;
    bool        failed         = false;
    const char* failure_reason = nullptr;   // static storage
};

src/motion/FadeMotion.h:

class FadeMotion final : public IMotion {
public:
    using OscSendFn = std::function<int(lo_address, const char*, float)>;

    FadeMotion(std::string motion_id,
               std::string osc_key,
               long  start_mtc_ms,
               float duration_ms,
               float start_value,
               float end_value,
               std::unique_ptr<gme::gradient::Curve> curve,
               lo_address osc_target,           // takes ownership
               std::string osc_path,
               std::string osc_host,
               int   osc_port,
               OscSendFn oscSend);
    ~FadeMotion() override;                     // lo_address_free(osc_target_)

    EvalResult evalAndSend(long mtc_ms) override;
    void       sendSnapToEnd() override;
    void       inheritFrom(const IMotion& prior) override;

    float start_value     = 0.0f;
    float end_value       = 0.0f;
    float last_sent_value = 0.0f;
    std::string osc_path;
    std::string osc_host;
    int         osc_port = 0;
};

src/motion/MotionFactory.h:

class MotionFactory {
public:
    using OscSendFn = std::function<int(lo_address, const char*, float)>;

    struct Context {
        const gme::time::MtcTickSource& mtcSource;
        OscSendFn                       oscSend;
        std::function<void(gme::signal::StatusKind,
                           const std::string&,
                           const std::string&)> emitStatus;
    };

    static std::unique_ptr<IMotion> fromCommand(const gme::signal::FadeCommand& cmd,
                                                const Context& ctx);
    MotionFactory() = delete;
};

src/motion/MotionRegistry.h:

class MotionRegistry {
public:
    static constexpr int kOscFailureThreshold = 5;

    using OscSendFn = std::function<int(lo_address, const char*, float)>;

    MotionRegistry(const gme::time::MtcTickSource& mtcSource,
                   std::function<void(gme::signal::StatusKind,
                                     const std::string&,
                                     const std::string&)> statusDirect,
                   OscSendFn oscSend = nullptr);

    void   apply(gme::signal::FadeCommand& cmd);
    void   addMotion(std::unique_ptr<IMotion> m);
    void   cancelMotion(const std::string& motion_id, bool snap_to_end);
    void   cancelAll();
    void   tick(long mtc_ms);
    std::size_t size() const noexcept;
};

gme::signal

src/signal/FadeCommand.h:

namespace gme::signal {

struct FadeCommand {
    enum class Type { START_FADE, CANCEL_MOTION, CANCEL_ALL, START_CROSSFADE };

    Type type = Type::START_FADE;

    std::string motion_id;
    std::string node_name;

    std::string osc_host;
    int         osc_port = 0;
    std::string osc_path;

    float start_value  = 0.0f;
    float end_value    = 0.0f;
    float duration_ms  = 0.0f;

    std::string    curve_type;
    nlohmann::json curve_params;

    long start_mtc_ms = -1;   // -1 = "start at current MTC"

    std::string partner_motion_id;
    std::string partner_osc_path;
    float       partner_start_value = 0.0f;
    float       partner_end_value   = 0.0f;
};

enum class ParseResult {
    Ok, TargetMismatch, NodeMismatch, UnknownCommand,
    MissingField, TypeError, MalformedJson
};

enum class StatusKind { MotionComplete, MotionError };

} // namespace gme::signal

Required field matrix:

Command Required
start_fade motion_id, node_name, osc_host, osc_port, osc_path, start_value, end_value, duration_ms, curve_type, start_mtc_ms
cancel_motion motion_id, node_name
cancel_all node_name
start_crossfade start_fade fields + partner_motion_id, partner_osc_path, partner_start_value, partner_end_value

src/signal/parseFadeOscCommand.h:

namespace gme::signal {

ParseResult parseFadeOscCommand(const char* path,
                                const char* types,
                                lo_arg** argv,
                                int argc,
                                std::string_view this_node_name,
                                FadeCommand* out_cmd);

} // namespace gme::signal

OSC address → type-tag:

Address Type tag
/gradient/start_fade sssisffhiss
/gradient/cancel_motion ss
/gradient/cancel_all s

src/signal/LockFreeQueue.h:

template <typename T, std::size_t N>
class LockFreeQueue {
public:
    LockFreeQueue() noexcept = default;

    bool         push(T&& item) noexcept;   // drop-oldest on full; false signals drop
    bool         pop(T& out) noexcept;
    std::size_t  size() const noexcept;     // advisory only
    bool         empty() const noexcept;    // advisory only
    static constexpr std::size_t capacity() noexcept { return N; }
};

Production instantiation: LockFreeQueue<gme::signal::FadeCommand, 64>. Usable capacity is N - 1.


gme::osc

src/osc/OscSender.h:

namespace gme::osc {

int        sendFloat(lo_address target, const char* path, float value) noexcept;
lo_address makeAddress(const std::string& host, int port) noexcept;

} // namespace gme::osc

Caller owns the lo_address returned by makeAddress and must call lo_address_free on destruction. In production, FadeMotion's destructor does this.


gme::engine

src/engine/GradientEngine.h:

namespace gme::engine {

struct GradientEngineConfig {
    std::string midiPort;
    int         oscPort;
    std::string nodeName;
};

class GradientEngine {
public:
    GradientEngine();
    ~GradientEngine();   // calls shutdown() if still running

    bool initialize(const GradientEngineConfig& config);
    void shutdown();     // idempotent
};

} // namespace gme::engine

OscServer is forward-declared in this header; the destructor body lives in GradientEngine.cpp, which is compiled into the daemon binary rather than the library.


gme::daemon::comms

daemon/comms/OscServer.h:

namespace gme::daemon::comms {

class OscServer {
public:
    OscServer(int port,
              std::string node_name,
              gme::signal::LockFreeQueue<gme::signal::FadeCommand, 64>* out_queue);
    ~OscServer();

    bool start();   // returns false on bind failure
    void stop();    // idempotent
    int  getPort() const noexcept;
};

} // namespace gme::daemon::comms

PIMPL pattern — the liblo headers are not exposed to consumers of this header. Binds 127.0.0.1 only.


Daemon-layer classes (no namespace)

daemon/GradientEngineApplication.h:

class GradientEngineApplication {
public:
    GradientEngineApplication();
    ~GradientEngineApplication();

    int  initialize(int argc, char** argv);   // 0 = run; <0 = clean exit; >0 = error
    int  run();                                // blocks until SIGTERM/SIGINT
    void shutdown();
};

daemon/config/ConfigurationManager.h:

class ConfigurationManager {
public:
    ConfigurationManager();
    ~ConfigurationManager() = default;

    int parseArgs(int argc, char** argv);   // 0 = ok, -1 = help/version, >0 = error

    const std::string& getMidiPort()         const;
    const std::string& getLogLevel()         const;
    const std::string& getConfPath()         const;
    int                getGradientOscPort()  const;
    const std::string& getNodeName()         const;
};

CLI flags and resolution order are documented in the header's Doxygen block. OSC-port priority: --osc-portCUEMS_GRADIENT_OSC_PORTsettings.xml <gradient_osc_port> → compile-time default 7100.