feat: add M6 insert — place selected bank sample at edit cursor

InsertMedia-driven placement reading the bank panel selection, undo-wrapped.
Native length by default; conform-to-tempo is an explicit second action, never
a silent stretch. Pure mode-bit logic (insert_plan) unit-tested.
This commit is contained in:
2026-07-23 05:23:37 -04:00
parent 60add4afc7
commit 172d5902c8
9 changed files with 557 additions and 5 deletions
+59
View File
@@ -0,0 +1,59 @@
#pragma once
// insert — placement of bank samples into the arrange (M6). REAPER-facing shell:
// it reads the bank_panel's current selection, resolves each selected sample's
// file, and drops it into the arrange at the edit cursor via InsertMedia, wrapped
// in an undo block.
//
// THE INTENDED PLACEMENT PATH (CONTEXT.md §load-bearing principle): capture NEVER
// auto-inserts; `insert` is the deliberate, user-invoked placement act, so it IS
// allowed and expected to add items to the arrange. It must only ever run from its
// own action — never from a capture path.
//
// Non-destructive to the bank: insert references the bank file (adds an arrange
// item pointing at it); it never modifies the bank, the bank files, or ext state.
// No SILENT time-stretch: conform-to-tempo is an explicit opt-in on the request,
// defaulting OFF (native length). See insert_plan for the mode-bit computation.
//
// The header is SDK-free: all REAPER API use lives in insert.cpp. The pure
// mode-bit arithmetic lives in insert_plan (unit-tested outside the DAW).
#include "insert_plan.h"
namespace reasampler {
class ReaSamplerSession;
// What one insert action does. Carries the InsertMedia options (target track +
// tempo-conform choice) so the two action variants (native-length vs
// conform-to-tempo) differ only by this struct — no divergent code paths.
struct InsertRequest {
InsertOptions options; // defaults: new track, no conform, native length
};
// The outcome of an insert action, for the caller to log to the console.
enum class InsertStatus {
Ok, // one or more samples inserted
NoSelection, // the panel had no selection — a no-op (not an error)
NoProject, // no saved project, so no resolvable bank dir — no-op
NothingResolved, // a selection existed but no sample resolved to a file
};
struct InsertResult {
InsertStatus status = InsertStatus::NoSelection;
int inserted = 0; // how many samples were actually placed
int skipped = 0; // selected-but-unresolvable/unreadable samples skipped
};
// Runs the insert: reads bank_panel's selection, resolves each sample against the
// current project dir, and inserts them AT THE EDIT CURSOR in bank order, advancing
// the cursor so multiple samples lay end-to-end. The whole placement is wrapped in
// a single Undo_BeginBlock2 / Undo_EndBlock2 so one undo removes the entire insert.
// `session` supplies the live bank the selected ids resolve against.
//
// Multi-select behavior: each selected sample is inserted sequentially at the
// then-current edit cursor; InsertMedia advances the cursor to the end of the
// inserted media, so N samples lay contiguously left-to-right. Single-select is the
// N==1 case of the same path.
InsertResult runInsert(ReaSamplerSession* session, const InsertRequest& request);
} // namespace reasampler