// embed_strip.h — PURE layout + hit-test for the S6 embedded TCP/MCP strip. NO VST3, // NO REAPER, NO SWELL/LICE types at the boundary. The mirror of editor_geometry / // mode_switch: the fiddly rectangle math for the compact inline keymap/level strip lives // here so it is unit-tested outside the DAW, while the embed shell (reasampler_embed.cpp) // marshals REAPER's embed messages (paint bitmap + mouse coords) into these functions. // // The strip is a single compact band REAPER draws inline in the track/mixer control panel // (context TCP or MCP) via the Cockos embedded-UI surface. It shows: // * the zone layout — each performance zone as a horizontal segment across the keyboard // span (MIDI 0..127 mapped to the strip width), so the keymap reads at a glance; and // * a thin level band at the bottom — a 0..1 activity indicator the shell fills. // Interaction is zone SELECTION at most (S6 constraint: no new editing semantics) — a // click maps to the zone whose key range covers that point, or -1. // // It reuses the same Rect + contains() as editor_geometry (the strip and the editor share // one geometry idiom), so this header depends on editor_geometry.h rather than redefining // a second rectangle type. #pragma once #include "editor_geometry.h" // Rect, contains — one shared geometry idiom namespace reasampler::vst { // The full MIDI key span the strip maps across its width. 128 keys (0..127); the strip's // horizontal axis is this range, so a zone [lowNote, highNote] becomes a sub-rectangle. inline constexpr int kEmbedKeyCount = 128; // Fixed metrics for the strip, exposed so the shell and tests agree. inline constexpr int kEmbedLevelBandHeight = 4; // the bottom activity band (px) inline constexpr int kEmbedKeymapMinHeight = 6; // keymap area collapses no smaller // One zone rendered on the strip: its inclusive MIDI key range. This is the minimal // projection of a PerformanceZone the strip needs (it does not carry sample ids or PCM — // the shell resolves labels; the strip only lays out ranges). lowNote/highNote are // expected in [0,127] with low <= high, but the layout clamps defensively so a malformed // zone never yields an out-of-strip rect. struct EmbedZone { int lowNote = 0; int highNote = 127; }; // The strip's regions, derived from the (w x h) embed area REAPER reports. Both clamp to // the area so a degenerate (tiny) size never yields a region spilling outside the surface. struct EmbedLayout { Rect keymap; // top: the zone-segment band (the compact keymap) Rect levelBand; // bottom: the thin level/activity indicator }; // Divide a (w x h) embed area into the strip's regions. Pure: same inputs -> same layout. // The level band takes a fixed height at the bottom (clamped so it never exceeds the area // or starves the keymap below kEmbedKeymapMinHeight); the keymap takes the rest. A zero or // negative size yields empty rects (no inversion). EmbedLayout layoutEmbed(int w, int h); // The horizontal sub-rectangle of the keymap band for a zone spanning [lowNote, highNote] // (inclusive). The 128-key span maps linearly across keymap.width(); the returned rect // spans the half-open pixel range [x(lowNote), x(highNote+1)) so adjacent zones (e.g. // 0..59 and 60..127) tile without a gap or overlap. Notes are clamped to [0,127] and low // is clamped to <= high, so a malformed zone yields an in-band (possibly zero-width) rect, // never an inverted one. Pure. Rect zoneSegmentRect(const EmbedLayout& layout, int lowNote, int highNote); // The zone a click at (x, y) lands on, given the zones in draw order, or -1 for a click // outside the keymap band or on a key not covered by any zone. When zones overlap on a // key, the FIRST covering zone in order wins — mirroring the sampler core's first-match // Keymap::resolve and the editor's zone order, so selection agrees with playback. Pure. int zoneAtPoint(const EmbedLayout& layout, const EmbedZone* zones, int zoneCount, int x, int y); // The filled portion of the level band for a 0..1 level. Clamps level to [0,1]; the // returned rect is the left sub-rectangle of levelBand whose width is level * band width // (rounded down). level <= 0 -> empty rect; level >= 1 -> the whole band. Pure. Rect levelFillRect(const EmbedLayout& layout, double level); } // namespace reasampler::vst