Skip to content

Instantly share code, notes, and snippets.

@cfillion
Last active May 20, 2024 07:19
Show Gist options
  • Save cfillion/f32b04e75e84e03cc463abb1eda41400 to your computer and use it in GitHub Desktop.
Save cfillion/f32b04e75e84e03cc463abb1eda41400 to your computer and use it in GitHub Desktop.
Bare-bone REAPER extension (prints Hello World! using the API)
// Bare-bone REAPER extension
//
// 1. Grab reaper_plugin.h from https://github.com/justinfrankel/reaper-sdk/raw/main/sdk/reaper_plugin.h
// 2. Grab reaper_plugin_functions.h by running the REAPER action "[developer] Write C++ API functions header"
// 3. Grab WDL: git clone https://github.com/justinfrankel/WDL.git
// 4. Build then copy or link the binary file into <REAPER resource directory>/UserPlugins
//
// Linux
// =====
//
// c++ -fPIC -O2 -std=c++14 -IWDL/WDL -shared reaper_barebone.cpp -o reaper_barebone.so
//
// macOS
// =====
//
// c++ -fPIC -O2 -std=c++14 -IWDL/WDL -dynamiclib reaper_barebone.cpp -o reaper_barebone.dylib
//
// Windows
// =======
//
// (Use the VS Command Prompt matching your REAPER architecture, eg. x64 to use the 64-bit compiler)
// cl /nologo /O2 /Z7 /Zo /DUNICODE reaper_barebone.cpp /link /DEBUG /OPT:REF /PDBALTPATH:%_PDB% /DLL /OUT:reaper_barebone.dll
#define REAPERAPI_IMPLEMENT
#include "reaper_plugin_functions.h"
#include <cstdio>
extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
REAPER_PLUGIN_HINSTANCE instance, reaper_plugin_info_t *rec)
{
if(!rec) {
// cleanup code here
return 0;
}
if(rec->caller_version != REAPER_PLUGIN_VERSION)
return 0;
// see also https://gist.github.com/cfillion/350356a62c61a1a2640024f8dc6c6770
ShowConsoleMsg = (decltype(ShowConsoleMsg))rec->GetFunc("ShowConsoleMsg");
if(!ShowConsoleMsg) {
fprintf(stderr, "[reaper_barebone] Unable to import ShowConsoleMsg\n");
return 0;
}
// initialization code here
ShowConsoleMsg("Hello World!\n");
return 1;
}
// zig build-lib -dynamic -O ReleaseFast -femit-bin=reaper_barebone.so reaper_barebone.zig
const std = @import("std");
const reaper = struct { // @import("reaper");
pub const PLUGIN_VERSION = 0x20E;
pub const HINSTANCE = *opaque {};
pub const HWND = *opaque {};
pub const plugin_info_t = extern struct {
caller_version: c_int,
hwnd_main: HWND,
register: ?*fn(name: [*:0]const u8, infostruct: *anyopaque) callconv(.C) c_int,
getFunc: ?*fn(name: [*:0]const u8) callconv(.C) ?*anyopaque,
};
pub fn init(rec: *plugin_info_t) bool {
if(rec.caller_version != PLUGIN_VERSION) {
std.debug.print("expected REAPER API version {x}, got {x}\n",
.{ PLUGIN_VERSION, rec.caller_version });
return false;
}
const getFunc = rec.getFunc.?;
inline for(@typeInfo(@This()).Struct.decls) |decl| {
comptime var decl_type = @typeInfo(@TypeOf(@field(@This(), decl.name)));
const is_optional = decl_type == .Optional;
if(is_optional)
decl_type = @typeInfo(decl_type.Optional.child);
if(decl_type != .Pointer or @typeInfo(decl_type.Pointer.child) != .Fn)
continue;
if(getFunc(decl.name)) |func|
@field(@This(), decl.name) = @ptrCast(func)
else if(is_optional)
@field(@This(), decl.name) = null
else {
std.debug.print("unable to import the API function '{s}'\n", .{ decl.name });
return false;
}
}
return true;
}
pub var ShowConsoleMsg: *fn(str: [*:0]const u8) callconv(.C) void = undefined;
};
export fn ReaperPluginEntry(instance: reaper.HINSTANCE, rec: ?*reaper.plugin_info_t) c_int {
_ = instance;
if(rec == null)
return 0 // cleanup here
else if(!reaper.init(rec.?))
return 0;
reaper.ShowConsoleMsg("Hello, Zig!\n");
return 1;
}
@jbloit
Copy link

jbloit commented Oct 6, 2020

Yes, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment