Bake window: derive it from the rate the voice actually reads at, so a dialled Rate or downward Pitch no longer truncates the file
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#include "../src/core/instrument/engine/envelopes.h" // AhdEnvelope (header-only: the codec
|
||||
// links no engine, and this adds none)
|
||||
#include "../src/core/instrument/engine/master_gain.h" // masterGainMaxLinear (the v8 wire cap)
|
||||
#include "../src/core/instrument/engine/time_stretch.h" // the rate bounds the codec clamps to
|
||||
#include "../src/core/util/curve_law.h" // kCurveNeutral (the migration neutral)
|
||||
|
||||
#include <cmath>
|
||||
@@ -1544,10 +1545,12 @@ static void testRateAndPitchOffsetRoundTripAndV15LiftsToUnity() {
|
||||
CHECK(PlaySeconds{}.pitchOffsetSemitones == 0.0);
|
||||
}
|
||||
|
||||
// Neither field has a clamp of its own downstream that could rescue a corrupt blob: the rate
|
||||
// multiplies a read increment (the engine's own clampStretchRate is the one authority on its
|
||||
// RANGE, so the codec only refuses the unusable) and the offset feeds a 2^(x/12) whose result
|
||||
// reaches a per-sample cast. Both degrade to their neutral rather than through.
|
||||
// Corruption degrades to the neutral, and an out-of-RANGE rate resolves through the stretcher's
|
||||
// own clamp rather than surviving unclamped: playback would clamp it anyway, so a stored value
|
||||
// that did not would leave the needle — and the host normalization, once the instrument reports
|
||||
// parameters — disagreeing with what is actually played. The offset has no such downstream clamp
|
||||
// at all (it feeds a 2^(x/12) that reaches a per-sample cast), so it gets a real range test and
|
||||
// degrades whole.
|
||||
static void testCorruptRateOrOffsetDegradesToTheNeutral() {
|
||||
const double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
const struct { double rate; double offset; double wantRate; double wantOffset; } cases[] = {
|
||||
@@ -1556,6 +1559,12 @@ static void testCorruptRateOrOffsetDegradesToTheNeutral() {
|
||||
{0.0, 3.0, 1.0, 3.0}, // a zero rate would stall the read head
|
||||
{-1.0, 3.0, 1.0, 3.0}, // and a negative one would run it backwards
|
||||
{std::numeric_limits<double>::infinity(), 3.0, 1.0, 3.0},
|
||||
// Finite but out of the stretcher's range — reachable from a downgrade, not corruption.
|
||||
// Clamped to the bound the engine would have played, not left to re-serialize.
|
||||
{10.0, 3.0, instrument::engine::kStretchRateMax, 3.0},
|
||||
{0.01, 3.0, instrument::engine::kStretchRateMin, 3.0},
|
||||
{instrument::engine::kStretchRateMin, 3.0, instrument::engine::kStretchRateMin, 3.0}, // the bounds themselves
|
||||
{instrument::engine::kStretchRateMax, 3.0, instrument::engine::kStretchRateMax, 3.0}, // survive untouched
|
||||
{0.75, 1e9, 0.75, 0.0}, // past the +/-24 st throw
|
||||
{0.75, -1e9, 0.75, 0.0},
|
||||
{0.75, 24.0, 0.75, 24.0}, // the throw itself is IN range
|
||||
|
||||
Reference in New Issue
Block a user