#pragma once // card_meta — pure formatting for the L7 decorative card metadata overlay. Each bank // card overlays capture length as bars.beats.subdivisions (bottom-LEFT, musical) and // seconds.milliseconds (bottom-RIGHT, wall-clock). Both read-outs are DECORATIVE and // non-interactive; the bank_panel draws them via the L1 kit. The formatting itself is // pure string work over the sample's stamped tempo + meter + length, so it is // unit-tested outside the DAW (CLAUDE.md §load-bearing split). // // PURE MODULE: NO REAPER types, NO SWELL, NO LICE, NO vendor/ includes. Standard // library only. Mirror of tooltip's prefix-strip helper. #include namespace reasampler { // The musical length inputs, taken straight off a Sample (L7 F1 capture-time stamp): // lengthSeconds — captured length in wall-clock seconds (>= 0). // tempoBpm — project tempo (BPM) at capture (Sample.captureTempo); 0 = unknown. // timeSigNum — meter numerator at capture (Sample.captureTimeSigNum); 0 = unstamped. // timeSigDenom — meter denominator at capture (Sample.captureTimeSigDenom); 0 = unstamped. struct MusicalLength { double lengthSeconds = 0.0; double tempoBpm = 0.0; int timeSigNum = 0; int timeSigDenom = 0; }; // bars.beats.subdivisions from a capture-time tempo + meter stamp (musical read-out). // // Derivation: one quarter-note lasts 60 / tempo seconds; a beat in this meter lasts // (4 / timeSigDenom) quarter-notes; a bar holds timeSigNum beats. From lengthSeconds we // get total beats, split into whole bars (÷ timeSigNum) + whole leftover beats + a // subdivision remainder scaled to 1..N of the next beat. The output is 1-BASED and // zero-padded to two subdivision digits: "1.1.00" is exactly one bar-start (a // zero-length or bar-aligned capture), "2.3.50" is 1 bar + 2 beats + half a beat. // // Contract / edge cases (all tested): // * UNSTAMPED meter (timeSigNum <= 0 || timeSigDenom <= 0) OR unknown tempo // (tempoBpm <= 0): returns "" — no musical read-out is derivable (the caller keeps // the s.ms read-out). This is the pre-L7-sample fallback (blank musical read-out). // * zero length: "1.1.00" (bar 1, beat 1, no subdivision) — the musical origin. // * exact bar boundary: the beat rolls to 1 and the bar increments (never "1.5.00" // in 4/4 — that reads as "2.1.00"). // * long captures: bars grow without cap ("129.1.00" is fine). // The subdivision is 0..99 (hundredths of a beat), floored — a display quantum, not a // tick-accurate PPQ (the model refuses to invent PPQ; this is a decorative read-out). std::string formatBarsBeats(const MusicalLength& m); // seconds.milliseconds from a wall-clock length (always derivable, meter-independent). // * "S.mmm" — integer seconds, a dot, zero-padded 3-digit milliseconds (rounded to nearest ms). // e.g. 0.0 -> "0.000", 1.5 -> "1.500", 62.037 -> "62.037". // * negative length is clamped to "0.000" (a length is never negative; defensive). std::string formatSecondsMs(double lengthSeconds); } // namespace reasampler