2536b4bedb
Wire both insert actions to CurrentTrack (InsertMedia base 0). Per selected track: SetOnlyTrackSelected, reset cursor to snapshot, InsertMedia; then restore original selection + cursor. No-op when no track selected. Corrected stale extraflags / new-track / preservePitch comments.
76 lines
3.7 KiB
C++
76 lines
3.7 KiB
C++
#pragma once
|
|
// insert_plan — the REAPER-free logic behind the `insert` shell (M6): computing
|
|
// the InsertMedia `mode` bitmask from a small options struct.
|
|
//
|
|
// PURE MODULE (CLAUDE.md §load-bearing split): NO REAPER types, NO SWELL, NO
|
|
// vendor/ includes. Standard library only. The one genuinely testable-outside-DAW
|
|
// piece of insert is the mode-bit arithmetic — the InsertMedia bitfield is easy to
|
|
// get wrong and its bits are load-bearing for the "no silent time-stretch"
|
|
// invariant, so it is factored here and unit-tested. The REAPER-bound placement
|
|
// (InsertMedia call, edit-cursor movement, undo block) lives in insert.cpp and is
|
|
// DAW-verified.
|
|
//
|
|
// The bit meanings below are transcribed VERBATIM from the authoritative header
|
|
// doc-comment (vendor/reaper-sdk/sdk/reaper_plugin_functions.h, InsertMedia):
|
|
// mode: 0=add to current track, 1=add new track, 3=add to selected items as
|
|
// takes, &4=stretch/loop to fit time sel, &8=try to match tempo 1x,
|
|
// &16=try to match tempo 0.5x, &32=try to match tempo 2x,
|
|
// &64=don't preserve pitch when matching tempo, ...
|
|
// We intentionally use only the base target (0/1) and the tempo-conform bits
|
|
// (&8/&16/&32/&64). We NEVER set &4 (stretch/loop to fit time selection) — that is
|
|
// the silent-time-stretch path the tool forbids (CONTEXT.md §Non-goals).
|
|
|
|
#include <cstdint>
|
|
|
|
namespace reasampler {
|
|
|
|
// Where InsertMedia drops the item. Maps to the low bits of `mode` (mode&3).
|
|
// We expose only the two placement targets M6 needs; "add as takes" (3) is a
|
|
// later concern (YAGNI). Both insert AT THE EDIT CURSOR — that is REAPER's
|
|
// convention for base modes 0/1 (the header names no explicit edit-cursor bit;
|
|
// see the flagged runtime assumption in insert.cpp).
|
|
enum class InsertTarget {
|
|
NewTrack, // mode base 1: add a new track for the item
|
|
CurrentTrack, // mode base 0: add to the current/selected track
|
|
};
|
|
|
|
// Tempo-conform choice. Default is None: insert at the file's native length with
|
|
// NO stretching (the precision-preserving default). The three ratios are the
|
|
// explicit opt-in "try to match project tempo" paths — never applied silently.
|
|
// Ratio1x is the ordinary "conform to tempo"; Half/Double are the octave-shifted
|
|
// variants REAPER exposes for half/double-time material.
|
|
enum class TempoConform {
|
|
None, // no tempo bits set: native length, no stretch (default)
|
|
Ratio1x, // &8: try to match tempo 1x
|
|
RatioHalf,// &16: try to match tempo 0.5x
|
|
RatioDouble,// &32: try to match tempo 2x
|
|
};
|
|
|
|
// Options that shape one InsertMedia call. Defaults encode the intended path:
|
|
// current track (user's selection), no conform, pitch preserved.
|
|
struct InsertOptions {
|
|
InsertTarget target = InsertTarget::CurrentTrack;
|
|
TempoConform conform = TempoConform::None;
|
|
|
|
// Only meaningful when conform != None. When false, adds &64 ("don't preserve
|
|
// pitch when matching tempo") so a tempo match also shifts pitch (classic
|
|
// varispeed). Default true = preserve pitch across the tempo match. Ignored
|
|
// when conform == None (no tempo bits set, so pitch is moot).
|
|
bool preservePitch = true;
|
|
};
|
|
|
|
// Computes the InsertMedia `mode` integer for the given options.
|
|
//
|
|
// Guarantees enforced here (and asserted in tests):
|
|
// * The &4 stretch-to-time-selection bit is NEVER set (no silent stretch).
|
|
// * When conform == None, NONE of the tempo bits (&8/&16/&32/&64) are set — the
|
|
// item lands at native length.
|
|
// * Exactly one base target bit pattern is used (0 or 1), never 3.
|
|
int computeInsertMode(const InsertOptions& opts);
|
|
|
|
// The forbidden stretch bit, exposed so a test can assert it is never present in
|
|
// any computed mode (the "no silent time-stretch" invariant, made checkable).
|
|
inline constexpr int kStretchToTimeSelBit = 4;
|
|
|
|
} // namespace reasampler
|