Merge pull request #434 from jpcima/oscillators-detunecc

Add oscillator_detune_oncc
This commit is contained in:
JP Cimalando 2020-09-23 17:42:56 +02:00 committed by GitHub
commit 2b790f4efd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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 float oscillatorDetune { 0 };
constexpr Range<float> oscillatorDetuneRange { -9600, 9600 };
constexpr Range<float> oscillatorDetuneCCRange { -9600, 9600 };
constexpr int oscillatorQuality { 1 };
constexpr Range<int> oscillatorQualityRange { 0, 3 };

View file

@ -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();

View file

@ -669,9 +669,14 @@ void sfz::Voice::fillWithGenerator(AudioSpan<float> 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<float>(leftSpan, rightSpan);
}
else {
@ -681,11 +686,19 @@ void sfz::Voice::fillWithGenerator(AudioSpan<float> 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<float>(waveLeftGain[i], *tempSpan, leftSpan);
multiplyAdd1<float>(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<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()));
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()));
}

View file

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

View file

@ -60,7 +60,7 @@ void WavetableOscillator::processSingle(float frequency, float detuneRatio, floa
}
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 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<const float> table = multi.getTableForFrequency(frequency);
float position = phase * tableSize;
@ -111,7 +111,7 @@ void WavetableOscillator::processDual(float frequency, float detuneRatio, float*
}
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 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<kInterpolatorNearest>(frequencies, detuneRatio, output, nframes);
processModulatedSingle<kInterpolatorNearest>(frequencies, detuneRatios, output, nframes);
break;
case 1:
processModulatedSingle<kInterpolatorLinear>(frequencies, detuneRatio, output, nframes);
processModulatedSingle<kInterpolatorLinear>(frequencies, detuneRatios, output, nframes);
break;
case 2:
processModulatedSingle<kInterpolatorBspline3>(frequencies, detuneRatio, output, nframes);
processModulatedSingle<kInterpolatorBspline3>(frequencies, detuneRatios, output, nframes);
break;
case 3:
processModulatedDual<kInterpolatorBspline3>(frequencies, detuneRatio, output, nframes);
processModulatedDual<kInterpolatorBspline3>(frequencies, detuneRatios, output, nframes);
break;
}
}

View file

@ -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 <InterpolatorModel M>
void processSingle(float frequency, float detuneRatio, float* output, unsigned nframes);
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
template <InterpolatorModel M>
void processDual(float frequency, float detuneRatio, float* output, unsigned nframes);
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:
float _phase = 0.0f;

View file

@ -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:

View file

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

View file

@ -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 {};

View file

@ -58,6 +58,7 @@ private:
float fSweepIncrement = 0.0;
std::unique_ptr<float[]> fTmpFrequency;
std::unique_ptr<float[]> 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;