fix: restore waveform symmetry about the midline, cut deck_values' link to the bank model, and unit-test the column arithmetic
The waveform column's vertical extents move to pure component_geometry so the shared primitive stops being untested; PlaySeconds hoists into a header-only play_seconds target.
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "core/model/bank_book.h" // BankBook::deserialize (shared bank JSON parse)
|
||||
#include "core/instrument/engine/play_params.h" // SampleData, SampleLoop, PlayParams
|
||||
#include "core/instrument/map/play_seconds.h" // PlaySeconds (the stored seconds value layer)
|
||||
#include "core/capture/wav_codec.h" // parseWavLayout, extractFloatFrames (shared WAV parse)
|
||||
|
||||
namespace reasampler::instrument::map {
|
||||
@@ -134,81 +135,7 @@ std::vector<AudioSample> downmixToMono(const std::vector<AudioSample>& interleav
|
||||
std::vector<AudioSample> extractChannel(const std::vector<AudioSample>& interleaved,
|
||||
int channelCount, int which);
|
||||
|
||||
// --- Stored (wall-clock SECONDS) play params ----------------------------------
|
||||
//
|
||||
// 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;
|
||||
};
|
||||
// --- Seconds -> frames --------------------------------------------------------
|
||||
|
||||
// Resolve a stored seconds bundle to the engine's frame-domain PlayParams against a live
|
||||
// sample rate (frames = round(seconds * rate)). Source-timeline fields carry through
|
||||
|
||||
Reference in New Issue
Block a user