From acfc9daabca9d4815cafabdea064082b9f00bc06 Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Tue, 21 Jul 2026 21:18:51 -0400 Subject: [PATCH] Retire MPE scaffold; stand up reaper_reasampler pure-core skeleton Rename CMake project/module to reaper_reasampler, delete mpe_model/mpe_view, add minimal pure bank_model lib + placeholder CTest. main.cpp loads, logs to console, keeps the action-registration seam commented with the forever-stable CEREBELLUM_REASAMPLER_ action-id prefix. --- CMakeLists.txt | 62 +++++++++++++++++++++++++++++ src/bank_model.cpp | 10 +++++ src/bank_model.h | 17 ++++++++ src/main.cpp | 83 +++++++++++++++++++++++++++++++++++++++ tests/test_bank_model.cpp | 24 +++++++++++ 5 files changed, 196 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 src/bank_model.cpp create mode 100644 src/bank_model.h create mode 100644 src/main.cpp create mode 100644 tests/test_bank_model.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..72c2d4f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,62 @@ +cmake_minimum_required(VERSION 3.19) +project(reaper_reasampler LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# --------------------------------------------------------------------------- +# Vendored dependencies — add as git submodules (see README): +# git submodule add https://github.com/justinfrankel/reaper-sdk vendor/reaper-sdk +# git submodule add https://github.com/justinfrankel/WDL vendor/WDL +# --------------------------------------------------------------------------- +set(SDK_INC ${CMAKE_CURRENT_SOURCE_DIR}/vendor/reaper-sdk/sdk) +set(WDL_INC ${CMAKE_CURRENT_SOURCE_DIR}/vendor/WDL/WDL) +set(SWELL ${WDL_INC}/swell) + +# --------------------------------------------------------------------------- +# 1) Pure model library — NO REAPER, NO SWELL. Builds & tests anywhere. +# The sampler's heart: Sample metadata + BankIndex (Milestone 1). +# --------------------------------------------------------------------------- +add_library(bank_model STATIC src/bank_model.cpp) +target_include_directories(bank_model PUBLIC src) + +# --------------------------------------------------------------------------- +# 2) Standalone tests for the model (run without launching REAPER). +# --------------------------------------------------------------------------- +enable_testing() +add_executable(bank_model_tests tests/test_bank_model.cpp) +target_link_libraries(bank_model_tests PRIVATE bank_model) +add_test(NAME bank_model_tests COMMAND bank_model_tests) + +# --------------------------------------------------------------------------- +# 3) The REAPER extension — a loadable module (dlopen'd by REAPER, not linked). +# --------------------------------------------------------------------------- +add_library(reaper_reasampler MODULE + src/main.cpp +) +target_link_libraries(reaper_reasampler PRIVATE bank_model) +target_include_directories(reaper_reasampler PRIVATE ${SDK_INC} ${WDL_INC}) +set_target_properties(reaper_reasampler PROPERTIES PREFIX "" OUTPUT_NAME "reaper_reasampler") + +if(WIN32) + # Native Win32. REAPER provides nothing extra to link. + # Dialog resources return with the docked bank_panel (Milestone 5). + +elseif(APPLE) + # macOS: use REAPER's OWN SWELL at runtime via the modstub. + # Do NOT build full SWELL. SWELL_PROVIDED_BY_APP routes calls to the host. + target_sources(reaper_reasampler PRIVATE ${SWELL}/swell-modstub.mm) + target_compile_definitions(reaper_reasampler PRIVATE SWELL_PROVIDED_BY_APP) + target_link_libraries(reaper_reasampler PRIVATE "-framework AppKit") + set_target_properties(reaper_reasampler PROPERTIES SUFFIX ".dylib") + # Dialog: run SWELL resgen once (see README) and add the generated source. + +else() + # Linux: REAPER's libSwell.so is used at runtime via the generic modstub. + # With SWELL_PROVIDED_BY_APP you can drop pkg-config / -lX11 entirely. + target_sources(reaper_reasampler PRIVATE ${SWELL}/swell-modstub-generic.cpp) + target_compile_definitions(reaper_reasampler PRIVATE SWELL_PROVIDED_BY_APP) + set_target_properties(reaper_reasampler PROPERTIES SUFFIX ".so") + # Dialog: run SWELL resgen once (see README) and add the generated source. +endif() diff --git a/src/bank_model.cpp b/src/bank_model.cpp new file mode 100644 index 0000000..e51b41b --- /dev/null +++ b/src/bank_model.cpp @@ -0,0 +1,10 @@ +#include "bank_model.h" + +namespace reasampler { + +int bankModelVersion() +{ + return 1; +} + +} // namespace reasampler diff --git a/src/bank_model.h b/src/bank_model.h new file mode 100644 index 0000000..02ce4fc --- /dev/null +++ b/src/bank_model.h @@ -0,0 +1,17 @@ +#pragma once +// bank_model — the HEART of ReaSampler, deliberately free of any REAPER type so +// it compiles and unit-tests OUTSIDE the DAW. It owns the per-project sample +// bank: the `Sample` metadata struct and the `BankIndex` (add / remove / query / +// tier moves / dedup-by-hash + JSON round-trip). +// +// Milestone 0 placeholder: this is a minimal compiling stub sufficient to stand +// up the pure static lib and its CTest. The full data model (Sample field set, +// BankIndex operations, JSON) is Milestone 1 — see PLAN.md / CONTEXT.md §Data model. + +namespace reasampler { + +// M0 placeholder — replaced by the real `Sample` / `BankIndex` in Milestone 1. +// Returns the on-disk schema version the bank_model serializes to. +int bankModelVersion(); + +} // namespace reasampler diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..0ed5d46 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,83 @@ +// main.cpp — the SINGLE translation unit that OWNS the REAPER API pointers. +// +// This file is the entire contract between REAPER and the extension: +// * At startup REAPER scans UserPlugins/ for reaper_*.dll|dylib|so and +// dlopen()s each one, then looks up ONE exported symbol: ReaperPluginEntry +// (that name is produced by the REAPER_PLUGIN_ENTRYPOINT macro). +// * REAPER calls it, handing over `rec` — a small dispatch struct. +// - rec->GetFunc(name) resolves any REAPER API function to a pointer +// - rec->Register(what,ptr) plugs OUR callbacks into REAPER +// * REAPERAPI_LoadAPI(rec->GetFunc) walks reaper_plugin_functions.h and +// fills in every global function pointer (ShowConsoleMsg, InsertMedia...). +// +// Exactly ONE .cpp defines REAPERAPI_IMPLEMENT (this one) — that allocates +// storage for those global pointers. Every other .cpp includes +// reaper_plugin_functions.h WITHOUT the define and gets `extern` declarations. + +#define REAPERAPI_IMPLEMENT +#include "reaper_plugin.h" +#include "reaper_plugin_functions.h" + +// Persistent action-id prefix for the ReaSampler action family. +// Every bindable action (capture / insert / slot / verify) mints its command id +// from a string beginning with this prefix, e.g. "CEREBELLUM_REASAMPLER_CAPTURE_MASTER". +// FOREVER-STABLE once shipped: user keybindings key off these strings, so the +// prefix and any minted id must never change after release. +#define REASAMPLER_ACTION_PREFIX "CEREBELLUM_REASAMPLER_" + +// Globals other files reference via `extern`. +REAPER_PLUGIN_HINSTANCE g_hInst = nullptr; // this module's instance handle +reaper_plugin_info_t* g_rec = nullptr; // REAPER's dispatch struct + +// ---- Action registration seam (reusable pattern, no action wired yet) ------ +// The capture/insert/slot action family (PLAN.md M3+) reinstates the pattern +// below. Kept as a commented template so the shape is not re-derived each time. +// +// static int g_cmdCaptureMaster = 0; +// +// // REAPER calls this for EVERY action fired anywhere; claim only your own id, +// // return false otherwise so REAPER keeps looking. +// static bool OnHookCommand(int command, int /*flag*/) +// { +// if (command == 0) return false; +// if (command == g_cmdCaptureMaster) { /* route to capture module */ return true; } +// return false; +// } +// +// Registration, inside the load branch below: +// 1) g_cmdCaptureMaster = +// rec->Register("command_id", (void*)(REASAMPLER_ACTION_PREFIX "CAPTURE_MASTER")); +// 2) static gaccel_register_t sAccel{}; +// sAccel.accel.cmd = g_cmdCaptureMaster; +// sAccel.desc = "ReaSampler: capture master mix"; +// rec->Register("gaccel", (void*)&sAccel); +// 3) rec->Register("hookcommand", (void*)&OnHookCommand); +// On unload (rec == nullptr), mirror-unregister with the same strings prefixed '-'. + +extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT( + REAPER_PLUGIN_HINSTANCE hInstance, reaper_plugin_info_t* rec) +{ + if (!rec) + { + // rec == nullptr => REAPER is UNLOADING us. Mirror-unregister every + // callback here (same strings prefixed with '-') once actions are wired. + g_rec = nullptr; + return 0; + } + + // ABI guard: the struct layout we compiled against must match this REAPER. + if (rec->caller_version != REAPER_PLUGIN_VERSION) + return 0; + + // Resolve every REAPER API function pointer. Returns the number that FAILED + // to load; 0 == success. Non-zero usually means REAPER is older than our SDK. + if (REAPERAPI_LoadAPI(rec->GetFunc) != 0) + return 0; + + g_hInst = hInstance; + g_rec = rec; + + ShowConsoleMsg("ReaSampler loaded.\n"); + + return 1; // success — REAPER keeps us loaded +} diff --git a/tests/test_bank_model.cpp b/tests/test_bank_model.cpp new file mode 100644 index 0000000..f55172a --- /dev/null +++ b/tests/test_bank_model.cpp @@ -0,0 +1,24 @@ +// Standalone tests for reasampler::bank_model — no REAPER, no test framework. +// This is the point of splitting the model out: iterate the hard logic here with +// a fast build/run loop instead of restarting REAPER. +// +// Milestone 0 placeholder: a single assertion proving the pure lib links and +// runs under CTest. The real round-trip / dedup / tier / relative-path tests +// arrive with the Milestone 1 data model (see TODO.md, PLAN.md). + +#include "../src/bank_model.h" +#include + +using namespace reasampler; + +static int g_fail = 0; +#define CHECK(cond) do { if(!(cond)) { \ + std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0) + +int main() +{ + CHECK(bankModelVersion() == 1); + + if (g_fail == 0) std::printf("All tests passed.\n"); + return g_fail ? 1 : 0; +}