#pragma once // ingest — the S8 "ingest through the bank" shell (EXTENSION side). // // Compiled into the reaper_reasampler MODULE. REAPER-facing (PCM_Source metadata reads, // Media-Explorer query, ext-state assignment write, action registration), so it is // DAW-verified, not unit-tested; the pure serialization it drives lives in // assignment_request (tested in CTest). // // -- The one gesture (CONTEXT.md §Ingest through the bank) -------------------- // // Loading a sample into the sampler is ONE gesture: capture/import-into-bank AND // auto-assign to the active sampler instance. The EXTENSION owns ingest (it has arrange // access, Media-Explorer access, and the drop-target surface on its own panels); the // instrument stays a READ-ONLY bank consumer. Three ingest surfaces: // // 1. Arrange capture -> bank -> assign (a bindable action; reuses the capture path). // 2. Media-Explorer import -> bank -> assign (a bindable action; single-file, pull-on- // action via MediaExplorerGetLastPlayedFileInfo). // 3. Drop-onto-panel -> bank -> assign (an OS file drop on the docked bank_panel HWND; // multi-file: import all, assign the first). // // -- The load-bearing principle (restated) ----------------------------------- // // Ingest NEVER inserts a timeline item. Capture writes a file + an index entry; import // copies a file + adds an index entry; assignment is a bank-index + instance-selection // act, not a placement. Any path here that calls InsertMedia would be a bug. // // -- Import semantics --------------------------------------------------------- // // A Media-Explorer/drop import is a FILE COPY into the project-relative bank folder + // an index add, mirroring how a capture lands (relative-paths-only, hash-dedup). If the // active bank already holds the imported content (by content hash), the import collapses // onto the existing sample and assigns THAT sample's id — no redundant on-disk copy. #include #include // Forward declarations keep this header REAPER-free at its own boundary (the .cpp pulls // the SDK). reaper_plugin_info_t is REAPER's dispatch struct; ReaSamplerSession owns the // book + persist bridge the ingest paths mutate. struct reaper_plugin_info_t; namespace reasampler { class ReaSamplerSession; // Registers the S8 ingest action family (command_id/gaccel per the house contract), // mirror of bankRegisterActions. `session` is the live session the ingest paths mutate // (shared with the capture / bank / Design-View families). The single hookcommand in // main.cpp routes fired ids here via ingestHandleCommand. void ingestRegisterActions(reaper_plugin_info_t* rec, ReaSamplerSession* session); // Routes a fired command id to its ingest action. Returns true iff it was one of ours // (claim-only, per the hookcommand contract); false otherwise so the hook keeps looking. bool ingestHandleCommand(int command); // Mirror-unregisters the ingest action family on unload (the '-'-prefixed strings). void ingestUnregisterActions(reaper_plugin_info_t* rec); // Write the S8 assignment request for a just-ingested sample: "the active sampler // instance should now play (bankId, sampleId)." Encodes the pure assignment_request value // (with a fresh monotonic generation stamp) and routes it to ext state via the session. // Called by EVERY ingest surface after the sample lands in the bank — the arrange // capture+assign action (main.cpp, alongside the capture machinery it reuses), the ME // import action, and the drop path. A no-op-safe write: if there is no saved/active // project the request is silently dropped (nothing to signal into), matching the // book/manifest quiet-persist idiom. `sampleId` empty -> no write (nothing to assign). void ingestAssignActiveInstance(const std::string& bankId, const std::string& sampleId); // Ingest OS-dropped files onto a ReaSampler surface (S8 drop path). Called by the // bank_panel's WM_DROPFILES handler with the dropped file paths (absolute, OS-native). // Imports EVERY file into the active bank (copy + index add, hash-dedup) and assigns the // FIRST successfully-imported sample to the active instance. A no-op on an empty list or // an unsaved/no-active project (nothing to import into). Reports outcomes to the console. void ingestDroppedFiles(const std::vector& absolutePaths); } // namespace reasampler