#pragma once // play_seconds — the STORED, wall-clock-SECONDS value layer the instrument edits and // serializes: PlaySeconds and the four stage-time structs it composes. Header-only, and split // from sample_map so a consumer that only edits these values (the editor's deck binding) does // not link the bank model and the WAV codec to reach one value struct. `resolvePlay`, which // turns them into the engine's frame domain, stays in sample_map with the rest of the mapping. #include "core/instrument/engine/play_params.h" // PlayMode / TriggerParams / SplineEnv / … namespace reasampler::instrument::map { using instrument::engine::VelocityCurve; // Daniel's standing ruling: no hardcoded sample rate anywhere in the program. The // instrument stores/edits wall-clock performance times (AHDSR A/H/D/R, pitch-env A/D) as // SECONDS, rate-free; the engine receives FRAMES resolved from the LIVE sample rate at // build. Quantities anchored to the source file's timeline (start point, loop points, // Trigger %-length + fades) stay in source frames/fractions, carried through unchanged // (TriggerParams reused verbatim). // // The stored AHDSR times (seconds). sustainLevel is dimensionless (0..1), not a time; the // three curve exponents are dimensionless too (curve_law.h owns their domain). struct AdsrSeconds { double attackSeconds = 0.003; // tier-0 default double holdSeconds = 0.0; double decaySeconds = 0.0; double sustainLevel = 1.0; double releaseSeconds = 0.060; // tier-0 default double attackCurve = util::kCurveNeutral; double decayCurve = util::kCurveNeutral; double releaseCurve = util::kCurveNeutral; }; // The stored sustain-less AHD: wall-clock stage times in SECONDS, Hold as a FRACTION of the // span left after them (AhdParams owns why a fraction, not a time). struct AhdSeconds { double attackSeconds = 0.0; double decaySeconds = 0.0; double holdFraction = 1.0; double attackCurve = util::kCurveNeutral; double decayCurve = util::kCurveNeutral; }; // The stored AHD pitch envelope. enabled + peakSemitones are dimensionless. The hold fraction // defaults to 0 so an instance predating the stage plays as its attack-decay predecessor did. struct PitchEnvSeconds { bool enabled = false; double peakSemitones = 0.0; // signed depth at the peak AhdSeconds shape{0.0, 0.0, /*holdFraction=*/0.0, util::kCurveNeutral, util::kCurveNeutral}; }; // The stored mirror of the engine's FilterParams (play_params.h, which owns what each field // MEANS). Only the envelope differs between the two: the control positions and depths are // rate-free already, so this block is a seconds/frames split of one field, not of the whole // struct. The env default is a flat unity, so `enabled` is the only thing standing between a // loaded blob and the pre-filter sound. struct FilterSeconds { bool enabled = false; engine::filter::FilterSettings settings; double modAmount = 0.0; double velAmount = 0.0; double keyTrack = 0.0; AdsrSeconds env{0.0, 0.0, 0.0, 1.0, 0.0}; // Gate AhdSeconds trigEnv; // Trigger VelocityCurve velocityCurve = VelocityCurve::zero(); }; // The stored play bundle: wall-clock times in SECONDS, source-timeline quantities in // frames/fractions (TriggerParams). Instrument-owned, serialized, editor-facing — distinct // from the engine-facing PlayParams (frames). struct PlaySeconds { PlayMode playMode = PlayMode::Gate; AdsrSeconds adsr; // Gate amp: AHDSR (seconds) TriggerParams trigger; // Trigger play span (%-length) AhdSeconds trigAhd; // Trigger amp: AHD (seconds + fraction) PitchEngine pitchEngine = kDefaultPitchEngine; // product default: Preserve PitchEnvSeconds pitchEnv; // AHD pitch modulation, off by default VelocityCurve pitchVelocityCurve = VelocityCurve::zero(); // velocity -> pitch, off by default FilterSeconds filter; // per-voice filter, off by default // The three drawn contours, in the same slots the engine bundle carries them (play_params.h // owns why they sit beside the envelopes rather than inside them). Normalized over the // sample's own length, so resolvePlay needs no rate for them. SplineEnv ampSpline; SplineEnv pitchSpline; SplineEnv filterSpline; }; } // namespace reasampler::instrument::map