♫ Mcaster1AMP Plugin SDK
Stable C ABI plugin interface for extending Mcaster1AMP with DSP processing, custom decoders, crossfader algorithms, and visualization. API version 2 — binary-compatible across compilers and C++ runtimes.
Overview
The Mcaster1AMP Plugin SDK provides a stable C ABI for extending the player without recompiling the host. Plugins are loaded via dlopen on macOS/Linux and LoadLibrary on Windows. Because the interface is pure C, plugins can be written in any language that can export C functions and remain binary-compatible across C++ compiler versions and standard library implementations.
The constant AMP_PLUGIN_API_VERSION is bumped whenever the ABI changes in an incompatible way. The host rejects any plugin whose api_version field does not match exactly.
Plugin Types
| Constant | Value | Description |
|---|---|---|
AMP_PLUGIN_GENERAL | 0 | Misc utility — no specific audio callback required. Use for settings panels, metadata scrapers, broadcast integrations. |
AMP_PLUGIN_DSP | 1 | In-place audio processing. Receives interleaved float PCM, modifies in-place. Realtime-safe callback required. |
AMP_PLUGIN_INPUT | 2 | File decoder. Reports which extensions it handles; host delegates decode to it when a matching file is opened. |
AMP_PLUGIN_VISUALIZATION | 3 | Receives a copy of the output PCM stream for spectrum/waveform display. Must not modify the buffer. |
AMP_PLUGIN_CROSSFADER | 4 | Blends Deck A and Deck B into a single output according to a fader position and curve algorithm. Replaces the built-in crossfader when loaded. |
Required Exports
Every plugin, regardless of type, must export these three C functions. If any is missing, the host will refuse to load the plugin and log an error.
/* Return a pointer to a static AmpPluginInfo struct.
* Must remain valid for the lifetime of the plugin. */
const AmpPluginInfo* amp_plugin_info(void);
/* Initialize the plugin. ctx is valid only during this call —
* copy any needed fields (sample_rate, data_dir, log) to plugin-static storage.
* Return 0 on success, non-zero on fatal error (host will dlclose). */
int amp_plugin_init(const AmpHostContext* ctx);
/* Shutdown: flush state, write config, free resources.
* Called on the GUI thread before dlclose. */
void amp_plugin_shutdown(void);
-fvisibility=hidden. Mark each exported function with __attribute__((visibility("default"))) or use a .def file on Windows to ensure dlsym/GetProcAddress can find them.Loading Sequence
The host follows this sequence when loading a plugin:
dlopen(path, RTLD_LAZY | RTLD_LOCAL)— load the shared librarydlsym("amp_plugin_info")— fetch and call; readapi_versionandtype- Reject if
api_version != AMP_PLUGIN_API_VERSION - Probe for type-specific symbols; log a warning for missing optional ones
dlsym("amp_plugin_init")— call with a populatedAmpHostContext- If init returns non-zero:
dlsym("amp_plugin_shutdown"), thendlclose - Register plugin in active chain / crossfader slot
AmpPluginInfo Struct
Returned by amp_plugin_info(). Must be a static constant — the host may cache the pointer and dereference it at any time while the plugin is loaded.
typedef struct {
const char* name; /* "MC1 Crossfader" */
const char* author; /* "Mcaster1 Audio" */
const char* description; /* Human-readable one-line description */
const char* version; /* Semantic version string "1.0.0" */
int type; /* AMP_PLUGIN_* constant */
int api_version; /* Must equal AMP_PLUGIN_API_VERSION (2) */
} AmpPluginInfo;
| Field | Type | Notes |
|---|---|---|
name | const char* | Short display name shown in Plugin Manager. ASCII, max 64 chars recommended. |
author | const char* | Author or organization name. Shown in plugin detail view. |
description | const char* | One-sentence description of what the plugin does. |
version | const char* | SemVer string (e.g., "1.0.0"). Parsed for update checking in future SDK versions. |
type | int | One of the AMP_PLUGIN_* constants. Determines which additional symbols the host looks for. |
api_version | int | Must be exactly AMP_PLUGIN_API_VERSION (currently 2). Plugin rejected if mismatched. |
AmpHostContext Struct
Passed to amp_plugin_init(). The pointer is only valid during the init call — copy any fields you need into plugin-static storage before returning.
typedef void (*AmpLogCallback)(int level, const char* message);
/* Log levels: 1=CRITICAL 2=ERROR 3=WARNING 4=INFO 5=DEBUG */
typedef struct {
int sample_rate; /* Host audio sample rate (e.g. 44100, 48000) */
int channels; /* Host channel count (1=mono, 2=stereo) */
int buffer_size; /* Frames per dsp_process/blend call */
const char* app_version; /* Host version string, e.g. "1.0.0" */
const char* plugin_data_dir; /* Writable per-plugin config directory */
AmpLogCallback log; /* Host logging callback (thread-safe) */
} AmpHostContext;
| Field | Notes |
|---|---|
sample_rate | Matches the PortAudio stream sample rate. If your plugin does internal FFT, size your buffers here. |
channels | Almost always 2 (stereo interleaved). Your DSP callback must handle channels == 1 too for future mono support. |
buffer_size | The frames parameter passed to DSP/blend callbacks. Allocate scratch buffers sized to this during init, not during the audio callback. |
app_version | Useful for gating features only available on newer host versions. |
plugin_data_dir | Guaranteed to exist and be writable. Path example: ~/Library/Application Support/Mcaster1AMP/plugins/dsp_mc1crossfader/. Store YAML config, presets, and caches here. Never hardcode paths. |
log | Thread-safe. Call from any thread including the realtime audio thread (but minimize calls on the audio thread to avoid latency spikes). |
Storing Context Fields
/* Plugin-static storage — copy during amp_plugin_init() */
static int g_sample_rate = 44100;
static int g_channels = 2;
static std::string g_data_dir;
static AmpLogCallback g_log = nullptr;
int amp_plugin_init(const AmpHostContext* ctx)
{
if (!ctx) return -1;
g_sample_rate = ctx->sample_rate;
g_channels = ctx->channels;
g_data_dir = ctx->plugin_data_dir ? ctx->plugin_data_dir : "/tmp/myplugin";
g_log = ctx->log;
return 0; /* 0 = success */
}
Parameter API SDK v2
The Parameter API lets the host build a generic settings UI for any plugin without requiring a custom Qt widget. All parameter names use snake_case ASCII strings. The API is optional — plugins that provide a full custom UI via amp_plugin_create_ui() may omit it.
Exported Functions
/* Set a named parameter. Plugin may ignore unknown names silently. */
void amp_plugin_set_param(const char* name, double value);
/* Get a named parameter value. Returns 0.0 for unknown names. */
double amp_plugin_get_param(const char* name);
/* Return the number of parameters this plugin exposes. */
int amp_plugin_get_param_count(void);
/* Return a descriptor for parameter at index.
* Pointer must remain valid for the lifetime of the plugin. */
const AmpParamInfo* amp_plugin_get_param_info(int param_index);
AmpParamInfo Struct
typedef struct {
const char* name; /* Internal key: "gain_db", "curve", etc. */
const char* label; /* Display name: "Gain (dB)", "Curve Type" */
const char* unit; /* "dB", "%", "Hz", "" (empty if unitless) */
double min_value;
double max_value;
double default_value;
int is_enum; /* 1 = integer index, show as dropdown */
const char* enum_names; /* Pipe-separated: "Linear|Constant Power|..." */
} AmpParamInfo;
| Field | Notes |
|---|---|
name | The key used in set_param/get_param. Must be stable — host may serialize it to settings files. |
label | Human-readable string for the host's auto-generated UI. |
unit | Appended to the value in the UI. Use "" for dimensionless values. |
min_value / max_value | Inclusive range. Host clamps sliders to these bounds. |
default_value | Used when the host generates a "Reset to Default" action. |
is_enum | If 1, the host renders a dropdown. value is always a non-negative integer index into enum_names. |
enum_names | Pipe (|) separated list of option labels. nullptr when is_enum == 0. Example: "Linear|Constant Power|S-Curve". |
Example: Defining Parameters
static const AmpParamInfo kParams[] = {
{ "gain_db", "Gain", "dB", -24.0, 24.0, 0.0, 0, nullptr },
{ "enabled", "Enabled", "", 0.0, 1.0, 1.0, 1, "Off|On" },
{ "freq_hz", "Frequency", "Hz", 20.0, 20000.0, 1000.0, 0, nullptr },
};
int amp_plugin_get_param_count() { return 3; }
const AmpParamInfo* amp_plugin_get_param_info(int i) {
if (i < 0 || i >= 3) return nullptr;
return &kParams[i];
}
Custom Qt UI API SDK v2
Plugins may provide a fully custom QWidget-based settings panel shown in the Plugin Manager's "Configure" dialog. The host calls amp_plugin_create_ui(), embeds the returned widget, and calls amp_plugin_destroy_ui() when the dialog closes or the plugin is unloaded.
/* Create and return a QWidget* for this plugin's settings panel.
* parent_qwidget: host passes QWidget* cast to void*.
* Return new QWidget* cast to void*, or NULL if no UI available.
* Host takes ownership and will call destroy_ui before unload. */
void* amp_plugin_create_ui(void* parent_qwidget);
/* Destroy the widget returned by create_ui. */
void amp_plugin_destroy_ui(void* ui_widget);
Thread Safety
Both create_ui and destroy_ui are called on the GUI thread. The widget must not touch audio-thread state without an atomics barrier or QMetaObject::invokeMethod(..., Qt::QueuedConnection).
/* Correct pattern: update audio-thread state from widget signal */
QObject::connect(g_widget, &MyWidget::gainChanged,
[](double db) {
g_gain_linear.store(static_cast<float>(std::pow(10.0, db / 20.0)));
});
Plugin Types — Additional Symbols
Type 1 — DSP Plugin (AMP_PLUGIN_DSP)
| Symbol | Required? | Signature |
|---|---|---|
amp_plugin_dsp_process | Required | void(float* samples, int frames, int channels) |
amp_plugin_set_param | Optional | void(const char* name, double value) |
amp_plugin_get_param | Optional | double(const char* name) |
amp_plugin_get_param_count | Optional | int(void) |
amp_plugin_get_param_info | Optional | const AmpParamInfo*(int index) |
amp_plugin_create_ui | Optional | void*(void* parent) |
amp_plugin_destroy_ui | Optional | void(void* widget) |
DSP callbacks are called on the realtime audio thread. REALTIME-SAFE — no heap allocation, no mutex lock, no file I/O, no blocking system call.
Type 2 — Input Plugin (AMP_PLUGIN_INPUT)
| Symbol | Required? | Signature |
|---|---|---|
amp_plugin_input_can_decode | Required | int(const char* ext) — return 1 if this plugin handles the extension |
amp_plugin_input_open | Required | void*(const char* path, int* out_sample_rate, int* out_channels) |
amp_plugin_input_read | Required | int(void* handle, float* buffer, int max_frames) — return frames decoded, 0=EOF, negative=error |
amp_plugin_input_close | Required | void(void* handle) |
Type 3 — Visualization Plugin (AMP_PLUGIN_VISUALIZATION)
| Symbol | Required? | Signature |
|---|---|---|
amp_plugin_vis_push | Required | void(const float* samples, int frames, int channels) — buffer is read-only, do not modify |
Called on the realtime audio thread with a copy of the post-DSP output PCM. Use a non-blocking ring buffer to hand data off to the GUI thread for rendering.
Type 4 — Crossfader Plugin (AMP_PLUGIN_CROSSFADER)
| Symbol | Required? | Signature |
|---|---|---|
amp_plugin_crossfader_blend | Required | void(const float* deck_a, const float* deck_b, float* out, int frames, int channels, float position) |
amp_plugin_set_param | Optional | same as DSP — for curve type, smoothing, etc. |
amp_plugin_create_ui | Optional | Custom fader widget |
position: 0.0 = full Deck A, 0.5 = equal blend, 1.0 = full Deck B. Pass -1.0f to use the last value set via set_param("position", ...).
C ABI Symbol Name Constants
Use these #define constants with dlsym()/GetProcAddress() to avoid typos when probing for optional symbols. They are defined in plugins/plugin_api.h.
/* Required — all plugin types */
#define AMP_SYM_INFO "amp_plugin_info"
#define AMP_SYM_INIT "amp_plugin_init"
#define AMP_SYM_SHUTDOWN "amp_plugin_shutdown"
/* DSP (type 1) */
#define AMP_SYM_DSP_PROCESS "amp_plugin_dsp_process"
/* Parameter API (types 1 and 4) */
#define AMP_SYM_SET_PARAM "amp_plugin_set_param"
#define AMP_SYM_GET_PARAM "amp_plugin_get_param"
#define AMP_SYM_GET_PARAM_COUNT "amp_plugin_get_param_count"
#define AMP_SYM_GET_PARAM_INFO "amp_plugin_get_param_info"
/* Custom Qt UI (shared Qt runtime only) */
#define AMP_SYM_CREATE_UI "amp_plugin_create_ui"
#define AMP_SYM_DESTROY_UI "amp_plugin_destroy_ui"
/* Crossfader (type 4) */
#define AMP_SYM_CROSSFADER_BLEND "amp_plugin_crossfader_blend"
/* Input decoder (type 2) */
#define AMP_SYM_INPUT_CAN "amp_plugin_input_can_decode"
#define AMP_SYM_INPUT_OPEN "amp_plugin_input_open"
#define AMP_SYM_INPUT_READ "amp_plugin_input_read"
#define AMP_SYM_INPUT_CLOSE "amp_plugin_input_close"
/* Visualization (type 3) */
#define AMP_SYM_VIS_PUSH "amp_plugin_vis_push"
Usage in Plugin Host
void* lib = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
auto info_fn = (AmpPluginInfoFn)dlsym(lib, AMP_SYM_INFO);
auto init_fn = (AmpPluginInitFn)dlsym(lib, AMP_SYM_INIT);
/* optional — check before calling */
auto set_p = (AmpPluginSetParamFn)dlsym(lib, AMP_SYM_SET_PARAM);
Building a Plugin
Plugins are shared libraries. Include plugins/plugin_api.h (no other Mcaster1AMP headers are needed for a basic plugin). Link against any libraries your plugin uses. Do not link against libmcaster1amp — all communication goes through the C ABI at runtime.
macOS (dylib)
# Makefile excerpt — macOS ARM64 / x86_64
PLUGIN_NAME = dsp_myplugin
SRCS = dsp_myplugin.cpp
SDK_INCLUDE = /path/to/Mcaster1AMP/src/Mcaster1AMP/plugins
QT6_CFLAGS := $(shell pkg-config --cflags Qt6Widgets)
QT6_LIBS := $(shell pkg-config --libs Qt6Widgets)
YAML_LIBS := $(shell pkg-config --libs yaml-0.1)
CXXFLAGS = -std=c++17 -O2 -fvisibility=hidden \
-I$(SDK_INCLUDE) $(QT6_CFLAGS)
$(PLUGIN_NAME).dylib: $(SRCS)
$(CXX) $(CXXFLAGS) -dynamiclib -install_name @rpath/$@.dylib \
-o $@ $^ $(QT6_LIBS) $(YAML_LIBS)
Linux (shared object)
$(PLUGIN_NAME).so: $(SRCS)
$(CXX) $(CXXFLAGS) -shared -fPIC -o $@ $^ $(QT6_LIBS) $(YAML_LIBS)
Windows (DLL, MinGW)
$(PLUGIN_NAME).dll: $(SRCS)
$(CXX) $(CXXFLAGS) -shared -o $@ $^ $(QT6_LIBS) $(YAML_LIBS) \
-Wl,--out-implib,$(PLUGIN_NAME).lib
Installation Path
Drop the built .dylib / .so / .dll into one of the following directories — the Plugin Manager scans all of them at startup:
- macOS:
~/Library/Application Support/Mcaster1AMP/plugins/ - Linux:
~/.local/share/Mcaster1AMP/plugins/ - Windows:
%APPDATA%\Mcaster1AMP\plugins\ - System-wide macOS:
/Library/Application Support/Mcaster1AMP/plugins/
cp dsp_myplugin.dylib ~/Library/Application\ Support/Mcaster1AMP/plugins/ then relaunch Mcaster1AMP and open Plugins > Plugin Manager.AI Agent Prompts
The following prompts are designed for use with Claude Code or any AI coding agent. Each prompt gives the agent enough context to generate a working plugin without further clarification. Paste them directly into your agent session with the Mcaster1AMP repo open.