Add oscillator_detune_oncc

This commit is contained in:
Jean Pierre Cimalando 2020-09-22 18:23:31 +02:00
parent dc7b42fc43
commit 5c1931de2c
10 changed files with 49 additions and 19 deletions

View file

@ -68,6 +68,7 @@ namespace Default
constexpr Range<int> oscillatorMultiRange { 1, config::oscillatorsPerVoice }; constexpr Range<int> oscillatorMultiRange { 1, config::oscillatorsPerVoice };
constexpr float oscillatorDetune { 0 }; constexpr float oscillatorDetune { 0 };
constexpr Range<float> oscillatorDetuneRange { -9600, 9600 }; constexpr Range<float> oscillatorDetuneRange { -9600, 9600 };
constexpr Range<float> oscillatorDetuneCCRange { -9600, 9600 };
constexpr int oscillatorQuality { 1 }; constexpr int oscillatorQuality { 1 };
constexpr Range<int> oscillatorQualityRange { 0, 3 }; constexpr Range<int> oscillatorQualityRange { 0, 3 };

View file

@ -153,6 +153,9 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
case hash("oscillator_detune"): case hash("oscillator_detune"):
setValueFromOpcode(opcode, oscillatorDetune, Default::oscillatorDetuneRange); setValueFromOpcode(opcode, oscillatorDetune, Default::oscillatorDetuneRange);
break; break;
case_any_ccN("oscillator_detune"):
processGenericCc(opcode, Default::oscillatorDetuneCCRange, ModKey::createNXYZ(ModId::OscillatorDetune, id));
break;
case hash("oscillator_quality"): case hash("oscillator_quality"):
if (opcode.value == "-1") if (opcode.value == "-1")
oscillatorQuality.reset(); oscillatorQuality.reset();

View file

@ -669,9 +669,14 @@ void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
fill(*frequencies, pitchRatio * keycenterFrequency); fill(*frequencies, pitchRatio * keycenterFrequency);
pitchEnvelope(*frequencies); pitchEnvelope(*frequencies);
auto detuneSpan = resources.bufferPool.getBuffer(numFrames);
if (!detuneSpan)
return;
if (waveUnisonSize == 1) { if (waveUnisonSize == 1) {
WavetableOscillator& osc = waveOscillators[0]; 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<float>(leftSpan, rightSpan); copy<float>(leftSpan, rightSpan);
} }
else { else {
@ -681,11 +686,19 @@ void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
if (!tempSpan) if (!tempSpan)
return; return;
for (unsigned i = 0, n = waveUnisonSize; i < n; ++i) { const float* detuneMod = resources.modMatrix.getModulation(oscillatorDetuneTarget);
WavetableOscillator& osc = waveOscillators[i]; for (unsigned u = 0, uSize = waveUnisonSize; u < uSize; ++u) {
osc.processModulated(frequencies->data(), waveDetuneRatio[i], tempSpan->data(), numFrames); WavetableOscillator& osc = waveOscillators[u];
multiplyAdd1<float>(waveLeftGain[i], *tempSpan, leftSpan); if (!detuneMod)
multiplyAdd1<float>(waveRightGain[i], *tempSpan, rightSpan); 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<float>(waveLeftGain[u], *tempSpan, leftSpan);
multiplyAdd1<float>(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())); positionTarget = mm.findTarget(ModKey::createNXYZ(ModId::Position, region->getId()));
widthTarget = mm.findTarget(ModKey::createNXYZ(ModId::Width, region->getId())); widthTarget = mm.findTarget(ModKey::createNXYZ(ModId::Width, region->getId()));
pitchTarget = mm.findTarget(ModKey::createNXYZ(ModId::Pitch, region->getId())); pitchTarget = mm.findTarget(ModKey::createNXYZ(ModId::Pitch, region->getId()));
oscillatorDetuneTarget = mm.findTarget(ModKey::createNXYZ(ModId::OscillatorDetune, region->getId()));
} }

View file

@ -494,6 +494,7 @@ private:
ModMatrix::TargetId positionTarget; ModMatrix::TargetId positionTarget;
ModMatrix::TargetId widthTarget; ModMatrix::TargetId widthTarget;
ModMatrix::TargetId pitchTarget; ModMatrix::TargetId pitchTarget;
ModMatrix::TargetId oscillatorDetuneTarget;
PowerFollower powerFollower; PowerFollower powerFollower;

View file

@ -60,7 +60,7 @@ void WavetableOscillator::processSingle(float frequency, float detuneRatio, floa
} }
template <InterpolatorModel M> template <InterpolatorModel M>
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 phase = _phase;
float sampleInterval = _sampleInterval; float sampleInterval = _sampleInterval;
@ -70,7 +70,7 @@ void WavetableOscillator::processModulatedSingle(const float* frequencies, float
for (unsigned i = 0; i < nframes; ++i) { for (unsigned i = 0; i < nframes; ++i) {
float frequency = frequencies[i]; float frequency = frequencies[i];
float phaseInc = frequency * (detuneRatio * sampleInterval); float phaseInc = frequency * (detuneRatios[i] * sampleInterval);
absl::Span<const float> table = multi.getTableForFrequency(frequency); absl::Span<const float> table = multi.getTableForFrequency(frequency);
float position = phase * tableSize; float position = phase * tableSize;
@ -111,7 +111,7 @@ void WavetableOscillator::processDual(float frequency, float detuneRatio, float*
} }
template <InterpolatorModel M> template <InterpolatorModel M>
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 phase = _phase;
float sampleInterval = _sampleInterval; float sampleInterval = _sampleInterval;
@ -121,7 +121,7 @@ void WavetableOscillator::processModulatedDual(const float* frequencies, float d
for (unsigned i = 0; i < nframes; ++i) { for (unsigned i = 0; i < nframes; ++i) {
float frequency = frequencies[i]; float frequency = frequencies[i];
float phaseInc = frequency * (detuneRatio * sampleInterval); float phaseInc = frequency * (detuneRatios[i] * sampleInterval);
WavetableMulti::DualTable dt = multi.getInterpolationPairForFrequency(frequency); 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); int quality = clamp(_quality, 0, 3);
switch (quality) { switch (quality) {
case 0: case 0:
processModulatedSingle<kInterpolatorNearest>(frequencies, detuneRatio, output, nframes); processModulatedSingle<kInterpolatorNearest>(frequencies, detuneRatios, output, nframes);
break; break;
case 1: case 1:
processModulatedSingle<kInterpolatorLinear>(frequencies, detuneRatio, output, nframes); processModulatedSingle<kInterpolatorLinear>(frequencies, detuneRatios, output, nframes);
break; break;
case 2: case 2:
processModulatedSingle<kInterpolatorBspline3>(frequencies, detuneRatio, output, nframes); processModulatedSingle<kInterpolatorBspline3>(frequencies, detuneRatios, output, nframes);
break; break;
case 3: case 3:
processModulatedDual<kInterpolatorBspline3>(frequencies, detuneRatio, output, nframes); processModulatedDual<kInterpolatorBspline3>(frequencies, detuneRatios, output, nframes);
break; break;
} }
} }

View file

@ -71,20 +71,20 @@ public:
/** /**
Compute a cycle of the oscillator, with varying frequency. 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: private:
// single-table interpolation // single-table interpolation
template <InterpolatorModel M> template <InterpolatorModel M>
void processSingle(float frequency, float detuneRatio, float* output, unsigned nframes); void processSingle(float frequency, float detuneRatio, float* output, unsigned nframes);
template <InterpolatorModel M> template <InterpolatorModel M>
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 // dual-table interpolation
template <InterpolatorModel M> template <InterpolatorModel M>
void processDual(float frequency, float detuneRatio, float* output, unsigned nframes); void processDual(float frequency, float detuneRatio, float* output, unsigned nframes);
template <InterpolatorModel M> template <InterpolatorModel M>
void processModulatedDual(const float* frequencies, float detuneRatio, float* output, unsigned nframes); void processModulatedDual(const float* frequencies, const float* detuneRatios, float* output, unsigned nframes);
private: private:
float _phase = 0.0f; float _phase = 0.0f;

View file

@ -56,6 +56,8 @@ int ModIds::flags(ModId id) noexcept
return kModIsPerVoice|kModIsAdditive; return kModIsPerVoice|kModIsAdditive;
case ModId::EqBandwidth: case ModId::EqBandwidth:
return kModIsPerVoice|kModIsAdditive; return kModIsPerVoice|kModIsAdditive;
case ModId::OscillatorDetune:
return kModIsPerVoice|kModIsAdditive;
// unknown // unknown
default: default:

View file

@ -43,6 +43,7 @@ enum class ModId : int {
EqGain, EqGain,
EqFrequency, EqFrequency,
EqBandwidth, EqBandwidth,
OscillatorDetune,
_TargetsEnd, _TargetsEnd,
// [/targets] -------------------------------------------------------------- // [/targets] --------------------------------------------------------------

View file

@ -99,6 +99,8 @@ std::string ModKey::toString() const
return absl::StrCat("EqFrequency {", region_.number(), ", N=", 1 + params_.N, "}"); return absl::StrCat("EqFrequency {", region_.number(), ", N=", 1 + params_.N, "}");
case ModId::EqBandwidth: case ModId::EqBandwidth:
return absl::StrCat("EqBandwidth {", region_.number(), ", N=", 1 + params_.N, "}"); return absl::StrCat("EqBandwidth {", region_.number(), ", N=", 1 + params_.N, "}");
case ModId::OscillatorDetune:
return absl::StrCat("OscillatorDetune {", region_.number(), ", N=", 1 + params_.N, "}");
default: default:
return {}; return {};

View file

@ -58,6 +58,7 @@ private:
float fSweepIncrement = 0.0; float fSweepIncrement = 0.0;
std::unique_ptr<float[]> fTmpFrequency; std::unique_ptr<float[]> fTmpFrequency;
std::unique_ptr<float[]> fTmpDetune;
jack_client_u fClient; jack_client_u fClient;
jack_port_t* fPorts[2] = {}; jack_port_t* fPorts[2] = {};
@ -88,6 +89,7 @@ bool DemoApp::initSound()
unsigned bufferSize = jack_get_buffer_size(client); unsigned bufferSize = jack_get_buffer_size(client);
fTmpFrequency.reset(new float[bufferSize]); fTmpFrequency.reset(new float[bufferSize]);
fTmpDetune.reset(new float[bufferSize]);
fMulti[0] = sfz::WavetableMulti::createForHarmonicProfile( fMulti[0] = sfz::WavetableMulti::createForHarmonicProfile(
sfz::HarmonicProfile::getSine(), sfz::config::amplitudeSine, 2048); sfz::HarmonicProfile::getSine(), sfz::config::amplitudeSine, 2048);
@ -188,8 +190,12 @@ int DemoApp::processAudio(jack_nframes_t nframes, void* cbdata)
} }
self->fSweepCurrent = sweepCurrent; self->fSweepCurrent = sweepCurrent;
// fill the detune value
float* detune = self->fTmpDetune.get();
std::fill(detune, detune + nframes, 1.0f);
// compute oscillator // compute oscillator
osc.processModulated(frequency, 1.0, left, nframes); osc.processModulated(frequency, detune, left, nframes);
std::memcpy(right, left, nframes * sizeof(float)); std::memcpy(right, left, nframes * sizeof(float));
return 0; return 0;