From cea3e4715beea6b755cf50e71dcd0ebee47ad203 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 3 Apr 2020 15:37:47 +0200 Subject: [PATCH 1/6] Add oscillator unison --- src/sfizz/Defaults.h | 4 ++ src/sfizz/Range.h | 4 +- src/sfizz/Region.cpp | 6 +++ src/sfizz/Region.h | 11 +++- src/sfizz/Synth.cpp | 2 +- src/sfizz/Voice.cpp | 109 ++++++++++++++++++++++++++++++++++----- src/sfizz/Voice.h | 14 ++++- src/sfizz/Wavetables.cpp | 8 +-- src/sfizz/Wavetables.h | 4 +- tests/DemoWavetables.cpp | 2 +- 10 files changed, 139 insertions(+), 25 deletions(-) diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 0e35ca49..2066cb19 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -63,6 +63,10 @@ namespace Default // Wavetable oscillator constexpr float oscillatorPhase { 0.0 }; constexpr Range oscillatorPhaseRange { -1.0, 360.0 }; + constexpr int oscillatorMulti { 1 }; + constexpr Range oscillatorMultiRange { 1, 9 }; + constexpr float oscillatorDetune { 0 }; + constexpr Range oscillatorDetuneRange { -9600, 9600 }; // Instrument setting: voice lifecycle constexpr uint32_t group { 0 }; diff --git a/src/sfizz/Range.h b/src/sfizz/Range.h index b39b7c0d..9f339565 100644 --- a/src/sfizz/Range.h +++ b/src/sfizz/Range.h @@ -28,8 +28,8 @@ public: { } - Type getStart() const noexcept { return _start; } - Type getEnd() const noexcept { return _end; } + constexpr Type getStart() const noexcept { return _start; } + constexpr Type getEnd() const noexcept { return _end; } /** * @brief Get the range as an std::pair of the endpoints * diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 76806b48..234878e9 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -102,6 +102,12 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) if (auto value = readBooleanFromOpcode(opcode)) oscillator = *value; break; + case hash("oscillator_multi"): + setValueFromOpcode(opcode, oscillatorMulti, Default::oscillatorMultiRange); + break; + case hash("oscillator_detune"): + setValueFromOpcode(opcode, oscillatorDetune, Default::oscillatorDetuneRange); + break; // Instrument settings: voice lifecycle case hash("group"): // fallthrough diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index a7a4e710..73f3bc53 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -60,6 +60,13 @@ struct Region { * @return false */ bool isGenerator() const noexcept { return sample.size() > 0 ? sample[0] == '*' : false; } + /** + * @brief Is stereo (has stereo sample or is unison oscillator)? + * + * @return true + * @return false + */ + bool isStereo() const noexcept { return hasStereoSample || ((oscillator || isGenerator()) && oscillatorMulti >= 3); } /** * @brief Is a looping region (at least potentially)? * @@ -235,6 +242,8 @@ struct Region { // Wavetable oscillator float oscillatorPhase { Default::oscillatorPhase }; bool oscillator = false; + int oscillatorMulti = Default::oscillatorMulti; + float oscillatorDetune = Default::oscillatorDetune; // Instrument settings: voice lifecycle uint32_t group { Default::group }; // group @@ -318,7 +327,7 @@ struct Region { EGDescription pitchEG; EGDescription filterEG; - bool isStereo { false }; + bool hasStereoSample { false }; // Effects std::vector gainToEffect; diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 25393b4d..54d7fc12 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -379,7 +379,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) } if (fileInformation->numChannels == 2) - region->isStereo = true; + region->hasStereoSample = true; // TODO: adjust with LFO targets const auto maxOffset = region->offset + region->offsetRandom; diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 4aa4e0bd..775d220d 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -21,7 +21,8 @@ sfz::Voice::Voice(sfz::Resources& resources) filters.reserve(config::filtersPerVoice); equalizers.reserve(config::eqsPerVoice); - waveOscillator.init(sampleRate); + for (WavetableOscillator& osc : waveOscillators) + osc.init(sampleRate); } void sfz::Voice::startVoice(Region* region, int delay, int number, float value, sfz::Voice::TriggerType triggerType) noexcept @@ -59,12 +60,18 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, wave = resources.wavePool.getWaveSaw(); break; } - waveOscillator.setWavetable(wave); - waveOscillator.setPhase(region->getPhase()); + for (WavetableOscillator& osc : waveOscillators) { + osc.setWavetable(wave); + osc.setPhase(region->getPhase()); + } + setupOscillatorUnison(); } else if (region->oscillator) { const WavetableMulti* wave = resources.wavePool.getFileWave(region->sample); - waveOscillator.setWavetable(wave); - waveOscillator.setPhase(region->getPhase()); + for (WavetableOscillator& osc : waveOscillators) { + osc.setWavetable(wave); + osc.setPhase(region->getPhase()); + } + setupOscillatorUnison(); } else { currentPromise = resources.filePool.getFilePromise(region->sample); if (currentPromise == nullptr) { @@ -83,7 +90,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, ASSERT((filters.capacity() - filters.size()) >= region->filters.size()); ASSERT((equalizers.capacity() - equalizers.size()) >= region->equalizers.size()); - const unsigned numChannels = region->isStereo ? 2 : 1; + const unsigned numChannels = region->isStereo() ? 2 : 1; for (auto& filter: region->filters) { auto newFilter = resources.filterPool.getFilter(filter, numChannels, number, value); if (newFilter) @@ -187,7 +194,8 @@ void sfz::Voice::setSampleRate(float sampleRate) noexcept { this->sampleRate = sampleRate; - waveOscillator.init(sampleRate); + for (WavetableOscillator& osc : waveOscillators) + osc.init(sampleRate); } void sfz::Voice::setSamplesPerBlock(int samplesPerBlock) noexcept @@ -218,7 +226,7 @@ void sfz::Voice::renderBlock(AudioSpan buffer) noexcept fillWithData(delayed_buffer); } - if (region->isStereo) { + if (region->isStereo()) { ampStageStereo(buffer); panStageStereo(buffer); filterStageStereo(buffer); @@ -518,9 +526,10 @@ void sfz::Voice::fillWithGenerator(AudioSpan buffer) noexcept absl::c_generate(leftSpan, [&](){ return noiseDist(Random::randomGenerator); }); absl::c_generate(rightSpan, [&](){ return noiseDist(Random::randomGenerator); }); } else { - const auto numSamples = buffer.getNumFrames(); - auto frequencies = resources.bufferPool.getBuffer(numSamples); - auto bends = resources.bufferPool.getBuffer(numSamples); + const auto numFrames = buffer.getNumFrames(); + + auto frequencies = resources.bufferPool.getBuffer(numFrames); + auto bends = resources.bufferPool.getBuffer(numFrames); if (!frequencies || !bends) return; @@ -544,8 +553,25 @@ void sfz::Voice::fillWithGenerator(AudioSpan buffer) noexcept applyGain(*bends, *frequencies); } - waveOscillator.processModulated(frequencies->data(), leftSpan.data(), buffer.getNumFrames()); - copy(leftSpan, rightSpan); + if (waveUnisonSize == 1) { + WavetableOscillator& osc = waveOscillators[0]; + osc.processModulated(frequencies->data(), 1.0, leftSpan.data(), buffer.getNumFrames()); + copy(leftSpan, rightSpan); + } + else { + buffer.fill(0.0f); + + auto tempSpan = resources.bufferPool.getBuffer(numFrames); + 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); + sfz::multiplyAdd(waveLeftGain[i], *tempSpan, leftSpan); + sfz::multiplyAdd(waveRightGain[i], *tempSpan, rightSpan); + } + } } } @@ -620,3 +646,60 @@ void sfz::Voice::setMaxEQsPerVoice(size_t numFilters) ASSERT(equalizers.size() == 0); equalizers.reserve(numFilters); } + +void sfz::Voice::setupOscillatorUnison() +{ + int m = region->oscillatorMulti; + float d = region->oscillatorDetune; + + // 3-9: unison mode, 1: normal/RM, 2: PM/FM + // TODO(jpc) RM/FM/PM synthesis + if (m < 3) { + waveUnisonSize = 1; + waveDetuneRatio[0] = 1.0; + waveLeftGain[0] = 1.0; + waveRightGain[0] = 1.0; + return; + } + + // oscillator count, aka. unison size + waveUnisonSize = m; + + // detune (cents) + float detunes[maxWaveOscillators]; + detunes[0] = 0.0; + detunes[1] = -d; + detunes[2] = +d; + for (int i = 3; i < m; ++i) + detunes[i] = d * ((i & 1) ? -0.25f : +0.25f) * ((i - 1) / 2); + + // detune (ratio) + for (int i = 0; i < m; ++i) + waveDetuneRatio[i] = std::exp2(detunes[i] * (0.01f / 12.0f)); + + // gains + waveLeftGain[0] = 0.0; + waveRightGain[m - 1] = 0.0; + for (int i = 0; i < m - 1; ++i) { + float g = 1 - i / float(m - 1); + waveLeftGain[m - 1 - i] = g; + waveRightGain[i] = g; + } + +#if 0 + fprintf(stderr, "\n"); + fprintf(stderr, "# Left:\n"); + for (int i = m - 1; i >= 0; --i) { + if (waveLeftGain[i] != 0) + fprintf(stderr, "[%d] %10g cents, %10g dB\n", i, detunes[i], 20.0f * std::log10(waveLeftGain[i])); + } + fprintf(stderr, "\n"); + fprintf(stderr, "# Right:\n"); + for (int i = 0; i < m; ++i) { + if (waveRightGain[i] != 0) + fprintf(stderr, "[%d] %10g cents, %10g dB\n", i, detunes[i], 20.0f * std::log10(waveRightGain[i])); + } +#endif +} + +constexpr unsigned sfz::Voice::maxWaveOscillators; diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index f0a70af9..afde9b53 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -244,6 +244,10 @@ private: void panStageStereo(AudioSpan buffer) noexcept; void filterStageMono(AudioSpan buffer) noexcept; void filterStageStereo(AudioSpan buffer) noexcept; + /** + * @brief Initialize frequency and gain coefficients for the oscillators. + */ + void setupOscillatorUnison(); Region* region { nullptr }; @@ -284,7 +288,15 @@ private: ADSREnvelope egEnvelope; float bendStepFactor { centsFactor(1) }; - WavetableOscillator waveOscillator; + static constexpr unsigned maxWaveOscillators = Default::oscillatorMultiRange.getEnd(); + + WavetableOscillator waveOscillators[maxWaveOscillators]; + + // unison of oscillators + unsigned waveUnisonSize { 0 }; + float waveDetuneRatio[maxWaveOscillators] { }; + float waveLeftGain[maxWaveOscillators] { }; + float waveRightGain[maxWaveOscillators] { }; Duration dataDuration; Duration amplitudeDuration; diff --git a/src/sfizz/Wavetables.cpp b/src/sfizz/Wavetables.cpp index c65b244a..b04c0a8c 100644 --- a/src/sfizz/Wavetables.cpp +++ b/src/sfizz/Wavetables.cpp @@ -34,10 +34,10 @@ void WavetableOscillator::setPhase(float phase) _phase = phase; } -void WavetableOscillator::process(float frequency, float* output, unsigned nframes) +void WavetableOscillator::process(float frequency, float detuneRatio, float* output, unsigned nframes) { float phase = _phase; - float phaseInc = frequency * _sampleInterval; + float phaseInc = frequency * (detuneRatio * _sampleInterval); const WavetableMulti& multi = *_multi; unsigned tableSize = multi.tableSize(); @@ -56,7 +56,7 @@ void WavetableOscillator::process(float frequency, float* output, unsigned nfram _phase = phase; } -void WavetableOscillator::processModulated(const float* frequencies, float* output, unsigned nframes) +void WavetableOscillator::processModulated(const float* frequencies, float detuneRatio, float* output, unsigned nframes) { float phase = _phase; float sampleInterval = _sampleInterval; @@ -66,7 +66,7 @@ void WavetableOscillator::processModulated(const float* frequencies, float* outp for (unsigned i = 0; i < nframes; ++i) { float frequency = frequencies[i]; - float phaseInc = frequency * sampleInterval; + float phaseInc = frequency * (detuneRatio * sampleInterval); absl::Span table = multi.getTableForFrequency(frequency); float position = phase * tableSize; diff --git a/src/sfizz/Wavetables.h b/src/sfizz/Wavetables.h index 6290f01f..3d0c9039 100644 --- a/src/sfizz/Wavetables.h +++ b/src/sfizz/Wavetables.h @@ -47,12 +47,12 @@ public: /** Compute a cycle of the oscillator, with constant frequency. */ - void process(float frequency, float* output, unsigned nframes); + void process(float frequency, float detuneRatio, float* output, unsigned nframes); /** Compute a cycle of the oscillator, with varying frequency. */ - void processModulated(const float* frequencies, float* output, unsigned nframes); + void processModulated(const float* frequencies, float detuneRatio, float* output, unsigned nframes); private: /** diff --git a/tests/DemoWavetables.cpp b/tests/DemoWavetables.cpp index d5b9ba5a..e573260b 100644 --- a/tests/DemoWavetables.cpp +++ b/tests/DemoWavetables.cpp @@ -172,7 +172,7 @@ int DemoApp::processAudio(jack_nframes_t nframes, void* cbdata) self->fSweepCurrent = sweepCurrent; // compute oscillator - osc.processModulated(frequency, left, nframes); + osc.processModulated(frequency, 1.0, left, nframes); std::memcpy(right, left, nframes * sizeof(float)); return 0; From b338ac0ff9fc959893df800a1288fff6f3a98320 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 3 Apr 2020 17:25:30 +0200 Subject: [PATCH 2/6] Update tests --- tests/FilesT.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index f4bdc3c4..f0a4db39 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -254,9 +254,9 @@ TEST_CASE("[Files] Channels (channels.sfz)") synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels.sfz"); REQUIRE(synth.getNumRegions() == 2); REQUIRE(synth.getRegionView(0)->sample == "mono_sample.wav"); - REQUIRE(!synth.getRegionView(0)->isStereo); + REQUIRE(!synth.getRegionView(0)->isStereo()); REQUIRE(synth.getRegionView(1)->sample == "stereo_sample.wav"); - REQUIRE(synth.getRegionView(1)->isStereo); + REQUIRE(synth.getRegionView(1)->isStereo()); } TEST_CASE("[Files] sw_default") From db3521027e749c5511a9ed5a4bdfc29b1934a4d9 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 3 Apr 2020 17:29:37 +0200 Subject: [PATCH 3/6] Fix some clang-tidy warnings --- src/sfizz/Synth.cpp | 2 +- src/sfizz/Voice.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 54d7fc12..ec102de3 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -785,7 +785,7 @@ void sfz::Synth::pitchWheel(int delay, int pitch) noexcept { ASSERT(pitch <= 8192); ASSERT(pitch >= -8192); - const auto normalizedPitch = normalizeBend(pitch); + const auto normalizedPitch = normalizeBend(float(pitch)); ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; resources.midiState.pitchBendEvent(delay, normalizedPitch); diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 775d220d..4a9c6c76 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -670,8 +670,10 @@ void sfz::Voice::setupOscillatorUnison() detunes[0] = 0.0; detunes[1] = -d; detunes[2] = +d; - for (int i = 3; i < m; ++i) - detunes[i] = d * ((i & 1) ? -0.25f : +0.25f) * ((i - 1) / 2); + for (int i = 3; i < m; ++i) { + int n = (i - 1) / 2; + detunes[i] = d * ((i & 1) ? -0.25f : +0.25f) * float(n); + } // detune (ratio) for (int i = 0; i < m; ++i) @@ -681,7 +683,7 @@ void sfz::Voice::setupOscillatorUnison() waveLeftGain[0] = 0.0; waveRightGain[m - 1] = 0.0; for (int i = 0; i < m - 1; ++i) { - float g = 1 - i / float(m - 1); + float g = 1.0f - float(i) / float(m - 1); waveLeftGain[m - 1 - i] = g; waveRightGain[i] = g; } From 0dca7dbc5e3603e17716b54b31682b24ad727e75 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 4 Apr 2020 23:14:15 +0200 Subject: [PATCH 4/6] Add tests for oscillator_multi --- tests/FilesT.cpp | 37 +++++++++++++++++++++++++++++ tests/TestFiles/channels_multi.sfz | 6 +++++ tests/TestFiles/ramp_wave.wav | Bin 0 -> 2092 bytes 3 files changed, 43 insertions(+) create mode 100644 tests/TestFiles/channels_multi.sfz create mode 100644 tests/TestFiles/ramp_wave.wav diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index f0a4db39..51f5e40f 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -259,6 +259,43 @@ TEST_CASE("[Files] Channels (channels.sfz)") REQUIRE(synth.getRegionView(1)->isStereo()); } +TEST_CASE("[Files] Channels (channels_multi.sfz)") +{ + sfz::Synth synth; + synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels_multi.sfz"); + REQUIRE(synth.getNumRegions() == 6); + + REQUIRE(synth.getRegionView(0)->sample == "*sine"); + REQUIRE(!synth.getRegionView(0)->isStereo()); + REQUIRE(synth.getRegionView(0)->isGenerator()); + REQUIRE(!synth.getRegionView(0)->oscillator); + + REQUIRE(synth.getRegionView(1)->sample == "*sine"); + REQUIRE(synth.getRegionView(1)->isStereo()); + REQUIRE(synth.getRegionView(1)->isGenerator()); + REQUIRE(!synth.getRegionView(1)->oscillator); + + REQUIRE(synth.getRegionView(2)->sample == "ramp_wave.wav"); + REQUIRE(!synth.getRegionView(2)->isStereo()); + REQUIRE(!synth.getRegionView(2)->isGenerator()); + REQUIRE(synth.getRegionView(2)->oscillator); + + REQUIRE(synth.getRegionView(3)->sample == "ramp_wave.wav"); + REQUIRE(synth.getRegionView(3)->isStereo()); + REQUIRE(!synth.getRegionView(3)->isGenerator()); + REQUIRE(synth.getRegionView(3)->oscillator); + + REQUIRE(synth.getRegionView(4)->sample == "*sine"); + REQUIRE(!synth.getRegionView(4)->isStereo()); + REQUIRE(synth.getRegionView(4)->isGenerator()); + REQUIRE(!synth.getRegionView(4)->oscillator); + + REQUIRE(synth.getRegionView(5)->sample == "*sine"); + REQUIRE(!synth.getRegionView(5)->isStereo()); + REQUIRE(synth.getRegionView(5)->isGenerator()); + REQUIRE(!synth.getRegionView(5)->oscillator); +} + TEST_CASE("[Files] sw_default") { sfz::Synth synth; diff --git a/tests/TestFiles/channels_multi.sfz b/tests/TestFiles/channels_multi.sfz new file mode 100644 index 00000000..8146ba45 --- /dev/null +++ b/tests/TestFiles/channels_multi.sfz @@ -0,0 +1,6 @@ + sample=*sine + sample=*sine oscillator_multi=3 + sample=ramp_wave.wav oscillator=on + sample=ramp_wave.wav oscillator=on oscillator_multi=3 + sample=*sine oscillator_multi=1 + sample=*sine oscillator_multi=2 diff --git a/tests/TestFiles/ramp_wave.wav b/tests/TestFiles/ramp_wave.wav new file mode 100644 index 0000000000000000000000000000000000000000..838d85566d14272034eddd1e3f4e87c2f451b747 GIT binary patch literal 2092 zcmWO6Q)=8FMFYwr$&-JK45v+f(0pJV-5I)KP90Z||TT8Br3!~aK+B14XZQN|cUTV40S`rFg45! zbHl`NHQWq$!^H?S!i;bu#7H&LjC3Q#C^gEAa-&3~iZqcfQjAuk&1g4Tj8S9E7&k_Y zRb$OqH&%>O&!a0POMk!&3d<9Y)~7_2Dd?MR2$7kw^3|T zo6IJ+No-b|&1Sb*Y*Aax7Pm!gRa?zgw^eLY+srn%O>9@&&33n4>`*(*4!1+>R6EU1 zw^Qs=yUZ@POYBy=&2G0_>`{Bn9=AvAReQ}|w^!^_`^-MKPwZFw&3?CE98d?$0e3)H zWsP+f7@3$E96ECB;2<8PgM1JU;UPN2hu|yJ~ zI3B0td>l^T2|B?i;3S@;lYA0R;VC-Br{FZ6rqg^H&fpn3!)M?uo~5&V7S7>0I>+bW zJf5fXd>$^~1-if&;38h6i+m9-;U&7nm*6s9rptU8uHY5A!dKubUZtyi6|Uhmy2jVw zI$o#id>wA!4Z6WM;3nRrn|u>);Vrtwx8OG3rrUfQ?%*A|!*}2=-le;I7w+Lby2tn6 zKHjJMd>;Vu6 z_&)?FNbr9gFbYNCC=eB+QdEu#(J&fC<7f~aqf>N_4lytW#o!na6Jt_LjtQ|a7RBON z5F2AtY>o|aFb>7xI1m@(Qe2J;@h~36<9HAs<5PT&4+$^6J_E|kQp;mX3h*j7(_uF1X(Z(W#KH46|+)S&I;Ku8)f5ckR7vAcFqnt zFbCz}9FP-pQclhZxiA;y;#`m$b5m~44S6sR<>5S#7xPkH&I|c4ALZkGkRS6?e$EdC zumBa{0#FbOQb8^Vg|HA6;zCdu3sYe(3`MXA72zUK6pK<(E(*o47!~7UP#lX>aV`!e zumqLh5>OILQb{ferLYv0;!;o=OH*ks4P~$lmEkf_7RypuE(;PRNtS>THCps2l~z`H zCClk@wwx~~%j@#Cye}^+=nA%iuOKVxingM!D1&vd4fesZlCESc`AV{~u52s&$}&WU z*bpBgtLQ4WimxK8>Z-P?uPUqQYPOoMCade}wz{t_Yv>xbhOZ%O>YBEuuPJNkTDF$2 zC2Q;2wzjV=>*zYRj;|x@>bkbBuPf{6dbXagC+q9_w!W_~8|VhMfo~u~b*K&Xp)yQ| z*)Sg_8|sF(p>HS~=|;AZZzLP*#vsc$Nq>1MW>Zzh}T=C-+S zE?ejpwuNsY!*#d~_u;aoZfRTkma>&@Wn1}HvbAn)Tl?0sjc#Mx_%^bwZfo26wz8dW zXWRL9vb}C^+xzyigYIBE_ztq8?r1yujPWIOpzva{}NJNwSEi|%5(_%5=m?rOXG zuCkl%X1n=rvb*kXyZi34hwfo}_#U#S?rD4ap0bzjWqbKvGD1h#2p=JP>)y7v?=Ac2 zKDLkVBm3&Uwy*Ci`{{nRpYJF8>;AUC?=J`F0d{~NAP4G!cAy_92kAj}kRK!m>%n%g RA1sIHA$Eu#BDL05{{!ji#B=}v literal 0 HcmV?d00001 From 75c898d50c63d33154ecd10b123692596bf28558 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 01:06:39 +0200 Subject: [PATCH 5/6] Added an external API for the external definitions --- src/sfizz.h | 19 +++++++++++++++++++ src/sfizz.hpp | 15 +++++++++++++++ src/sfizz/sfizz.cpp | 10 ++++++++++ src/sfizz/sfizz_wrapper.cpp | 12 ++++++++++++ 4 files changed, 56 insertions(+) diff --git a/src/sfizz.h b/src/sfizz.h index 5e5c6af8..36f6216d 100644 --- a/src/sfizz.h +++ b/src/sfizz.h @@ -394,6 +394,25 @@ SFIZZ_EXPORTED_API void sfizz_set_logging_prefix(sfizz_synth_t* synth, const cha */ SFIZZ_EXPORTED_API void sfizz_all_sound_off(sfizz_synth_t* synth); +/** + * @brief Add external definitions prior to loading; + * Note that these do not get reset by loading or resetting the synth. + * You need to call sfizz_clear_external_definitions() to erase them. + * + * @param synth + * @param id + * @param value + */ +SFIZZ_EXPORTED_API void sfizz_add_external_definitions(sfizz_synth_t* synth, const char* id, const char* value); + +/** + * @brief Clears external definitions for the next file loading. + * + * @param synth + */ +SFIZZ_EXPORTED_API void sfizz_clear_external_definitions(sfizz_synth_t* synth); + + #ifdef __cplusplus } #endif diff --git a/src/sfizz.hpp b/src/sfizz.hpp index d0e62e37..911b5eac 100644 --- a/src/sfizz.hpp +++ b/src/sfizz.hpp @@ -284,6 +284,21 @@ public: * */ void allSoundOff() noexcept; + + /** + * @brief Add external definitions prior to loading; + * Note that these do not get reset by loading or resetting the synth. + * You need to call clearExternalDefintions() to erase them. + * + * @param id + * @param value + */ + void addExternalDefinition(const std::string& id, const std::string& value); + /** + * @brief Clears external definitions for the next file loading. + * + */ + void clearExternalDefinitions(); private: std::unique_ptr synth; }; diff --git a/src/sfizz/sfizz.cpp b/src/sfizz/sfizz.cpp index 08f88c1a..3e17af01 100644 --- a/src/sfizz/sfizz.cpp +++ b/src/sfizz/sfizz.cpp @@ -210,3 +210,13 @@ void sfz::Sfizz::allSoundOff() noexcept { synth->allSoundOff(); } + +void sfz::Sfizz::addExternalDefinition(const std::string& id, const std::string& value) +{ + synth->getParser().addExternalDefinition(id, value); +} + +void sfz::Sfizz::clearExternalDefinitions() +{ + synth->getParser().clearExternalDefinitions(); +} diff --git a/src/sfizz/sfizz_wrapper.cpp b/src/sfizz/sfizz_wrapper.cpp index bc62c4c7..3c9d88f0 100644 --- a/src/sfizz/sfizz_wrapper.cpp +++ b/src/sfizz/sfizz_wrapper.cpp @@ -256,6 +256,18 @@ void sfizz_all_sound_off(sfizz_synth_t* synth) return self->allSoundOff(); } +void sfizz_add_external_definitions(sfizz_synth_t* synth, const char* id, const char* value) +{ + auto self = reinterpret_cast(synth); + self->getParser().addExternalDefinition(id, value); +} + +void sfizz_clear_external_definitions(sfizz_synth_t* synth) +{ + auto self = reinterpret_cast(synth); + self->getParser().clearExternalDefinitions(); +} + #ifdef __cplusplus } #endif From c4c4278ee393832cc5f0be82e36218204d9bdbc0 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 7 Apr 2020 16:12:29 +0200 Subject: [PATCH 6/6] Move the oscillator count to Config.h --- src/sfizz/Config.h | 1 + src/sfizz/Defaults.h | 2 +- src/sfizz/Voice.cpp | 4 +--- src/sfizz/Voice.h | 10 ++++------ 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index a71ad3db..727a125f 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -60,6 +60,7 @@ namespace config { constexpr int filtersInPool { maxVoices * 2 }; constexpr int filtersPerVoice { 2 }; constexpr int eqsPerVoice { 3 }; + constexpr int oscillatorsPerVoice { 9 }; constexpr float noiseVariance { 0.25f }; /** Minimum interval in frames between recomputations of coefficients of the diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 2066cb19..158da597 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -64,7 +64,7 @@ namespace Default constexpr float oscillatorPhase { 0.0 }; constexpr Range oscillatorPhaseRange { -1.0, 360.0 }; constexpr int oscillatorMulti { 1 }; - constexpr Range oscillatorMultiRange { 1, 9 }; + constexpr Range oscillatorMultiRange { 1, config::oscillatorsPerVoice }; constexpr float oscillatorDetune { 0 }; constexpr Range oscillatorDetuneRange { -9600, 9600 }; diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 4a9c6c76..251a1167 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -666,7 +666,7 @@ void sfz::Voice::setupOscillatorUnison() waveUnisonSize = m; // detune (cents) - float detunes[maxWaveOscillators]; + float detunes[config::oscillatorsPerVoice]; detunes[0] = 0.0; detunes[1] = -d; detunes[2] = +d; @@ -703,5 +703,3 @@ void sfz::Voice::setupOscillatorUnison() } #endif } - -constexpr unsigned sfz::Voice::maxWaveOscillators; diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index afde9b53..1723c416 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -288,15 +288,13 @@ private: ADSREnvelope egEnvelope; float bendStepFactor { centsFactor(1) }; - static constexpr unsigned maxWaveOscillators = Default::oscillatorMultiRange.getEnd(); - - WavetableOscillator waveOscillators[maxWaveOscillators]; + WavetableOscillator waveOscillators[config::oscillatorsPerVoice]; // unison of oscillators unsigned waveUnisonSize { 0 }; - float waveDetuneRatio[maxWaveOscillators] { }; - float waveLeftGain[maxWaveOscillators] { }; - float waveRightGain[maxWaveOscillators] { }; + float waveDetuneRatio[config::oscillatorsPerVoice] { }; + float waveLeftGain[config::oscillatorsPerVoice] { }; + float waveRightGain[config::oscillatorsPerVoice] { }; Duration dataDuration; Duration amplitudeDuration;