// live_params.cpp — the fold from the parameter set to the live block, and the ramp-step law. // See live_params.h for the publication contract. #include "core/instrument/engine/live_params.h" namespace reasampler::instrument::engine { LiveValues foldLive(const PlayParams& params, double keyTrack) { // Value-initialized, so the padding is determinate too. Nothing reads it — the block's // equality is field-wise for exactly that reason — but this is the one construction site // every publisher goes through, and an object with indeterminate bytes travelling under a // seqlock is a hazard worth not having. Off the audio thread; the memset costs nothing here. LiveValues v{}; v.keyTrack = keyTrack; // Folded here, not at the voice: Voice::start reads the block's value directly, so the // spline rule has to be applied on the way in or the two would answer differently. v.splineActive = splineActive(params); v.lengthFraction = effectiveLengthFraction(params); v.filterSettings = params.filter.settings; v.filterModAmount = params.filter.modAmount; v.filterVelAmount = params.filter.velAmount; v.filterKeyTrack = params.filter.keyTrack; v.filterEnv = params.filter.env; v.filterAhd = params.filter.trigEnv; v.adsr = params.adsr; v.ampAhd = params.trigAhd; v.pitchEnv = params.pitchEnv; v.playRate = params.playRate; v.pitchOffsetSemitones = params.pitchOffsetSemitones; return v; } namespace { bool sameAdsr(const AdsrParams& a, const AdsrParams& b) { return a.attackFrames == b.attackFrames && a.holdFrames == b.holdFrames && a.decayFrames == b.decayFrames && a.sustainLevel == b.sustainLevel && a.releaseFrames == b.releaseFrames && a.attackCurve == b.attackCurve && a.decayCurve == b.decayCurve && a.releaseCurve == b.releaseCurve; } bool sameAhd(const AhdParams& a, const AhdParams& b) { return a.attackFrames == b.attackFrames && a.decayFrames == b.decayFrames && a.holdFraction == b.holdFraction && a.attackCurve == b.attackCurve && a.decayCurve == b.decayCurve; } bool sameFilterSettings(const filter::FilterSettings& a, const filter::FilterSettings& b) { return a.cutoffNorm == b.cutoffNorm && a.resonanceNorm == b.resonanceNorm && a.morphNorm == b.morphNorm && a.driveNorm == b.driveNorm && a.morphLaw == b.morphLaw; } } // namespace bool operator==(const LiveValues& a, const LiveValues& b) { return sameFilterSettings(a.filterSettings, b.filterSettings) && a.filterModAmount == b.filterModAmount && a.filterVelAmount == b.filterVelAmount && a.filterKeyTrack == b.filterKeyTrack && sameAdsr(a.filterEnv, b.filterEnv) && sameAhd(a.filterAhd, b.filterAhd) && sameAdsr(a.adsr, b.adsr) && sameAhd(a.ampAhd, b.ampAhd) && a.pitchEnv.enabled == b.pitchEnv.enabled && a.pitchEnv.peakSemitones == b.pitchEnv.peakSemitones && sameAhd(a.pitchEnv.shape, b.pitchEnv.shape) && a.playRate == b.playRate && a.pitchOffsetSemitones == b.pitchOffsetSemitones && a.keyTrack == b.keyTrack && a.lengthFraction == b.lengthFraction && a.splineActive == b.splineActive; } double liveRampStep(double sampleRate) { if (!(sampleRate > 0.0)) return 0.0; // also catches NaN return 1.0 / (kLiveRampSeconds * sampleRate); } } // namespace reasampler::instrument::engine