Fix cross-rate Preserve splices, re-stamp on host-rate change, name the rate refusal
The splice crossfade now counts output frames and stops before the ring's far end (this also fixes a lap at matched rate). The search strides by the rate ratio. The editor takes its time base from its own decode. Same-rate output is pinned to hashes from before the change.
This commit is contained in:
@@ -104,8 +104,8 @@ enum class PitchEngine { Varispeed, Preserve };
|
||||
inline constexpr PitchEngine kDefaultPitchEngine = PitchEngine::Preserve;
|
||||
|
||||
// OLA window for the Preserve PitchShifter, in ms of SOURCE (preserveWindowFrames); larger =
|
||||
// smoother on big transpositions. Onset latency is zero — start() primes the ring with the first window
|
||||
// of real source, so output frame 0 is source frame 0 regardless of window size.
|
||||
// smoother on big transpositions. Onset latency is zero — start() primes the ring with the first
|
||||
// window of real source, so output frame 0 is source frame 0 regardless of window size.
|
||||
inline constexpr double kPreserveWindowMs = 50.0;
|
||||
|
||||
// AHD pitch-modulation envelope, off by default (enabled=false -> offset always 0 ->
|
||||
@@ -324,15 +324,27 @@ struct SampleData {
|
||||
}
|
||||
};
|
||||
|
||||
// The largest sourceRateRatio a load accepts. The Preserve feed consumes that many times the
|
||||
// stretch rate in source frames per output frame, so the ratio bounds per-frame work on the
|
||||
// audio thread; a header claiming more than this is treated as corrupt (buildSampleData).
|
||||
// The largest sourceRateRatio a load accepts. It is the one per-frame cost the ratio scales: a
|
||||
// Preserve voice writes up to ceil(kStretchRateMax x ratio) source frames into its ring per
|
||||
// output frame (StretchCursor::due). The splice search does NOT scale with it — pitch_shift
|
||||
// strides it to a matched rate's cost. A header claiming more than this is treated as corrupt
|
||||
// (buildSampleData).
|
||||
inline constexpr double kMaxSourceRateRatio = 32.0;
|
||||
|
||||
// THE refusal predicate for that bound: whether a file at `sourceRate` may play in a host
|
||||
// emitting `renderRate` (<= 0: not reported, so the file plays at its own rate). Named apart
|
||||
// from the build so a shell can tell the user WHY a load is silent — the file decoded fine.
|
||||
inline bool sourceRateSupported(int sourceRate, int renderRate) {
|
||||
if (sourceRate <= 0 || renderRate <= 0) return true;
|
||||
return static_cast<double>(sourceRate) / static_cast<double>(renderRate) <=
|
||||
kMaxSourceRateRatio;
|
||||
}
|
||||
|
||||
// The Preserve ring's window in SOURCE frames. The ring holds source and its feed and tap both
|
||||
// carry sourceRateRatio, so a window sized at the source rate keeps every splice interval,
|
||||
// search reach and alignable period in time_stretch.h fixed in Hz at any host rate. Floored at 2
|
||||
// so a valid window is always a real ring.
|
||||
// search reach and alignable period in time_stretch.h fixed in Hz at any host rate; the
|
||||
// shifter's crossfade and search stride are what it converts to output frames
|
||||
// (PitchShifter::setSourceRateRatio). Floored at 2 so a valid window is always a real ring.
|
||||
inline std::int64_t preserveWindowFrames(const SampleData& s) {
|
||||
const std::int64_t w = static_cast<std::int64_t>(
|
||||
kPreserveWindowMs * static_cast<double>(s.sampleRate) / 1000.0 + 0.5);
|
||||
|
||||
Reference in New Issue
Block a user