From 5c1931de2c4c6a16c3d6c7f32c4294cc882b58f2 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 22 Sep 2020 18:23:31 +0200 Subject: [PATCH] Add oscillator_detune_oncc --- src/sfizz/Defaults.h | 1 + src/sfizz/Region.cpp | 3 +++ src/sfizz/Voice.cpp | 26 ++++++++++++++++++++------ src/sfizz/Voice.h | 1 + src/sfizz/Wavetables.cpp | 18 +++++++++--------- src/sfizz/Wavetables.h | 6 +++--- src/sfizz/modulations/ModId.cpp | 2 ++ src/sfizz/modulations/ModId.h | 1 + src/sfizz/modulations/ModKey.cpp | 2 ++ tests/DemoWavetables.cpp | 8 +++++++- 10 files changed, 49 insertions(+), 19 deletions(-) diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index e6cc47f5..1fb2255f 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -68,6 +68,7 @@ namespace Default constexpr Range oscillatorMultiRange { 1, config::oscillatorsPerVoice }; constexpr float oscillatorDetune { 0 }; constexpr Range oscillatorDetuneRange { -9600, 9600 }; + constexpr Range oscillatorDetuneCCRange { -9600, 9600 }; constexpr int oscillatorQuality { 1 }; constexpr Range oscillatorQualityRange { 0, 3 }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 7ec6619d..affc4997 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -153,6 +153,9 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) case hash("oscillator_detune"): setValueFromOpcode(opcode, oscillatorDetune, Default::oscillatorDetuneRange); break; + case_any_ccN("oscillator_detune"): + processGenericCc(opcode, Default::oscillatorDetuneCCRange, ModKey::createNXYZ(ModId::OscillatorDetune, id)); + break; case hash("oscillator_quality"): if (opcode.value == "-1") oscillatorQuality.reset(); diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 499e21b2..ad236fe7 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -669,9 +669,14 @@ void sfz::Voice::fillWithGenerator(AudioSpan buffer) noexcept fill(*frequencies, pitchRatio * keycenterFrequency); pitchEnvelope(*frequencies); + auto detuneSpan = resources.bufferPool.getBuffer(numFrames); + if (!detuneSpan) + return; + if (waveUnisonSize == 1) { WavetableOscillator& osc = waveOscillators[0]; - osc.processModulated(frequencies->data(), 1.0, leftSpan.data(), buffer.getNumFrames()); + fill(*detuneSpan, 1.0f); + osc.processModulated(frequencies->data(), detuneSpan->data(), leftSpan.data(), buffer.getNumFrames()); copy(leftSpan, rightSpan); } else { @@ -681,11 +686,19 @@ void sfz::Voice::fillWithGenerator(AudioSpan buffer) noexcept if (!tempSpan) return; - for (unsigned i = 0, n = waveUnisonSize; i < n; ++i) { - WavetableOscillator& osc = waveOscillators[i]; - osc.processModulated(frequencies->data(), waveDetuneRatio[i], tempSpan->data(), numFrames); - multiplyAdd1(waveLeftGain[i], *tempSpan, leftSpan); - multiplyAdd1(waveRightGain[i], *tempSpan, rightSpan); + const float* detuneMod = resources.modMatrix.getModulation(oscillatorDetuneTarget); + for (unsigned u = 0, uSize = waveUnisonSize; u < uSize; ++u) { + WavetableOscillator& osc = waveOscillators[u]; + if (!detuneMod) + fill(*detuneSpan, waveDetuneRatio[u]); + else { + for (size_t i = 0; i < numFrames; ++i) + (*detuneSpan)[i] = centsFactor(detuneMod[i]); + applyGain1(waveDetuneRatio[u], *detuneSpan); + } + osc.processModulated(frequencies->data(), detuneSpan->data(), tempSpan->data(), numFrames); + multiplyAdd1(waveLeftGain[u], *tempSpan, leftSpan); + multiplyAdd1(waveRightGain[u], *tempSpan, rightSpan); } } } @@ -921,4 +934,5 @@ void sfz::Voice::saveModulationTargets(const Region* region) noexcept positionTarget = mm.findTarget(ModKey::createNXYZ(ModId::Position, region->getId())); widthTarget = mm.findTarget(ModKey::createNXYZ(ModId::Width, region->getId())); pitchTarget = mm.findTarget(ModKey::createNXYZ(ModId::Pitch, region->getId())); + oscillatorDetuneTarget = mm.findTarget(ModKey::createNXYZ(ModId::OscillatorDetune, region->getId())); } diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 8afe3a06..4224c927 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -494,6 +494,7 @@ private: ModMatrix::TargetId positionTarget; ModMatrix::TargetId widthTarget; ModMatrix::TargetId pitchTarget; + ModMatrix::TargetId oscillatorDetuneTarget; PowerFollower powerFollower; diff --git a/src/sfizz/Wavetables.cpp b/src/sfizz/Wavetables.cpp index 55a39be6..2a668a13 100644 --- a/src/sfizz/Wavetables.cpp +++ b/src/sfizz/Wavetables.cpp @@ -60,7 +60,7 @@ void WavetableOscillator::processSingle(float frequency, float detuneRatio, floa } template -void WavetableOscillator::processModulatedSingle(const float* frequencies, float detuneRatio, float* output, unsigned nframes) +void WavetableOscillator::processModulatedSingle(const float* frequencies, const float* detuneRatios, float* output, unsigned nframes) { float phase = _phase; float sampleInterval = _sampleInterval; @@ -70,7 +70,7 @@ void WavetableOscillator::processModulatedSingle(const float* frequencies, float for (unsigned i = 0; i < nframes; ++i) { float frequency = frequencies[i]; - float phaseInc = frequency * (detuneRatio * sampleInterval); + float phaseInc = frequency * (detuneRatios[i] * sampleInterval); absl::Span table = multi.getTableForFrequency(frequency); float position = phase * tableSize; @@ -111,7 +111,7 @@ void WavetableOscillator::processDual(float frequency, float detuneRatio, float* } template -void WavetableOscillator::processModulatedDual(const float* frequencies, float detuneRatio, float* output, unsigned nframes) +void WavetableOscillator::processModulatedDual(const float* frequencies, const float* detuneRatios, float* output, unsigned nframes) { float phase = _phase; float sampleInterval = _sampleInterval; @@ -121,7 +121,7 @@ void WavetableOscillator::processModulatedDual(const float* frequencies, float d for (unsigned i = 0; i < nframes; ++i) { float frequency = frequencies[i]; - float phaseInc = frequency * (detuneRatio * sampleInterval); + float phaseInc = frequency * (detuneRatios[i] * sampleInterval); WavetableMulti::DualTable dt = multi.getInterpolationPairForFrequency(frequency); @@ -159,22 +159,22 @@ void WavetableOscillator::process(float frequency, float detuneRatio, float* out } } -void WavetableOscillator::processModulated(const float* frequencies, float detuneRatio, float* output, unsigned nframes) +void WavetableOscillator::processModulated(const float* frequencies, const float* detuneRatios, float* output, unsigned nframes) { int quality = clamp(_quality, 0, 3); switch (quality) { case 0: - processModulatedSingle(frequencies, detuneRatio, output, nframes); + processModulatedSingle(frequencies, detuneRatios, output, nframes); break; case 1: - processModulatedSingle(frequencies, detuneRatio, output, nframes); + processModulatedSingle(frequencies, detuneRatios, output, nframes); break; case 2: - processModulatedSingle(frequencies, detuneRatio, output, nframes); + processModulatedSingle(frequencies, detuneRatios, output, nframes); break; case 3: - processModulatedDual(frequencies, detuneRatio, output, nframes); + processModulatedDual(frequencies, detuneRatios, output, nframes); break; } } diff --git a/src/sfizz/Wavetables.h b/src/sfizz/Wavetables.h index 87cd72aa..a7d90773 100644 --- a/src/sfizz/Wavetables.h +++ b/src/sfizz/Wavetables.h @@ -71,20 +71,20 @@ public: /** Compute a cycle of the oscillator, with varying frequency. */ - void processModulated(const float* frequencies, float detuneRatio, float* output, unsigned nframes); + void processModulated(const float* frequencies, const float* detuneRatios, float* output, unsigned nframes); private: // single-table interpolation template void processSingle(float frequency, float detuneRatio, float* output, unsigned nframes); template - void processModulatedSingle(const float* frequencies, float detuneRatio, float* output, unsigned nframes); + void processModulatedSingle(const float* frequencies, const float* detuneRatios, float* output, unsigned nframes); // dual-table interpolation template void processDual(float frequency, float detuneRatio, float* output, unsigned nframes); template - void processModulatedDual(const float* frequencies, float detuneRatio, float* output, unsigned nframes); + void processModulatedDual(const float* frequencies, const float* detuneRatios, float* output, unsigned nframes); private: float _phase = 0.0f; diff --git a/src/sfizz/modulations/ModId.cpp b/src/sfizz/modulations/ModId.cpp index 9d19e10e..ed1c6fc0 100644 --- a/src/sfizz/modulations/ModId.cpp +++ b/src/sfizz/modulations/ModId.cpp @@ -56,6 +56,8 @@ int ModIds::flags(ModId id) noexcept return kModIsPerVoice|kModIsAdditive; case ModId::EqBandwidth: return kModIsPerVoice|kModIsAdditive; + case ModId::OscillatorDetune: + return kModIsPerVoice|kModIsAdditive; // unknown default: diff --git a/src/sfizz/modulations/ModId.h b/src/sfizz/modulations/ModId.h index 7ab96ee9..f7402e39 100644 --- a/src/sfizz/modulations/ModId.h +++ b/src/sfizz/modulations/ModId.h @@ -43,6 +43,7 @@ enum class ModId : int { EqGain, EqFrequency, EqBandwidth, + OscillatorDetune, _TargetsEnd, // [/targets] -------------------------------------------------------------- diff --git a/src/sfizz/modulations/ModKey.cpp b/src/sfizz/modulations/ModKey.cpp index 3615c976..7a7a3a3c 100644 --- a/src/sfizz/modulations/ModKey.cpp +++ b/src/sfizz/modulations/ModKey.cpp @@ -99,6 +99,8 @@ std::string ModKey::toString() const return absl::StrCat("EqFrequency {", region_.number(), ", N=", 1 + params_.N, "}"); case ModId::EqBandwidth: return absl::StrCat("EqBandwidth {", region_.number(), ", N=", 1 + params_.N, "}"); + case ModId::OscillatorDetune: + return absl::StrCat("OscillatorDetune {", region_.number(), ", N=", 1 + params_.N, "}"); default: return {}; diff --git a/tests/DemoWavetables.cpp b/tests/DemoWavetables.cpp index 873eb68d..bd57abbe 100644 --- a/tests/DemoWavetables.cpp +++ b/tests/DemoWavetables.cpp @@ -58,6 +58,7 @@ private: float fSweepIncrement = 0.0; std::unique_ptr fTmpFrequency; + std::unique_ptr fTmpDetune; jack_client_u fClient; jack_port_t* fPorts[2] = {}; @@ -88,6 +89,7 @@ bool DemoApp::initSound() unsigned bufferSize = jack_get_buffer_size(client); fTmpFrequency.reset(new float[bufferSize]); + fTmpDetune.reset(new float[bufferSize]); fMulti[0] = sfz::WavetableMulti::createForHarmonicProfile( sfz::HarmonicProfile::getSine(), sfz::config::amplitudeSine, 2048); @@ -188,8 +190,12 @@ int DemoApp::processAudio(jack_nframes_t nframes, void* cbdata) } self->fSweepCurrent = sweepCurrent; + // fill the detune value + float* detune = self->fTmpDetune.get(); + std::fill(detune, detune + nframes, 1.0f); + // compute oscillator - osc.processModulated(frequency, 1.0, left, nframes); + osc.processModulated(frequency, detune, left, nframes); std::memcpy(right, left, nframes * sizeof(float)); return 0;