From cea3e4715beea6b755cf50e71dcd0ebee47ad203 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 3 Apr 2020 15:37:47 +0200 Subject: [PATCH 001/444] 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 0e35ca490..2066cb19a 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 b39b7c0d5..9f3395657 100644 --- a/src/sfizz/Range.h +++ b/src/sfizz/Range.h @@ -28,8 +28,8 @@ class Range { { } - 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 76806b48e..234878e93 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 a7a4e7105..73f3bc531 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 25393b4de..54d7fc12b 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 4aa4e0bd6..775d220d4 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 f0a70af90..afde9b53e 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -244,6 +244,10 @@ class Voice { 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 @@ class Voice { 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 c65b244a3..b04c0a8c0 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 6290f01f9..3d0c90399 100644 --- a/src/sfizz/Wavetables.h +++ b/src/sfizz/Wavetables.h @@ -47,12 +47,12 @@ class WavetableOscillator { /** 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 d5b9ba5ad..e573260bf 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 002/444] Update tests --- tests/FilesT.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index f4bdc3c44..f0a4db39a 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 003/444] 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 54d7fc12b..ec102de37 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 775d220d4..4a9c6c76f 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 7d4e9b144f5984d4bae002c787dd7a607e10d85f Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 4 Apr 2020 19:50:45 +0200 Subject: [PATCH 004/444] Pass the option -msse also on x86_64 In some cases of chroot-based debian build environments where the host is 64-bit and target 32-bit, the system processor of cmake is not correctly set. To work this around, pass the -msse2 flag also in case of 64-bit system processor; it will be harmless for the case of normal builds. --- cmake/SfizzConfig.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/SfizzConfig.cmake b/cmake/SfizzConfig.cmake index bbf9f43ee..77415fdf2 100644 --- a/cmake/SfizzConfig.cmake +++ b/cmake/SfizzConfig.cmake @@ -32,7 +32,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") add_compile_options(-Wextra) add_compile_options(-ffast-math) add_compile_options(-fno-omit-frame-pointer) # For debugging purposes - if (SFIZZ_SYSTEM_PROCESSOR MATCHES "^i.86$") + if (SFIZZ_SYSTEM_PROCESSOR MATCHES "^(i.86|x86_64)$") add_compile_options(-msse2) endif() elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") From 0dca7dbc5e3603e17716b54b31682b24ad727e75 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 4 Apr 2020 23:14:15 +0200 Subject: [PATCH 005/444] 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 f0a4db39a..51f5e40fb 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 000000000..8146ba45d --- /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 4e7f15328ac221d918977aa3a8f767d032d0b0f5 Mon Sep 17 00:00:00 2001 From: redtide Date: Mon, 6 Apr 2020 14:19:55 +0200 Subject: [PATCH 006/444] Auto update the api index page --- .travis/update_dox.sh | 4 +++- doxygen/scripts/generate_api_index.sh | 11 +++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.travis/update_dox.sh b/.travis/update_dox.sh index e838dc055..55ea08f4c 100755 --- a/.travis/update_dox.sh +++ b/.travis/update_dox.sh @@ -5,11 +5,13 @@ set -x # No fail, we need to go back to the original branch at the end mkdir build && cd build && cmake -DSFIZZ_JACK=OFF -DSFIZZ_SHARED=OFF -DSFIZZ_LV2=OFF .. && cd .. doxygen Doxyfile +./doxygen/scripts/generate_api_index.sh git fetch --depth=1 https://github.com/${TRAVIS_REPO_SLUG}.git refs/heads/gh-pages:refs/remotes/origin/gh-pages git checkout origin/gh-pages git checkout -b gh-pages mv _api api/${TRAVIS_TAG} -git add api/${TRAVIS_TAG} && git commit -m "Release ${TRAVIS_TAG} (Travis build: ${TRAVIS_BUILD_NUMBER})" +mv api_index.md api/index.md +git add api && git commit -m "Release ${TRAVIS_TAG} (Travis build: ${TRAVIS_BUILD_NUMBER})" git remote add origin-pages https://${GITHUB_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git > /dev/null 2>&1 git push --quiet --set-upstream origin-pages gh-pages git checkout ${TRAVIS_BRANCH} diff --git a/doxygen/scripts/generate_api_index.sh b/doxygen/scripts/generate_api_index.sh index 34a494d09..af618b6f2 100755 --- a/doxygen/scripts/generate_api_index.sh +++ b/doxygen/scripts/generate_api_index.sh @@ -1,12 +1,19 @@ #!/bin/bash # Must be called from the root directory -cat >>index.md <>api_index.md <> index.md + tag=$(echo "${tag}" | sed -r 's/v//g') + tags+=("${tag}") fi done +IFS=$'\n' tags=($(sort <<<"${tags[*]}")); unset IFS +for tag in "${tags[@]}"; do + echo "- [${tag}](${tag})" >> api_index.md +done From 75c898d50c63d33154ecd10b123692596bf28558 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 01:06:39 +0200 Subject: [PATCH 007/444] 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 5e5c6af87..36f6216d9 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 d0e62e37b..911b5eac7 100644 --- a/src/sfizz.hpp +++ b/src/sfizz.hpp @@ -284,6 +284,21 @@ class SFIZZ_EXPORTED_API Sfizz * */ 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 08f88c1ac..3e17af01f 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 bc62c4c74..3c9d88f09 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 008/444] 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 a71ad3db5..727a125fb 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 2066cb19a..158da5970 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 4a9c6c76f..251a11670 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 afde9b53e..1723c4165 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -288,15 +288,13 @@ class Voice { 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; From c2360bad454fa91901aae0c7e2ec8f81b53cc8c2 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 16:24:38 +0200 Subject: [PATCH 009/444] Feed master opcodes to the handleGroupOpcodes --- src/sfizz/Synth.cpp | 14 ++++++++++---- src/sfizz/Synth.h | 2 +- tests/SynthT.cpp | 22 ++++++++++++++++++++++ tests/TestFiles/master_polyphony.sfz | 7 +++++++ 4 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 tests/TestFiles/master_polyphony.sfz diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 25393b4de..7f4df9d7f 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -67,7 +67,7 @@ void sfz::Synth::onParseFullBlock(const std::string& header, const std::vector& members) } } -void sfz::Synth::handleGroupOpcodes(const std::vector& members) +void sfz::Synth::handleGroupOpcodes(const std::vector& members, const std::vector& masterMembers) { absl::optional groupIdx; unsigned maxPolyphony { config::maxVoices }; - for (auto& member : members) { + const auto parseOpcode = [&](const Opcode& member) { switch (member.lettersOnlyHash) { case hash("group"): setValueFromOpcode(member, groupIdx, Default::groupRange); @@ -191,7 +191,13 @@ void sfz::Synth::handleGroupOpcodes(const std::vector& members) setValueFromOpcode(member, maxPolyphony, Range(0, config::maxVoices)); break; } - } + }; + + for (auto& member : masterMembers) + parseOpcode(member); + + for (auto& member : members) + parseOpcode(member); if (groupIdx) setGroupPolyphony(*groupIdx, maxPolyphony); diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index a9ce64ab5..16017f3a8 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -454,7 +454,7 @@ class Synth final : public Parser::Listener { * * @param members the opcodes of the block */ - void handleGroupOpcodes(const std::vector& members); + void handleGroupOpcodes(const std::vector& members, const std::vector& masterMembers); /** * @brief Helper function to dispatch opcodes * diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 0434d9ce2..bd0d28cce 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -397,3 +397,25 @@ TEST_CASE("[Synth] Not self-masking") REQUIRE(synth.getVoiceView(2)->getTriggerValue() == 64_norm); REQUIRE(!synth.getVoiceView(2)->canBeStolen()); } + +TEST_CASE("[Synth] Polyphony in master") +{ + sfz::Synth synth; + synth.loadSfzFile(fs::current_path() / "tests/TestFiles/master_polyphony.sfz"); + synth.noteOn(0, 65, 64); + synth.noteOn(0, 65, 64); + synth.noteOn(0, 65, 64); + REQUIRE(synth.getNumActiveVoices() == 2); // group polyphony should block the last note + synth.allSoundOff(); + REQUIRE(synth.getNumActiveVoices() == 0); + synth.noteOn(0, 63, 64); + synth.noteOn(0, 63, 64); + synth.noteOn(0, 63, 64); + REQUIRE(synth.getNumActiveVoices() == 2); // group polyphony should block the last note + synth.allSoundOff(); + REQUIRE(synth.getNumActiveVoices() == 0); + synth.noteOn(0, 61, 64); + synth.noteOn(0, 61, 64); + synth.noteOn(0, 61, 64); + REQUIRE(synth.getNumActiveVoices() == 3); +} diff --git a/tests/TestFiles/master_polyphony.sfz b/tests/TestFiles/master_polyphony.sfz new file mode 100644 index 000000000..88d7bd7a9 --- /dev/null +++ b/tests/TestFiles/master_polyphony.sfz @@ -0,0 +1,7 @@ + polyphony=2 + group=2 + sample=*sine key=65 + group=3 + sample=*sine key=63 + // Empty master resets the polyphony + sample=*sine key=61 From b2a4f8a559bb08f3501432cf86cd1c69eca3f73f Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 16:26:00 +0200 Subject: [PATCH 010/444] global clears master and group, master clears group --- src/sfizz/Synth.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 7f4df9d7f..f9f9abd2a 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -55,6 +55,8 @@ void sfz::Synth::onParseFullBlock(const std::string& header, const std::vector Date: Tue, 7 Apr 2020 18:41:55 +0200 Subject: [PATCH 011/444] Update doxygen related files, added c++ API documentation --- doxygen/pages/index.md | 3 ++- doxygen/scripts/Doxyfile.in | 48 ++++++++++++++++++++----------------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/doxygen/pages/index.md b/doxygen/pages/index.md index 47aff24b1..a7e12b3f1 100644 --- a/doxygen/pages/index.md +++ b/doxygen/pages/index.md @@ -2,4 +2,5 @@ SFZ file format library -[public API](sfizz_8h.html) +- [public C API](sfizz_8h.html) +- [public C++ API](classsfz_1_1_sfizz.html) diff --git a/doxygen/scripts/Doxyfile.in b/doxygen/scripts/Doxyfile.in index 53dc1502b..0f7eb945d 100644 --- a/doxygen/scripts/Doxyfile.in +++ b/doxygen/scripts/Doxyfile.in @@ -1,4 +1,4 @@ -# Doxyfile 1.8.16 +# Doxyfile 1.8.17 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -251,8 +251,8 @@ TAB_SIZE = 4 # commands \{ and \} for these it is advised to use the version @{ and @} or use # a double escape (\\{ and \\}) -ALIASES = true=true \ - false=false +ALIASES = "true=true" \ + "false=false" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" @@ -300,7 +300,7 @@ OPTIMIZE_OUTPUT_SLICE = NO # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, # Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser @@ -515,7 +515,7 @@ EXTRACT_ANON_NSPACES = NO # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. -HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set @@ -523,11 +523,11 @@ HIDE_UNDOC_MEMBERS = YES # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. -HIDE_UNDOC_CLASSES = YES +HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO, these declarations will be -# included in the documentation. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO @@ -843,17 +843,20 @@ INPUT_ENCODING = UTF-8 # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, -# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen +# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.h \ - *.md + *.md \ + *.hpp # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. -RECURSIVE = YES +RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a @@ -862,12 +865,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = sfizz/ghc \ - sfizz/mathfuns \ - sfizz/atomicops.h \ - sfizz/MathHelpers.h \ - sfizz/readerwriterqueue.h \ - sfizz/ScopedFTZ.h +EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -1220,9 +1218,9 @@ HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that -# are dynamically created via Javascript. If disabled, the navigation index will +# are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML -# page. Disable this option to support browsers that do not have Javascript, +# page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1510,8 +1508,14 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# https://www.mathjax.org) which uses client side Javascript for the rendering +# https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path @@ -1581,7 +1585,7 @@ MATHJAX_CODEFILE = SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. There +# implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH # setting. When disabled, doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing From 33f9e6bf327a45ef7ae28e5edba5dd0b7728402c Mon Sep 17 00:00:00 2001 From: redtide Date: Tue, 7 Apr 2020 18:42:41 +0200 Subject: [PATCH 012/444] Updated API doxygen documentation --- src/sfizz.h | 222 +++++++++++++++++++++++--------------------------- src/sfizz.hpp | 158 ++++++++++++++++++----------------- 2 files changed, 187 insertions(+), 193 deletions(-) diff --git a/src/sfizz.h b/src/sfizz.h index 5e5c6af87..42585a9de 100644 --- a/src/sfizz.h +++ b/src/sfizz.h @@ -27,7 +27,13 @@ extern "C" { #endif +/** + * @brief Synth handle + */ typedef struct sfizz_synth_t sfizz_synth_t; +/** + * @brief Oversampling factor + */ typedef enum { SFIZZ_OVERSAMPLING_X1 = 1, SFIZZ_OVERSAMPLING_X2 = 2, @@ -40,14 +46,13 @@ typedef enum { * using sfizz_free(). The synth by default is set at 48 kHz * and a maximum block size of 1024. You should change these values * if they are not correct for your application. - * - * @return sfizz_synth_t* */ SFIZZ_EXPORTED_API sfizz_synth_t* sfizz_create_synth(); + /** * @brief Frees an existing sfizz synth. * - * @param synth The synth to destroy + * @param synth The synth to destroy. */ SFIZZ_EXPORTED_API void sfizz_free(sfizz_synth_t* synth); @@ -60,88 +65,76 @@ SFIZZ_EXPORTED_API void sfizz_free(sfizz_synth_t* synth); * @param path A null-terminated string representing a path to an SFZ * file. * - * @return true when file loading went OK. - * @return false if some error occured while loading. + * @return @true when file loading went OK, + * @false if some error occured while loading. */ SFIZZ_EXPORTED_API bool sfizz_load_file(sfizz_synth_t* synth, const char* path); /** - * @brief Returns the number of regions in the currently loaded SFZ file. + * @brief Return the number of regions in the currently loaded SFZ file. * - * @param synth The synth - * - * @return int the number of regions + * @param synth The synth. */ SFIZZ_EXPORTED_API int sfizz_get_num_regions(sfizz_synth_t* synth); /** - * @brief Returns the number of groups in the currently loaded SFZ file. - * - * @param synth The synth + * @brief Return the number of groups in the currently loaded SFZ file. * - * @return int the number of groups + * @param synth The synth. */ SFIZZ_EXPORTED_API int sfizz_get_num_groups(sfizz_synth_t* synth); /** - * @brief Returns the number of masters in the currently loaded SFZ file. - * - * @param synth The synth + * @brief Return the number of masters in the currently loaded SFZ file. * - * @return int the number of masters + * @param synth The synth. */ SFIZZ_EXPORTED_API int sfizz_get_num_masters(sfizz_synth_t* synth); /** - * @brief Returns the number of curves in the currently loaded SFZ file. + * @brief Return the number of curves in the currently loaded SFZ file. * - * @param synth The synth - * - * @return int the number of curves + * @param synth The synth. */ SFIZZ_EXPORTED_API int sfizz_get_num_curves(sfizz_synth_t* synth); /** * @brief Export a MIDI Name document describing the the currently loaded * SFZ file. * - * @param synth The synth - * @param model the model name used if a non-empty string, otherwise generated + * @param synth The synth. + * @param model The model name used if a non-empty string, otherwise generated. * - * @return char* a newly allocated XML string, which must be freed after use + * @return A newly allocated XML string, which must be freed after use. */ SFIZZ_EXPORTED_API char* sfizz_export_midnam(sfizz_synth_t* synth, const char* model); /** - * @brief Returns the number of preloaded samples for the current SFZ file. + * @brief Return the number of preloaded samples for the current SFZ file. * - * @param synth The synth - * - * @return int the number of preloaded samples + * @param synth The synth. */ SFIZZ_EXPORTED_API size_t sfizz_get_num_preloaded_samples(sfizz_synth_t* synth); /** - * @brief Returns the number of active voices. Note that this function is a + * @brief Return the number of active voices. Note that this function is a * basic indicator and does not aim to be perfect. In particular, it * runs on the calling thread so voices may well start or stop while * the function is checking which voice is active. * - * @param synth The synth - * - * @return size_t the number of playing voices + * @param synth The synth. */ SFIZZ_EXPORTED_API int sfizz_get_num_active_voices(sfizz_synth_t* synth); /** - * @brief Sets the expected number of samples per block. If unsure, give an + * @brief Set the expected number of samples per block. If unsure, give an * upper bound since right now ugly things may happen if you go over * this number. * - * @param synth The synth - * @param samples_per_block the number of samples per block + * @param synth The synth. + * @param samples_per_block The number of samples per block. */ SFIZZ_EXPORTED_API void sfizz_set_samples_per_block(sfizz_synth_t* synth, int samples_per_block); /** - * @brief Sets the sample rate for the synth. This is the output sample + * @brief Set the sample rate for the synth. This is the output sample * rate. This setting does not affect the internal processing. * * @param synth The synth - * @param sample_rate the sample rate + * @param sample_rate The sample rate. */ SFIZZ_EXPORTED_API void sfizz_set_sample_rate(sfizz_synth_t* synth, float sample_rate); @@ -150,10 +143,10 @@ SFIZZ_EXPORTED_API void sfizz_set_sample_rate(sfizz_synth_t* synth, float sample * needs to happen before the call to sfizz_render_block in each * block and should appear in order of the delays. * - * @param synth The synth - * @param delay the delay of the event in the block, in samples. - * @param note_number the MIDI note number - * @param velocity the MIDI velocity + * @param synth The synth. + * @param delay The delay of the event in the block, in samples. + * @param note_number The MIDI note number. + * @param velocity The MIDI velocity. */ SFIZZ_EXPORTED_API void sfizz_send_note_on(sfizz_synth_t* synth, int delay, int note_number, char velocity); @@ -164,10 +157,10 @@ SFIZZ_EXPORTED_API void sfizz_send_note_on(sfizz_synth_t* synth, int delay, int * As per the SFZ spec the velocity of note-off events is usually replaced by * the note-on velocity. * - * @param synth The synth - * @param delay the delay of the event in the block, in samples. - * @param note_number the MIDI note number - * @param velocity the MIDI velocity + * @param synth The synth. + * @param delay The delay of the event in the block, in samples. + * @param note_number The MIDI note number. + * @param velocity The MIDI velocity. */ SFIZZ_EXPORTED_API void sfizz_send_note_off(sfizz_synth_t* synth, int delay, int note_number, char velocity); @@ -176,38 +169,39 @@ SFIZZ_EXPORTED_API void sfizz_send_note_off(sfizz_synth_t* synth, int delay, int * to happen before the call to sfizz_render_block in each block and * should appear in order of the delays. * - * @param synth The synth - * @param delay the delay of the event in the block, in samples. - * @param cc_number the MIDI CC number - * @param cc_value the MIDI CC value + * @param synth The synth. + * @param delay The delay of the event in the block, in samples. + * @param cc_number The MIDI CC number. + * @param cc_value The MIDI CC value. */ SFIZZ_EXPORTED_API void sfizz_send_cc(sfizz_synth_t* synth, int delay, int cc_number, char cc_value); + /** * @brief Send a pitch wheel event. As with all MIDI events, this needs * to happen before the call to sfizz_render_block in each block and * should appear in order of the delays. * - * @param synth The synth - * @param delay The delay - * @param pitch The pitch + * @param synth The synth. + * @param delay The delay. + * @param pitch The pitch. */ SFIZZ_EXPORTED_API void sfizz_send_pitch_wheel(sfizz_synth_t* synth, int delay, int pitch); /** - * @brief Send an aftertouch event. (CURRENTLY UNIMPLEMENTED) + * @brief Send an aftertouch event. (CURRENTLY UNIMPLEMENTED) * - * @param synth - * @param delay - * @param aftertouch + * @param synth + * @param delay + * @param aftertouch */ SFIZZ_EXPORTED_API void sfizz_send_aftertouch(sfizz_synth_t* synth, int delay, char aftertouch); /** * @brief Send a tempo event. (CURRENTLY UNIMPLEMENTED) * - * @param synth The synth - * @param delay The delay - * @param seconds_per_quarter The seconds per quarter + * @param synth The synth. + * @param delay The delay. + * @param seconds_per_quarter The seconds per quarter. */ SFIZZ_EXPORTED_API void sfizz_send_tempo(sfizz_synth_t* synth, int delay, float seconds_per_quarter); @@ -219,11 +213,11 @@ SFIZZ_EXPORTED_API void sfizz_send_tempo(sfizz_synth_t* synth, int delay, float * block. The synth will memorize the inputs and render sample * accurates envelopes depending on the input events passed to it. * - * @param synth The synth - * @param channels pointers to the left and right channel of the - * output - * @param num_channels should be equal to 2 for the time being. - * @param num_frames number of frames to fill. This should be less than + * @param synth The synth. + * @param channels Pointers to the left and right channel of the + * output. + * @param num_channels Should be equal to 2 for the time being. + * @param num_frames Number of frames to fill. This should be less than * or equal to the expected samples_per_block. */ SFIZZ_EXPORTED_API void sfizz_render_block(sfizz_synth_t* synth, float** channels, int num_channels, int num_frames); @@ -232,18 +226,16 @@ SFIZZ_EXPORTED_API void sfizz_render_block(sfizz_synth_t* synth, float** channel * @brief Get the size of the preloaded data. This returns the number of * floats used in the preloading buffers. * - * @param synth The synth - * - * @return the preloaded data size in sizeof(floats) + * @param synth The synth. */ SFIZZ_EXPORTED_API unsigned int sfizz_get_preload_size(sfizz_synth_t* synth); /** - * @brief Sets the size of the preloaded data in number of floats (not + * @brief Set the size of the preloaded data in number of floats (not * bytes). This will disable the callbacks for the duration of the * load. * - * @param synth The synth - * @param[in] preload_size The preload size + * @param synth The synth. + * @param[in] preload_size The preload size. */ SFIZZ_EXPORTED_API void sfizz_set_preload_size(sfizz_synth_t* synth, unsigned int preload_size); @@ -252,9 +244,7 @@ SFIZZ_EXPORTED_API void sfizz_set_preload_size(sfizz_synth_t* synth, unsigned in * the engine, not the output or expected rate of the calling * function. For the latter use the `get_sample_rate()` functions. * - * @param synth The synth - * - * @return The internal sample rate of the engine + * @param synth The synth. */ SFIZZ_EXPORTED_API sfizz_oversampling_factor_t sfizz_get_oversampling_factor(sfizz_synth_t* synth); /** @@ -272,51 +262,46 @@ SFIZZ_EXPORTED_API sfizz_oversampling_factor_t sfizz_get_oversampling_factor(sfi * to compensate for the memory increase, but the full loading will * need to take place anyway. * - * @param synth The synth - * @param[in] preload_size The preload size + * @param synth The synth. + * @param[in] oversampling The oversampling factor. * - * @return True if the oversampling factor was correct + * @return @true if the oversampling factor was correct, @false otherwise. */ SFIZZ_EXPORTED_API bool sfizz_set_oversampling_factor(sfizz_synth_t* synth, sfizz_oversampling_factor_t oversampling); /** * @brief Set the global instrument volume. * - * @param synth The synth - * @param volume the new volume + * @param synth The synth. + * @param volume The new volume. */ SFIZZ_EXPORTED_API void sfizz_set_volume(sfizz_synth_t* synth, float volume); /** - * @brief Get the global instrument volume. - * - * @param synth The synth + * @brief Return the global instrument volume. * - * @return float the instrument volume + * @param synth The synth. */ SFIZZ_EXPORTED_API float sfizz_get_volume(sfizz_synth_t* synth); /** - * @brief Sets the number of voices used by the synth + * @brief Set the number of voices used by the synth. * - * @param synth The synth - * @param num_voices The number voices + * @param synth The synth. + * @param num_voices The number of voices. */ SFIZZ_EXPORTED_API void sfizz_set_num_voices(sfizz_synth_t* synth, int num_voices); /** - * @brief Returns the number of voices + * @brief Return the number of voices. * - * @param synth - * @return num_voices + * @param synth The synth. */ SFIZZ_EXPORTED_API int sfizz_get_num_voices(sfizz_synth_t* synth); /** - * @brief Get the number of allocated buffers from the synth. + * @brief Return the number of allocated buffers from the synth. * - * @param synth The synth - * - * @return The number of buffers held by the synth + * @param synth The synth. */ SFIZZ_EXPORTED_API int sfizz_get_num_buffers(sfizz_synth_t* synth); /** @@ -324,73 +309,70 @@ SFIZZ_EXPORTED_API int sfizz_get_num_buffers(sfizz_synth_t* synth); * value can be less than the actual memory usage since it only * counts the buffer objects managed by sfizz. * - * @param synth The synth - * - * @return The number of bytes held by the synth in buffers; + * @param synth The synth. */ SFIZZ_EXPORTED_API int sfizz_get_num_bytes(sfizz_synth_t* synth); /** - * @brief Enables freewheeling on the synth. + * @brief Enable freewheeling on the synth. * - * @param synth + * @param synth The synth. */ SFIZZ_EXPORTED_API void sfizz_enable_freewheeling(sfizz_synth_t* synth); /** - * @brief Disables freewheeling on the synth. + * @brief Disable freewheeling on the synth. * - * @param synth + * @param synth The synth. */ SFIZZ_EXPORTED_API void sfizz_disable_freewheeling(sfizz_synth_t* synth); /** - * @brief Get a comma separated list of unknown opcodes. The caller has to free() - * the string returned. This function allocates memory, do not call on the - * audio thread. + * @brief Return a comma separated list of unknown opcodes. + * The caller has to free() the string returned. + * This function allocates memory, do not call on the audio thread. * - * @param synth - * @return char* + * @param synth The synth. */ SFIZZ_EXPORTED_API char* sfizz_get_unknown_opcodes(sfizz_synth_t* synth); /** - * @brief Check if the SFZ should be reloaded. + * @brief Check if the SFZ should be reloaded. + * Depending on the platform this can create file descriptors. * - * Depending on the platform this can create file descriptors. + * @param synth The synth. * - * @param synth - * @return true if any included files (including the root file) have - * been modified since the sfz file was loaded. - * @return false + * @return @true if any included files (including the root file) have + * been modified since the sfz file was loaded, @false otherwise. */ SFIZZ_EXPORTED_API bool sfizz_should_reload_file(sfizz_synth_t* synth); /** - * @brief Enable logging of timings to sidecar CSV files. This can produce - * many outputs so use with caution. + * @brief Enable logging of timings to sidecar CSV files. This can produce + * many outputs so use with caution. * - * @param synth + * @param synth The synth. */ SFIZZ_EXPORTED_API void sfizz_enable_logging(sfizz_synth_t* synth); /** - * @brief Disable logging + * @brief Disable logging. * - * @param synth + * @param synth The synth. */ SFIZZ_EXPORTED_API void sfizz_disable_logging(sfizz_synth_t* synth); /** - * @brief Enable logging of timings to sidecar CSV files. This can produce - * many outputs so use with caution. + * @brief Enable logging of timings to sidecar CSV files. This can produce + * many outputs so use with caution. * - * @param synth + * @param synth The synth. + * @param prefix The prefix. */ SFIZZ_EXPORTED_API void sfizz_set_logging_prefix(sfizz_synth_t* synth, const char* prefix); /** - * @brief Shuts down the current processing, clear buffers and reset the voices. + * @brief Shuts down the current processing, clear buffers and reset the voices. * - * @param synth + * @param synth The synth. */ SFIZZ_EXPORTED_API void sfizz_all_sound_off(sfizz_synth_t* synth); diff --git a/src/sfizz.hpp b/src/sfizz.hpp index d0e62e37b..b2af98c19 100644 --- a/src/sfizz.hpp +++ b/src/sfizz.hpp @@ -4,6 +4,11 @@ // license. You should have receive a LICENSE.md file along with the code. // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz +/** + @file + @brief sfizz public C++ API +*/ + #include #include #include @@ -21,6 +26,9 @@ namespace sfz { class Synth; +/** + * @brief Main class + */ class SFIZZ_EXPORTED_API Sfizz { public: @@ -32,6 +40,7 @@ class SFIZZ_EXPORTED_API Sfizz */ Sfizz(); ~Sfizz(); + /** * @brief Empties the current regions and load a new SFZ file into the synth. * @@ -39,68 +48,70 @@ class SFIZZ_EXPORTED_API Sfizz * UI thread for example, although it may generate a click. However it is * not reentrant, so you should not call it from concurrent threads. * - * @param file - * @return true - * @return false if the file was not found or no regions were loaded. + * @param path The path to the file to load, as string. + * + * @return @false if the file was not found or no regions were loaded, + * @true otherwise. */ bool loadSfzFile(const std::string& path); + /** - * @brief Get the current number of regions loaded - * - * @return int + * @brief Return the current number of regions loaded. */ int getNumRegions() const noexcept; + /** - * @brief Get the current number of groups loaded - * - * @return int + * @brief Return the current number of groups loaded. */ int getNumGroups() const noexcept; + /** - * @brief Get the current number of masters loaded - * - * @return int + * @brief Return the current number of masters loaded. */ int getNumMasters() const noexcept; + /** - * @brief Get the current number of curves loaded - * - * @return int + * @brief Return the current number of curves loaded. */ int getNumCurves() const noexcept; + + /** + * @brief Return a list of unsupported opcodes, if any. + */ const std::vector& getUnknownOpcodes() const noexcept; + /** - * @brief Get the number of preloaded samples in the synth - * - * @return size_t + * @brief Return the number of preloaded samples in the synth. */ size_t getNumPreloadedSamples() const noexcept; + /** * @brief Set the maximum size of the blocks for the callback. The actual * size can be lower in each callback but should not be larger * than this value. * - * @param samplesPerBlock + * @param samplesPerBlock The number of samples per block. */ void setSamplesPerBlock(int samplesPerBlock) noexcept; + /** * @brief Set the sample rate. If you do not call it it is initialized * to sfz::config::defaultSampleRate. * - * @param sampleRate + * @param sampleRate The sample rate. */ void setSampleRate(float sampleRate) noexcept; + /** - * @brief Get the current value for the volume, in dB. - * - * @return float + * @brief Return the current value for the volume, in dB. */ float getVolume() const noexcept; - /** + + /** * @brief Set the value for the volume. This value will be * clamped within sfz::default::volumeRange. * - * @param volume + * @param volume The new volume. */ void setVolume(float volume) noexcept; @@ -109,125 +120,122 @@ class SFIZZ_EXPORTED_API Sfizz * * @param delay the delay at which the event occurs; this should be lower * than the size of the block in the next call to renderBlock(). - * @param noteNumber the midi note number - * @param velocity the midi note velocity + * @param noteNumber the midi note number. + * @param velocity the midi note velocity. */ void noteOn(int delay, int noteNumber, uint8_t velocity) noexcept; + /** * @brief Send a note off event to the synth * * @param delay the delay at which the event occurs; this should be lower * than the size of the block in the next call to renderBlock(). - * @param noteNumber the midi note number - * @param velocity the midi note velocity + * @param noteNumber the midi note number. + * @param velocity the midi note velocity. */ void noteOff(int delay, int noteNumber, uint8_t velocity) noexcept; + /** * @brief Send a CC event to the synth * * @param delay the delay at which the event occurs; this should be lower than the size of * the block in the next call to renderBlock(). - * @param ccNumber the cc number - * @param ccValue the cc value + * @param ccNumber the cc number. + * @param ccValue the cc value. */ void cc(int delay, int ccNumber, uint8_t ccValue) noexcept; + /** * @brief Send a pitch bend event to the synth * * @param delay the delay at which the event occurs; this should be lower * than the size of the block in the next call to * renderBlock(). - * @param pitch the pitch value centered between -8192 and 8192 + * @param pitch the pitch value centered between -8192 and 8192. */ void pitchWheel(int delay, int pitch) noexcept; + /** - * @brief Send a aftertouch event to the synth + * @brief Send a aftertouch event to the synth. (CURRENTLY UNIMPLEMENTED) * * @param delay the delay at which the event occurs; this should be lower than the size of * the block in the next call to renderBlock(). - * @param aftertouch the aftertouch value + * @param aftertouch the aftertouch value. */ void aftertouch(int delay, uint8_t aftertouch) noexcept; + /** - * @brief Send a tempo event to the synth + * @brief Send a tempo event to the synth. (CURRENTLY UNIMPLEMENTED) * * @param delay the delay at which the event occurs; this should be lower than the size of * the block in the next call to renderBlock(). - * @param secondsPerQuarter the new period of the quarter note + * @param secondsPerQuarter the new period of the quarter note. */ void tempo(int delay, float secondsPerQuarter) noexcept; + /** * @brief Render an block of audio data in the buffer. This call will reset * the synth in its waiting state for the next batch of events. The buffers must * be float[numSamples][numOutputs * 2]. * - * @param buffers the buffers to write the next block into - * @param numSamples the number of stereo frames in the block - * @param numOutputs the number of stereo outputs + * @param buffers the buffers to write the next block into. + * @param numFrames the number of stereo frames in the block. + * @param numOutputs the number of stereo outputs. */ void renderBlock(float** buffers, size_t numFrames, int numOutputs = 1) noexcept; /** - * @brief Get the number of active voices - * - * @return int + * @brief Return the number of active voices. */ int getNumActiveVoices() const noexcept; + /** - * @brief Get the total number of voices in the synth (the polyphony) - * - * @return int + * @brief Return the total number of voices in the synth (the polyphony). */ int getNumVoices() const noexcept; + /** - * @brief Change the number of voices (the polyphony) + * @brief Change the number of voices (the polyphony). * - * @param numVoices + * @param numVoices The number of voices. */ void setNumVoices(int numVoices) noexcept; - /** + /** * @brief Set the oversampling factor to a new value. This will disable all callbacks * kill all the voices, and trigger a reloading of every file in the FilePool under * the new oversampling. * - * @param factor - * @return true if the factor did indeed change + * @param factor The oversampling factor. + * + * @return @true if the factor did indeed change, @false otherwise. */ bool setOversamplingFactor(int factor) noexcept; /** - * @brief get the current oversampling factor - * - * @return Oversampling + * @brief Return the current oversampling factor. */ int getOversamplingFactor() const noexcept; /** * @brief Set the preloaded file size. This will disable the callback. * - * @param factor + * @param preloadSize The preload size. */ void setPreloadSize(uint32_t preloadSize) noexcept; /** - * @brief get the current preloaded file size - * - * @return Oversampling + * @brief Return the current preloaded file size. */ uint32_t getPreloadSize() const noexcept; /** - * @brief Gets the number of allocated buffers. - * - * @return The allocated buffers. + * @brief Return the number of allocated buffers. */ int getAllocatedBuffers() const noexcept; /** - * @brief Gets the number of bytes allocated through the buffers - * - * @return The allocated bytes. + * @brief Return the number of bytes allocated through the buffers. */ int getAllocatedBytes() const noexcept; @@ -235,55 +243,59 @@ class SFIZZ_EXPORTED_API Sfizz * @brief Enable freewheeling on the synth. This will wait for background * loaded files to finish loading before each render callback to ensure that * there will be no dropouts. - * */ void enableFreeWheeling() noexcept; + /** * @brief Disable freewheeling on the synth. You should disable freewheeling * before live use of the plugin otherwise the audio thread will lock. * */ void disableFreeWheeling() noexcept; + /** * @brief Check if the SFZ should be reloaded. * * Depending on the platform this can create file descriptors. * - * @return true if any included files (including the root file) have - * been modified since the sfz file was loaded. - * @return false + * @return @true if any included files (including the root file) have + * been modified since the sfz file was loaded, @false otherwise. */ bool shouldReloadFile(); + /** * @brief Enable logging of timings to sidecar CSV files. This can produce * many outputs so use with caution. * - * @param prefix the file prefix to use for logging + * @param prefix the file prefix to use for logging. */ void enableLogging() noexcept; + /** * @brief Enable logging of timings to sidecar CSV files. This can produce * many outputs so use with caution. * - * @param prefix the file prefix to use for logging + * @param prefix the file prefix to use for logging. */ void enableLogging(const std::string& prefix) noexcept; + /** - * @brief Set the logging prefix + * @brief Set the logging prefix. * * @param prefix */ void setLoggingPrefix(const std::string& prefix) noexcept; + /** - * @brief Disable logging; - * + * @brief Disable logging. */ void disableLogging() noexcept; + /** * @brief Shuts down the current processing, clear buffers and reset the voices. - * */ void allSoundOff() noexcept; + private: std::unique_ptr synth; }; From 819e4da275594ad64738021a615bf4068ac28a12 Mon Sep 17 00:00:00 2001 From: redtide Date: Wed, 8 Apr 2020 12:09:40 +0200 Subject: [PATCH 013/444] Try to reflect site navigation in Doxygen pages header --- doxygen/layout/custom_footer.html | 2 +- doxygen/layout/custom_header.html | 113 ++++++++++++++++++++++++------ 2 files changed, 92 insertions(+), 23 deletions(-) diff --git a/doxygen/layout/custom_footer.html b/doxygen/layout/custom_footer.html index 016704967..15ed43273 100644 --- a/doxygen/layout/custom_footer.html +++ b/doxygen/layout/custom_footer.html @@ -6,7 +6,7 @@ target="_new">Doxygen $doxygenversion

- + diff --git a/doxygen/layout/custom_header.html b/doxygen/layout/custom_header.html index 866b5a66d..bf5a07f63 100644 --- a/doxygen/layout/custom_header.html +++ b/doxygen/layout/custom_header.html @@ -26,16 +26,14 @@ content="#da532c"> - - - - + + + $treeview $search $mathjax - + $extrastylesheet - @@ -44,36 +42,107 @@ -
- Skip to content +
-
+
From ce2dd39a51cdaf35d4d92a8b637fdb9e98815aa0 Mon Sep 17 00:00:00 2001 From: redtide Date: Wed, 8 Apr 2020 18:14:13 +0200 Subject: [PATCH 014/444] Typo [ci-skip] --- doxygen/layout/custom_footer.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doxygen/layout/custom_footer.html b/doxygen/layout/custom_footer.html index 15ed43273..6f98db557 100644 --- a/doxygen/layout/custom_footer.html +++ b/doxygen/layout/custom_footer.html @@ -6,7 +6,7 @@ target="_new">Doxygen $doxygenversion

- +
From 8b711780d72f9ae1ea828d373d330f40baf3062f Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 12 Apr 2020 01:33:09 +0200 Subject: [PATCH 015/444] Update the base system of MinGW docker The current upstream seems to need an update of the base libs in order to run the MinGW compiler. --- .travis/install_mingw.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/install_mingw.sh b/.travis/install_mingw.sh index f97ee3d84..882b5d52c 100755 --- a/.travis/install_mingw.sh +++ b/.travis/install_mingw.sh @@ -3,6 +3,6 @@ set -ex . .travis/docker_container.sh -buildenv_as_root pacman -Sqy --noconfirm +buildenv_as_root pacman -Sqyu --noconfirm buildenv_as_root pacman -Sq --noconfirm base-devel wget mingw-w64-cmake mingw-w64-gcc mingw-w64-pkg-config mingw-w64-libsndfile buildenv i686-w64-mingw32-gcc -v && buildenv i686-w64-mingw32-g++ -v && buildenv i686-w64-mingw32-cmake --version From 9613c0ed57345927966f3a7207c6382d1d5c19b3 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 20:23:13 +0200 Subject: [PATCH 016/444] Added a nan/inf checker to the fp traits --- src/sfizz/ADSREnvelope.cpp | 2 ++ src/sfizz/MathHelpers.h | 34 ++++++++++++++++++++++++++++++++++ tests/FloatHelpersT.cpp | 17 +++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index 9581ab7af..d2f3dafff 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -188,6 +188,8 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept this->currentValue = currentValue; this->shouldRelease = shouldRelease; this->releaseDelay = releaseDelay; + + ASSERT(!hasNanInf(output)); } template diff --git a/src/sfizz/MathHelpers.h b/src/sfizz/MathHelpers.h index 960ccb733..7d7aae6e1 100644 --- a/src/sfizz/MathHelpers.h +++ b/src/sfizz/MathHelpers.h @@ -11,6 +11,7 @@ #pragma once #include "Config.h" #include "Macros.h" +#include "absl/types/span.h" #include #include #include @@ -292,3 +293,36 @@ inline F fp_from_parts(bool sgn, int ex, uint64_t mant) (static_cast(sgn) << (T::e_bits + T::m_bits)); return u.real; } + + +template +inline bool fp_naninf(F x) +{ + typedef FP_traits T; + typedef typename T::same_size_int I; + union { F real; I integer; } u; + u.real = x; + const auto all_ones = ((1u << T::e_bits) - 1); + const auto ex = (u.integer >> T::m_bits) & all_ones; + return ex == all_ones; +} + +template +bool hasNanInf(absl::Span span) +{ + for (const auto& x: span) + if (fp_naninf(x)) + return true; + + return false; +} + +template +bool isValidAudio(absl::Span span) +{ + for (const auto& x: span) + if (x < -1.0f || x > 1.0f) + return false; + + return true; +} diff --git a/tests/FloatHelpersT.cpp b/tests/FloatHelpersT.cpp index 58b92acc3..ecfc0cf00 100644 --- a/tests/FloatHelpersT.cpp +++ b/tests/FloatHelpersT.cpp @@ -7,6 +7,7 @@ #include "catch2/catch.hpp" #include "sfizz/MathHelpers.h" #include +#include TEST_CASE("[FloatMath] Fast ilog2 (float)") { @@ -51,3 +52,19 @@ TEST_CASE("[FloatMath] Break apart and reconstruct (double)") REQUIRE(fp_from_parts(sgn, ex, mant.num) == f); } } + +TEST_CASE("[FloatMath] Nan/Inf checker") +{ + REQUIRE(fp_naninf(std::numeric_limits::quiet_NaN())); + REQUIRE(fp_naninf(std::numeric_limits::quiet_NaN())); + REQUIRE(fp_naninf(std::numeric_limits::infinity())); + REQUIRE(fp_naninf(std::numeric_limits::infinity())); + REQUIRE(fp_naninf(-std::numeric_limits::infinity())); + REQUIRE(fp_naninf(-std::numeric_limits::infinity())); + REQUIRE(!fp_naninf(0.0f)); + REQUIRE(!fp_naninf(0.0)); + REQUIRE(!fp_naninf(1.0f)); + REQUIRE(!fp_naninf(1.0)); + REQUIRE(!fp_naninf(-1.0f)); + REQUIRE(!fp_naninf(-1.0)); +} From c820fbc591eb69e8e429a12f9ed33f21db761f56 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 22:01:37 +0200 Subject: [PATCH 017/444] Added sanity checks in Voice (disabled) and Synth --- src/sfizz/Synth.cpp | 5 +++++ src/sfizz/Voice.cpp | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 52dfba4ca..814f5e5e3 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -645,6 +645,11 @@ void sfz::Synth::renderBlock(AudioSpan buffer) noexcept // Reset the dispatch counter dispatchDuration = Duration(0); + + ASSERT(!hasNanInf(buffer.getConstSpan(0))); + ASSERT(!hasNanInf(buffer.getConstSpan(1))); + ASSERT(isValidAudio(buffer.getConstSpan(0))); + ASSERT(isValidAudio(buffer.getConstSpan(1))); } void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 251a11670..fcb1d9fd8 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -241,6 +241,12 @@ void sfz::Voice::renderBlock(AudioSpan buffer) noexcept powerHistory.push(buffer.meanSquared()); this->triggerDelay = absl::nullopt; +#if 0 + ASSERT(!hasNanInf(buffer.getConstSpan(0))); + ASSERT(!hasNanInf(buffer.getConstSpan(1))); + ASSERT(isValidAudio(buffer.getConstSpan(0))); + ASSERT(isValidAudio(buffer.getConstSpan(1))); +#endif } void sfz::Voice::amplitudeEnvelope(absl::Span modulationSpan) noexcept @@ -515,6 +521,13 @@ void sfz::Voice::fillWithData(AudioSpan buffer) noexcept sourcePosition = indices->back(); floatPositionOffset = rightCoeffs->back(); + +#if 0 + ASSERT(!hasNanInf(buffer.getConstSpan(0))); + ASSERT(!hasNanInf(buffer.getConstSpan(1))); + ASSERT(isValidAudio(buffer.getConstSpan(0))); + ASSERT(isValidAudio(buffer.getConstSpan(1))); +#endif } void sfz::Voice::fillWithGenerator(AudioSpan buffer) noexcept @@ -573,6 +586,13 @@ void sfz::Voice::fillWithGenerator(AudioSpan buffer) noexcept } } } + +#if 0 + ASSERT(!hasNanInf(buffer.getConstSpan(0))); + ASSERT(!hasNanInf(buffer.getConstSpan(1))); + ASSERT(isValidAudio(buffer.getConstSpan(0))); + ASSERT(isValidAudio(buffer.getConstSpan(1))); +#endif } bool sfz::Voice::checkOffGroup(int delay, uint32_t group) noexcept From d294d1e3e08ebd577e8a24dfbee4ee9d0ee38b56 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 22:02:16 +0200 Subject: [PATCH 018/444] Corrected a bug were the silence wavetable was not initalized with 0 --- src/sfizz/Wavetables.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sfizz/Wavetables.cpp b/src/sfizz/Wavetables.cpp index b04c0a8c0..e7a3f3dad 100644 --- a/src/sfizz/Wavetables.cpp +++ b/src/sfizz/Wavetables.cpp @@ -253,8 +253,17 @@ WavetableMulti WavetableMulti::createForHarmonicProfile( const WavetableMulti* WavetableMulti::getSilenceWavetable() { static WavetableMulti wm; - wm.allocateStorage(1); - wm.fillExtra(); + static bool initialized { false }; + + if (!initialized) { + constexpr unsigned numTables = WavetableMulti::numTables(); + wm.allocateStorage(1); + for (unsigned m = 0; m < numTables; ++m) { + float* ptr = const_cast(wm.getTablePointer(m)); + *ptr = 0; + } + wm.fillExtra(); + } return &wm; } From 9a1582892f3838b193db1010383b4944ba66d70a Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 8 Apr 2020 09:16:18 +0200 Subject: [PATCH 019/444] Flip the initialized switch on the silence wavetable --- src/sfizz/Wavetables.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sfizz/Wavetables.cpp b/src/sfizz/Wavetables.cpp index e7a3f3dad..617d39018 100644 --- a/src/sfizz/Wavetables.cpp +++ b/src/sfizz/Wavetables.cpp @@ -263,6 +263,7 @@ const WavetableMulti* WavetableMulti::getSilenceWavetable() *ptr = 0; } wm.fillExtra(); + initialized = true; } return &wm; } From 09bad92cff81db5ed912b47e0520a140bb9dcab1 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 12 Apr 2020 10:30:36 +0200 Subject: [PATCH 020/444] Added a check that a region is a generator for free-running the envelope --- src/sfizz/ADSREnvelope.cpp | 9 +++------ src/sfizz/MathHelpers.h | 16 +++++++++------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index d2f3dafff..bb86115bc 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -40,11 +40,9 @@ void ADSREnvelope::reset(const EGDescription& desc, const Region& region, releaseDelay = 0; shouldRelease = false; - freeRunning = ( - (region.trigger == SfzTrigger::release) + freeRunning = ((region.trigger == SfzTrigger::release) || (region.trigger == SfzTrigger::release_key) - || region.loopMode == SfzLoopMode::one_shot - ); + || (region.loopMode == SfzLoopMode::one_shot && (region.isGenerator() || region.oscillator))); currentValue = this->start; currentState = State::Delay; } @@ -117,8 +115,7 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept // release takes effect this frame currentState = State::Release; releaseDelay = -1; - } - else if (shouldRelease && releaseDelay > 0) { + } else if (shouldRelease && releaseDelay > 0) { // prevent computing the segment further than release point size = std::min(size, releaseDelay); } diff --git a/src/sfizz/MathHelpers.h b/src/sfizz/MathHelpers.h index 7d7aae6e1..9fa734f12 100644 --- a/src/sfizz/MathHelpers.h +++ b/src/sfizz/MathHelpers.h @@ -294,33 +294,35 @@ inline F fp_from_parts(bool sgn, int ex, uint64_t mant) return u.real; } - -template +template inline bool fp_naninf(F x) { typedef FP_traits T; typedef typename T::same_size_int I; - union { F real; I integer; } u; + union { + F real; + I integer; + } u; u.real = x; const auto all_ones = ((1u << T::e_bits) - 1); const auto ex = (u.integer >> T::m_bits) & all_ones; return ex == all_ones; } -template +template bool hasNanInf(absl::Span span) { - for (const auto& x: span) + for (const auto& x : span) if (fp_naninf(x)) return true; return false; } -template +template bool isValidAudio(absl::Span span) { - for (const auto& x: span) + for (const auto& x : span) if (x < -1.0f || x > 1.0f) return false; From 6b36054a91dff3e8259e17ef8573e8679b10e1f7 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 13 Apr 2020 00:59:31 +0200 Subject: [PATCH 021/444] Update travis deployment to v2 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6753d573d..646de2446 100644 --- a/.travis.yml +++ b/.travis.yml @@ -174,8 +174,8 @@ after_failure: - bash ${TRAVIS_BUILD_DIR}/.travis/discord_webhook.sh failure deploy: + edge: true provider: releases - skip_cleanup: true file: "${INSTALL_DIR}.tar.gz" token: secure: jBIMWMPva8OazC8Wnqc2vqATa3exjvGDGvejEu6fgwt/BMDkgJCsJ7gS3ITpYFuO0HoHtLGfTVrFcZIE+agGVRdRGRni5xZRp5Vuf9hvLf8hCTaxkS/HYoliYYPp9FLY6J4yq9mp/R/QyGrwgssQEjhYlI2GL4+lJzt750AcmYibnQo2AMe5SSOp3q8KdLBG1vca1o1BZWBhW/S7l2hA1aJ7y1ytwm0ETIdrTZHxgfz7bqdqQcw/Ytqjm1rgzty6iQKX3B/InaZNB6CqXy6FoGLf771lery8nWJbjTjKYD4QK2NelExldyp1vrdHGvknWguFk+8vlQ16Dt0+R7Byr/LOPRLTCf/T+IehMQGtVA9/gjrkH8LqHy9oVFB+33p11gGu3h6hvg7yB6z3sSck0u4FHjrlpNd5XmmCsKQVQ9vI1cPPkVkbMHIewc7uOu+bD6cmotFj1vJ9UYesvN6n4siyCiPOIhgV9++bjcyiLqX3DWP5UWyZ9/VT8bz5VcxUdJnEYtdNPx4x5pRW4081VUjIf2EmHhQTrjb1iz6FAGwNU/fpefA8x+bBxXP9MkFFgU0tbCDKw2y6o0GOXX82mZr/IB0/DIBg7UllTzCBKTuiJQV1prFPlZLc6V22H5ozAXHgu3E+qNjvHoMsoYjGssX3+AYHwu8isBINo/gGo6Y= From 11569378abe23d464322f75812de4811e7e48948 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 13 Apr 2020 01:15:53 +0200 Subject: [PATCH 022/444] Fix the arm64 test CI --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6753d573d..83722930f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ jobs: script: .travis/script_test.sh - name: "Linux arm64 tests" - arch: amd64 + arch: arm64 addons: apt: packages: From 5bd80b4834ebfb18f45090db57ce0ea18615566f Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 13 Apr 2020 02:08:35 +0200 Subject: [PATCH 023/444] Try alternative curve formula --- src/sfizz/Curve.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sfizz/Curve.cpp b/src/sfizz/Curve.cpp index b29fbf155..9ad569594 100644 --- a/src/sfizz/Curve.cpp +++ b/src/sfizz/Curve.cpp @@ -79,20 +79,20 @@ Curve Curve::buildPredefinedCurve(int index) break; case 4: for (unsigned i = 0; i < NumValues; ++i) { - double x = i * (1.0 / (NumValues - 1)); + double x = i / static_cast(NumValues - 1); curve._points[i] = x * x; } break; case 5: for (unsigned i = 0; i < NumValues; ++i) { - double x = i * (1.0 / (NumValues - 1)); - curve._points[i] = std::pow(x, 0.5); + double x = i / static_cast(NumValues - 1); + curve._points[i] = std::sqrt(x); } break; case 6: for (unsigned i = 0; i < NumValues; ++i) { - double x = i * (1.0 / (NumValues - 1)); - curve._points[i] = std::pow(1.0 - x, 0.5); + double x = i / static_cast(NumValues - 1); + curve._points[i] = std::sqrt(1.0 - x); } break; } From 12e9cf88962c9cd68d2b80164ce370b725636031 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 13 Apr 2020 02:18:20 +0200 Subject: [PATCH 024/444] Ensure the extreme values of xfade curves are exact --- src/sfizz/Curve.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sfizz/Curve.cpp b/src/sfizz/Curve.cpp index 9ad569594..44aa3c24d 100644 --- a/src/sfizz/Curve.cpp +++ b/src/sfizz/Curve.cpp @@ -84,13 +84,17 @@ Curve Curve::buildPredefinedCurve(int index) } break; case 5: - for (unsigned i = 0; i < NumValues; ++i) { + curve._points[0] = 0.0; + curve._points[NumValues - 1] = 1.0; + for (unsigned i = 1; i < NumValues - 1; ++i) { double x = i / static_cast(NumValues - 1); curve._points[i] = std::sqrt(x); } break; case 6: - for (unsigned i = 0; i < NumValues; ++i) { + curve._points[0] = 1.0; + curve._points[NumValues - 1] = 0.0; + for (unsigned i = 1; i < NumValues - 1; ++i) { double x = i / static_cast(NumValues - 1); curve._points[i] = std::sqrt(1.0 - x); } From 151d9d3f7d67ee1d1d6b8bfed06f979bfb519a7e Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 13 Apr 2020 15:30:07 +0200 Subject: [PATCH 025/444] Fix libatomic detection on clang 10 compiler --- cmake/SfizzConfig.cmake | 13 ++++++++++++- src/CMakeLists.txt | 13 ++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/cmake/SfizzConfig.cmake b/cmake/SfizzConfig.cmake index 77415fdf2..c4c607afe 100644 --- a/cmake/SfizzConfig.cmake +++ b/cmake/SfizzConfig.cmake @@ -78,8 +78,18 @@ add_library(sfizz-spline STATIC "src/external/spline/spline/spline.cpp") target_include_directories(sfizz-spline PUBLIC "src/external/spline") include (CheckLibraryExists) +add_library (sfizz-atomic INTERFACE) if (UNIX AND NOT APPLE) - check_library_exists(atomic __atomic_load "" LIBATOMIC_FOUND) + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/check_libatomic") + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/check_libatomic/check_libatomic.c" "int main() { return 0; }") + try_compile(SFIZZ_LINK_LIBATOMIC "${CMAKE_CURRENT_BINARY_DIR}/check_libatomic" + SOURCES "${CMAKE_CURRENT_BINARY_DIR}/check_libatomic/check_libatomic.c" + LINK_LIBRARIES "atomic") + if (SFIZZ_LINK_LIBATOMIC) + target_link_libraries (sfizz-atomic INTERFACE "atomic") + endif() +else() + set(SFIZZ_LINK_LIBATOMIC FALSE) endif() # Don't show build information when building a different project @@ -98,6 +108,7 @@ Build benchmarks: ${SFIZZ_BENCHMARKS} Build tests: ${SFIZZ_TESTS} Use vcpkg: ${SFIZZ_USE_VCPKG} Statically link libsndfile: ${SFIZZ_STATIC_LIBSNDFILE} +Link libatomic: ${SFIZZ_LINK_LIBATOMIC} Install prefix: ${CMAKE_INSTALL_PREFIX} LV2 destination directory: ${LV2PLUGIN_INSTALL_DIR} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b3d472df0..bd044a455 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -55,7 +55,7 @@ target_sources(sfizz_static PRIVATE ${SFIZZ_SOURCES} sfizz/sfizz_wrapper.cpp sfi target_include_directories (sfizz_static PUBLIC .) target_include_directories (sfizz_static PUBLIC external) target_link_libraries (sfizz_static PUBLIC absl::strings absl::span) -target_link_libraries (sfizz_static PRIVATE sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-kissfft sfizz-cpuid) +target_link_libraries (sfizz_static PRIVATE sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-kissfft sfizz-cpuid sfizz-atomic) set_target_properties (sfizz_static PROPERTIES OUTPUT_NAME sfizz PUBLIC_HEADER "sfizz.h;sfizz.hpp") if (WIN32) target_compile_definitions (sfizz_static PRIVATE _USE_MATH_DEFINES) @@ -80,9 +80,6 @@ configure_file (${PROJECT_SOURCE_DIR}/doxygen/scripts/Doxyfile.in ${PROJECT_SOUR add_library (sfizz::parser ALIAS sfizz_parser) add_library (sfizz::sfizz ALIAS sfizz_static) -if (LIBATOMIC_FOUND) - target_link_libraries (sfizz_static PRIVATE atomic) -endif() # Shared library and installation target if (SFIZZ_SHARED) @@ -90,7 +87,7 @@ if (SFIZZ_SHARED) target_sources(sfizz_shared PRIVATE ${SFIZZ_SOURCES} sfizz/sfizz_wrapper.cpp sfizz/sfizz.cpp) target_include_directories (sfizz_shared PRIVATE .) target_include_directories (sfizz_shared PRIVATE external) - target_link_libraries (sfizz_shared PRIVATE absl::strings absl::span sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-kissfft sfizz-cpuid) + target_link_libraries (sfizz_shared PRIVATE absl::strings absl::span sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-kissfft sfizz-cpuid sfizz-atomic) if (WIN32) target_compile_definitions (sfizz_shared PRIVATE _USE_MATH_DEFINES) endif() @@ -104,10 +101,4 @@ if (SFIZZ_SHARED) ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() - - if (UNIX AND NOT APPLE) - if (LIBATOMIC_FOUND) - target_link_libraries (sfizz_shared PRIVATE atomic) - endif() - endif() endif() From cbc037e055c285573a11359d28e84f7b5bc050a8 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 13 Apr 2020 00:33:16 +0200 Subject: [PATCH 026/444] Add RTSemaphore and tests --- src/CMakeLists.txt | 1 + src/sfizz/RTSemaphore.cpp | 254 ++++++++++++++++++++++++++++++++++++++ src/sfizz/RTSemaphore.h | 50 ++++++++ tests/CMakeLists.txt | 1 + tests/SemaphoreT.cpp | 68 ++++++++++ 5 files changed, 374 insertions(+) create mode 100644 src/sfizz/RTSemaphore.cpp create mode 100644 src/sfizz/RTSemaphore.h create mode 100644 tests/SemaphoreT.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b3d472df0..aceb11654 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,6 +19,7 @@ set (SFIZZ_SOURCES sfizz/SfzFilter.cpp sfizz/Curve.cpp sfizz/Wavetables.cpp + sfizz/RTSemaphore.cpp sfizz/Effects.cpp sfizz/effects/Nothing.cpp sfizz/effects/Filter.cpp diff --git a/src/sfizz/RTSemaphore.cpp b/src/sfizz/RTSemaphore.cpp new file mode 100644 index 000000000..7a3340d5d --- /dev/null +++ b/src/sfizz/RTSemaphore.cpp @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "RTSemaphore.h" +#include +#include +#include + +RTSemaphore::RTSemaphore(unsigned value) +{ + std::error_code ec; + init(ec, value); + if (ec) + throw std::system_error(ec); + good_ = true; +} + +RTSemaphore::RTSemaphore(std::error_code &ec, unsigned value) noexcept +{ + init(ec, value); + good_ = ec ? false : true; +} + +RTSemaphore::~RTSemaphore() noexcept +{ + if (good_) { + std::error_code ec; + destroy(ec); + } +} + +void RTSemaphore::post() +{ + std::error_code ec; + post(ec); + if (ec) + throw std::system_error(ec); +} + +void RTSemaphore::wait() +{ + std::error_code ec; + wait(ec); + if (ec) + throw std::system_error(ec); +} + +bool RTSemaphore::try_wait() +{ + std::error_code ec; + bool b = try_wait(ec); + if (ec) + throw std::system_error(ec); + return b; +} + +#if defined(__APPLE__) +void RTSemaphore::init(std::error_code &ec, unsigned value) +{ + ec.clear(); + kern_return_t ret = semaphore_create(mach_task_self(), &sem_, SYNC_POLICY_FIFO, value); + if (ret != KERN_SUCCESS) + ec = std::error_code(ret, mach_category()); +} + +void RTSemaphore::destroy(std::error_code &ec) +{ + ec.clear(); + kern_return_t ret = semaphore_destroy(mach_task_self(), sem_); + if (ret != KERN_SUCCESS) + ec = std::error_code(ret, mach_category()); +} + +void RTSemaphore::post(std::error_code &ec) noexcept +{ + ec.clear(); + kern_return_t ret = semaphore_signal(sem_); + if (ret != KERN_SUCCESS) + ec = std::error_code(ret, mach_category()); +} + +void RTSemaphore::wait(std::error_code &ec) noexcept +{ + ec.clear(); + do { + kern_return_t ret = semaphore_wait(sem_); + switch (ret) { + case KERN_SUCCESS: + return; + case KERN_ABORTED: + break; + default: + ec = std::error_code(ret, mach_category()); + return; + } + } while (1); +} + +bool RTSemaphore::try_wait(std::error_code &ec) noexcept +{ + ec.clear(); + do { + const mach_timespec_t timeout = {0, 0}; + kern_return_t ret = semaphore_timedwait(sem_, timeout); + switch (ret) { + case KERN_SUCCESS: + return true; + case KERN_OPERATION_TIMED_OUT: + return false; + case KERN_ABORTED: + break; + default: + ec = std::error_code(ret, mach_category()); + return false; + } + } while (1); +} + +const std::error_category &RTSemaphore::mach_category() +{ + class mach_category : public std::error_category { + public: + const char *name() const noexcept override + { + return "kern_return_t"; + } + + std::string message(int condition) const override + { + const char *str = mach_error_string(condition); + return str ? str : ""; + } + }; + + static const mach_category cat; + return cat; +} +#elif defined(_WIN32) +void RTSemaphore::init(std::error_code &ec, unsigned value) +{ + ec.clear(); + sem_ = CreateSemaphore(nullptr, value, LONG_MAX, nullptr); + if (!sem_) + ec = std::error_code(GetLastError(), std::system_category()); +} + +void RTSemaphore::destroy(std::error_code &ec) +{ + ec.clear(); + if (CloseHandle(sem_) == 0) + ec = std::error_code(GetLastError(), std::system_category()); +} + +void RTSemaphore::post(std::error_code &ec) noexcept +{ + ec.clear(); + if (ReleaseSemaphore(sem_, 1, nullptr) == 0) + ec = std::error_code(GetLastError(), std::system_category()); +} + +void RTSemaphore::wait(std::error_code &ec) noexcept +{ + ec.clear(); + DWORD ret = WaitForSingleObject(sem_, INFINITE); + switch (ret) { + case WAIT_OBJECT_0: + return; + case WAIT_FAILED: + ec = std::error_code(GetLastError(), std::system_category()); + return; + default: + ec = std::error_code(ret, std::system_category()); + return; + } +} + +bool RTSemaphore::try_wait(std::error_code &ec) noexcept +{ + ec.clear(); + DWORD ret = WaitForSingleObject(sem_, 0); + switch (ret) { + case WAIT_OBJECT_0: + return true; + case WAIT_TIMEOUT: + return false; + case WAIT_FAILED: + ec = std::error_code(GetLastError(), std::system_category()); + return false; + default: + ec = std::error_code(ret, std::system_category()); + return false; + } +} +#else +void RTSemaphore::init(std::error_code &ec, unsigned value) +{ + ec.clear(); + if (sem_init(&sem_, 0, value) != 0) + ec = std::error_code(errno, std::generic_category()); +} + +void RTSemaphore::destroy(std::error_code &ec) +{ + ec.clear(); + if (sem_destroy(&sem_) != 0) + ec = std::error_code(errno, std::generic_category()); +} + +void RTSemaphore::post(std::error_code &ec) noexcept +{ + ec.clear(); + while (sem_post(&sem_) != 0) { + int e = errno; + if (e != EINTR) { + ec = std::error_code(e, std::generic_category()); + return; + } + } +} + +void RTSemaphore::wait(std::error_code &ec) noexcept +{ + ec.clear(); + while (sem_wait(&sem_) != 0) { + int e = errno; + if (e != EINTR) { + ec = std::error_code(e, std::generic_category()); + return; + } + } +} + +bool RTSemaphore::try_wait(std::error_code &ec) noexcept +{ + ec.clear(); + do { + if (sem_trywait(&sem_) == 0) + return true; + int e = errno; + switch (e) { + case EINTR: + break; + case EAGAIN: + return false; + default: + ec = std::error_code(e, std::generic_category()); + return false; + } + } while (1); +} +#endif diff --git a/src/sfizz/RTSemaphore.h b/src/sfizz/RTSemaphore.h new file mode 100644 index 000000000..4ce011126 --- /dev/null +++ b/src/sfizz/RTSemaphore.h @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#if defined(__APPLE__) +#include +#elif defined(_WIN32) +#include +#else +#include +#endif +#include + +class RTSemaphore { +public: + explicit RTSemaphore(unsigned value = 0); + explicit RTSemaphore(std::error_code &ec, unsigned value = 0) noexcept; + ~RTSemaphore() noexcept; + + RTSemaphore(const RTSemaphore &) = delete; + RTSemaphore &operator=(const RTSemaphore &) = delete; + + explicit operator bool() const noexcept { return good_; } + + void post(); + void wait(); + bool try_wait(); + + void post(std::error_code &ec) noexcept; + void wait(std::error_code &ec) noexcept; + bool try_wait(std::error_code &ec) noexcept; + +private: + void init(std::error_code &ec, unsigned value); + void destroy(std::error_code &ec); + +private: +#if defined(__APPLE__) + semaphore_t sem_ {}; + static const std::error_category &mach_category(); +#elif defined(_WIN32) + HANDLE sem_ {}; +#else + sem_t sem_ {}; +#endif + bool good_ {}; +}; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 423c867ec..345a052b7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -29,6 +29,7 @@ set(SFIZZ_TEST_SOURCES RegionTriggersT.cpp FloatHelpersT.cpp WavetablesT.cpp + SemaphoreT.cpp ) add_executable(sfizz_tests ${SFIZZ_TEST_SOURCES}) diff --git a/tests/SemaphoreT.cpp b/tests/SemaphoreT.cpp new file mode 100644 index 000000000..cc4b22c26 --- /dev/null +++ b/tests/SemaphoreT.cpp @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "sfizz/RTSemaphore.h" +#include "catch2/catch.hpp" +#include + +TEST_CASE("[Semaphore] Basic operations") +{ + RTSemaphore sem; + + REQUIRE(sem.try_wait() == false); + + sem.post(); + REQUIRE(sem.try_wait() == true); + REQUIRE(sem.try_wait() == false); + + sem.post(); + sem.post(); + REQUIRE(sem.try_wait() == true); + REQUIRE(sem.try_wait() == true); + REQUIRE(sem.try_wait() == false); + + sem.post(); + sem.post(); + sem.wait(); + sem.wait(); + + REQUIRE(sem.try_wait() == false); +} + +TEST_CASE("[Semaphore] Counter initialization") +{ + RTSemaphore sem(3); + + REQUIRE(sem.try_wait() == true); + REQUIRE(sem.try_wait() == true); + REQUIRE(sem.try_wait() == true); + REQUIRE(sem.try_wait() == false); +} + +TEST_CASE("[Semaphore] Thread synchronization") +{ + RTSemaphore sem1; + RTSemaphore sem2; + constexpr int n = 1000; + + std::thread t1([&]() { + for (int i = 0; i < n; ++i) { + sem1.post(); + sem2.wait(); + } + }); + std::thread t2([&]() { + for (int i = 0; i < n; ++i) { + sem2.post(); + sem1.wait(); + } + }); + + t1.join(); + t2.join(); + REQUIRE(sem1.try_wait() == false); + REQUIRE(sem2.try_wait() == false); +} From 54b92f9f9d575b0368cc5409e84feee49c9798c3 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 13 Apr 2020 17:14:04 +0200 Subject: [PATCH 027/444] Try fixing the case-sensitivity test on Mac --- tests/FilesT.cpp | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 51f5e40fb..75941298e 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -8,6 +8,9 @@ #include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" #include "ghc/fs_std.hpp" +#if defined(__APPLE__) +#include // pathconf +#endif using namespace Catch::literals; using namespace sfz::literals; @@ -529,15 +532,26 @@ TEST_CASE("[Files] Looped regions taken from files and possibly overriden") TEST_CASE("[Files] Case sentitiveness") { -#ifndef WIN32 - sfz::Synth synth; - synth.loadSfzFile(fs::current_path() / "tests/TestFiles/case_insensitive.sfz"); - REQUIRE(synth.getNumRegions() == 4); - REQUIRE(synth.getRegionView(0)->sample == "dummy1.wav"); - REQUIRE(synth.getRegionView(1)->sample == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(2)->sample == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(3)->sample == "Regions/dummy.wav"); + const fs::path sfzFilePath = fs::current_path() / + "tests/TestFiles/case_insensitive.sfz"; + +#if defined(_WIN32) + const bool caseSensitiveFs = false; +#elif defined(__APPLE__) + const bool caseSensitiveFs = pathconf(sfzFilePath.string().c_str(), _PC_CASE_SENSITIVE) != 0; +#else + const bool caseSensitiveFs = true; #endif + + if (caseSensitiveFs) { + sfz::Synth synth; + synth.loadSfzFile(sfzFilePath); + REQUIRE(synth.getNumRegions() == 4); + REQUIRE(synth.getRegionView(0)->sample == "dummy1.wav"); + REQUIRE(synth.getRegionView(1)->sample == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(2)->sample == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(3)->sample == "Regions/dummy.wav"); + } } TEST_CASE("[Files] Empty file") From 99cb921b2c04b177a54aa764a993594a653ef8f2 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 13 Apr 2020 17:35:37 +0200 Subject: [PATCH 028/444] Make VST use the RTSemaphore from sfizz --- vst/RTSemaphore.h | 167 ---------------------------------------- vst/SfizzVstProcessor.h | 2 +- 2 files changed, 1 insertion(+), 168 deletions(-) delete mode 100644 vst/RTSemaphore.h diff --git a/vst/RTSemaphore.h b/vst/RTSemaphore.h deleted file mode 100644 index 5ec3c765f..000000000 --- a/vst/RTSemaphore.h +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright Jean Pierre Cimalando 2018-2020. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#pragma once -#if defined(__APPLE__) -#include -#elif defined(_WIN32) -#include -#include -#else -#include -#include -#endif -#include - -class RTSemaphore { -public: - explicit RTSemaphore(unsigned value = 0); - ~RTSemaphore(); - - RTSemaphore(const RTSemaphore &) = delete; - RTSemaphore &operator=(const RTSemaphore &) = delete; - - void post(); - void wait(); - bool try_wait(); - -private: -#if defined(__APPLE__) - semaphore_t sem_; -#elif defined(_WIN32) - HANDLE sem_; -#else - sem_t sem_; -#endif -}; - -#if defined(__APPLE__) -inline RTSemaphore::RTSemaphore(unsigned value) -{ - if (semaphore_create(mach_task_self(), &sem_, SYNC_POLICY_FIFO, value) != 0) - throw std::runtime_error("RTSemaphore::RTSemaphore"); -} - -inline RTSemaphore::~RTSemaphore() -{ - semaphore_destroy(mach_task_self(), sem_); -} - -inline void RTSemaphore::post() -{ - if (semaphore_signal(sem_) != KERN_SUCCESS) - throw std::runtime_error("RTSemaphore::post"); -} - -inline void RTSemaphore::wait() -{ - do { - switch (semaphore_wait(sem_)) { - case KERN_SUCCESS: - return; - case KERN_ABORTED: - break; - default: - throw std::runtime_error("RTSemaphore::wait"); - } - } while (1); -} - -inline bool RTSemaphore::try_wait() -{ - do { - const mach_timespec_t timeout = {0, 0}; - switch (semaphore_timedwait(sem_, timeout)) { - case KERN_SUCCESS: - return true; - case KERN_OPERATION_TIMED_OUT: - return false; - case KERN_ABORTED: - break; - default: - throw std::runtime_error("RTSemaphore::try_wait"); - } - } while (1); -} -#elif defined(_WIN32) -inline RTSemaphore::RTSemaphore(unsigned value) -{ - sem_ = CreateSemaphore(nullptr, value, LONG_MAX, nullptr); - if (!sem_) - throw std::runtime_error("RTSemaphore::RTSemaphore"); -} - -inline RTSemaphore::~RTSemaphore() -{ - CloseHandle(sem_); -} - -inline void RTSemaphore::post() -{ - if (!ReleaseSemaphore(sem_, 1, nullptr)) - throw std::runtime_error("RTSemaphore::post"); -} - -inline void RTSemaphore::wait() -{ - if (WaitForSingleObject(sem_, INFINITE) != WAIT_OBJECT_0) - throw std::runtime_error("RTSemaphore::wait"); -} - -inline bool RTSemaphore::try_wait() -{ - switch (WaitForSingleObject(sem_, 0)) { - case WAIT_OBJECT_0: - return true; - case WAIT_TIMEOUT: - return false; - default: - throw std::runtime_error("RTSemaphore::try_wait"); - } -} -#else -inline RTSemaphore::RTSemaphore(unsigned value) -{ - if (sem_init(&sem_, 0, value) != 0) - throw std::runtime_error("RTSemaphore::RTSemaphore"); -} - -inline RTSemaphore::~RTSemaphore() -{ - sem_destroy(&sem_); -} - -inline void RTSemaphore::post() -{ - while (sem_post(&sem_) != 0) { - if (errno != EINTR) - throw std::runtime_error("RTSemaphore::post"); - } -} - -inline void RTSemaphore::wait() -{ - while (sem_wait(&sem_) != 0) { - if (errno != EINTR) - throw std::runtime_error("RTSemaphore::wait"); - } -} - -inline bool RTSemaphore::try_wait() -{ - do { - if (sem_trywait(&sem_) == 0) - return true; - switch (errno) { - case EINTR: - break; - case EAGAIN: - return false; - default: - throw std::runtime_error("RTSemaphore::try_wait"); - } - } while (1); -} -#endif diff --git a/vst/SfizzVstProcessor.h b/vst/SfizzVstProcessor.h index 1ac718270..1ed7fb37b 100644 --- a/vst/SfizzVstProcessor.h +++ b/vst/SfizzVstProcessor.h @@ -6,7 +6,7 @@ #pragma once #include "SfizzVstState.h" -#include "RTSemaphore.h" +#include "sfizz/RTSemaphore.h" #include "ring_buffer/ring_buffer.h" #include "public.sdk/source/vst/vstaudioeffect.h" #include From 5726fd92d1d2e405a77fc13b9b9149378a1f7021 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 14:35:28 +0200 Subject: [PATCH 029/444] Replace AtomicGuards by mutexes with try_lock --- src/sfizz/AtomicGuard.h | 114 --------------------------------------- src/sfizz/Defer.h | 22 ++++++++ src/sfizz/EQPool.cpp | 11 ++-- src/sfizz/EQPool.h | 4 +- src/sfizz/FilePool.cpp | 12 ++--- src/sfizz/FilePool.h | 4 +- src/sfizz/FilterPool.cpp | 11 ++-- src/sfizz/FilterPool.h | 4 +- src/sfizz/Synth.cpp | 78 ++++++++------------------- src/sfizz/Synth.h | 21 ++++---- 10 files changed, 74 insertions(+), 207 deletions(-) delete mode 100644 src/sfizz/AtomicGuard.h create mode 100644 src/sfizz/Defer.h diff --git a/src/sfizz/AtomicGuard.h b/src/sfizz/AtomicGuard.h deleted file mode 100644 index 16d1f4dde..000000000 --- a/src/sfizz/AtomicGuard.h +++ /dev/null @@ -1,114 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause - -// This code is part of the sfizz library and is licensed under a BSD 2-clause -// license. You should have receive a LICENSE.md file along with the code. -// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz - -/** - * @brief This file contains a pair of RAII helpers that handle some form - * of lock-free mutex-type protection adapter to audio applications where you have 1 priority thread - * that should never block and would rather return silence than wait, and another low-priority - * thread that handles long computations. - * - * @code{.cpp} - * - * // Somewhere in a class... - * std::atomic canEnterCallback; - * std::atomic inCallback; - * - * void functionThatSuspendsCallback() - * { - * AtomicDisabler callbackDisabler { canEnterCallback }; - * - * while (inCallback) { - * std::this_thread::sleep_for(1ms); - * } - * - * // Do your thing. - * } - * - * void callback(int samplesPerBlock) noexcept - * { - * AtomicGuard callbackGuard { inCallback }; - * if (!canEnterCallback) - * return; - * - * // Do your thing. - * } - * @endcode - * There are probably many ways to improve these and probably even debug them. - * The spinlocking itself could be integrated in the constructor, although the - * check for return in the callback could not. - */ -#include - -namespace sfz -{ -/** - * @brief Simple class to set an atomic to true and automatically set it back to false on - * destruction. - * - * You call it like this assuming you need indicate that you are in e.g. a callback - * @code{.cpp} - * void functionToProtect() - * { - * AtomicGuard { guard }; - * - * // Do stuff, the atomic will be set back to false as soon as you're back - * } - * @endcode - * Note that this is not thread-safe at all, in the sense that it is only meant to be - * used with 2 threads along with the AtomicDisabler. One thread uses AtomicGuards, the other - * AtomicDisablers, and no other contending thread can share this pair of atomics. - */ -class AtomicGuard -{ -public: - AtomicGuard() = delete; - AtomicGuard(std::atomic& guard) - : guard(guard) - { - guard = true; - } - ~AtomicGuard() - { - guard = false; - } -private: - std::atomic& guard; -}; - -/** - * @brief Simple class to set an atomic to false and automatically set it back to true on - * destruction. - * - * You call it like this assuming you need to disable e.g. a callback - * @code{.cpp} - * void functionThatDisableAnotherFunction() - * { - * AtomicDisabler { disabler }; - * - * // Do stuff, the atomic will be set back to true as soon as you're back - * } - * @endcode - * Note that this is not thread-safe at all, in the sense that it is only meant to be - * used with 2 threads along with the AtomicGuard. One thread uses AtomicGuards, the other - * AtomicDisabler, and no other contending thread can share this pair of atomics. - */ -class AtomicDisabler -{ -public: - AtomicDisabler() = delete; - AtomicDisabler(std::atomic& allowed) - : allowed(allowed) - { - allowed = false; - } - ~AtomicDisabler() - { - allowed = true; - } -private: - std::atomic& allowed; -}; -} diff --git a/src/sfizz/Defer.h b/src/sfizz/Defer.h new file mode 100644 index 000000000..087ca8d69 --- /dev/null +++ b/src/sfizz/Defer.h @@ -0,0 +1,22 @@ +#pragma once + +// From https://stackoverflow.com/questions/48117908/is-the-a-practical-way-to-emulate-go-language-defer-in-c-or-c-destructors +#include +#include + +template +struct deferred +{ + std::decay_t f; + template + deferred(G&& g) : f{std::forward(g)} {} + ~deferred() { f(); } +}; + +template +deferred(G&&) -> deferred; + +#define CAT_(x, y) x##y +#define CAT(x, y) CAT_(x, y) +#define ANONYMOUS_VAR(x) CAT(x, __LINE__) +#define DEFER deferred ANONYMOUS_VAR(defer_variable) = [&] diff --git a/src/sfizz/EQPool.cpp b/src/sfizz/EQPool.cpp index 515f26ef1..574c5fd07 100644 --- a/src/sfizz/EQPool.cpp +++ b/src/sfizz/EQPool.cpp @@ -1,5 +1,5 @@ #include "EQPool.h" -#include "AtomicGuard.h" +#include "Defer.h" #include #include "absl/algorithm/container.h" #include "SIMDHelpers.h" @@ -108,9 +108,9 @@ sfz::EQPool::EQPool(const MidiState& state, int numEQs) sfz::EQHolderPtr sfz::EQPool::getEQ(const EQDescription& description, unsigned numChannels, float velocity) { - AtomicGuard guard { givingOutEQs }; - if (!canGiveOutEQs) + if (!eqGuard.try_lock()) return {}; + DEFER { eqGuard.unlock(); }; auto eq = absl::c_find_if(eqs, [](const EQHolderPtr& holder) { return holder.use_count() == 1; @@ -132,10 +132,7 @@ size_t sfz::EQPool::getActiveEQs() const size_t sfz::EQPool::setnumEQs(size_t numEQs) { - AtomicDisabler disabler { canGiveOutEQs }; - - while(givingOutEQs) - std::this_thread::sleep_for(std::chrono::milliseconds(1)); + const std::lock_guard eqLock { eqGuard }; auto eqIterator = eqs.begin(); auto eqSentinel = eqs.rbegin(); diff --git a/src/sfizz/EQPool.h b/src/sfizz/EQPool.h index 9890c10a5..7c70af224 100644 --- a/src/sfizz/EQPool.h +++ b/src/sfizz/EQPool.h @@ -4,6 +4,7 @@ #include "MidiState.h" #include #include +#include namespace sfz { @@ -114,8 +115,7 @@ class EQPool */ void setSampleRate(float sampleRate); private: - std::atomic givingOutEQs { false }; - std::atomic canGiveOutEQs { true }; + std::mutex eqGuard; float sampleRate { config::defaultSampleRate }; const MidiState& midiState; std::vector eqs; diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index 9b43a2bef..0fac55917 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -29,7 +29,7 @@ #include "Config.h" #include "Debug.h" #include "Oversampler.h" -#include "AtomicGuard.h" +#include "Defer.h" #include "absl/types/span.h" #include "absl/strings/match.h" #include "absl/memory/memory.h" @@ -301,10 +301,7 @@ void sfz::FilePool::setPreloadSize(uint32_t preloadSize) noexcept void sfz::FilePool::tryToClearPromises() { - AtomicDisabler disabler { canAddPromisesToClear }; - - while (addingPromisesToClear) - std::this_thread::sleep_for(std::chrono::milliseconds(1)); + const std::lock_guard promiseLock { promiseGuard }; for (auto& promise: promisesToClear) { if (promise->dataStatus != FilePromise::DataStatus::Wait) @@ -376,10 +373,9 @@ void sfz::FilePool::clear() void sfz::FilePool::cleanupPromises() noexcept { - AtomicGuard guard { addingPromisesToClear }; - - if (!canAddPromisesToClear) + if (!promiseGuard.try_lock()) return; + DEFER { promiseGuard.unlock(); }; // The garbage collection cleared the data from these so we can move them // back to the empty queue diff --git a/src/sfizz/FilePool.h b/src/sfizz/FilePool.h index 450a4c240..50939dbbd 100644 --- a/src/sfizz/FilePool.h +++ b/src/sfizz/FilePool.h @@ -38,6 +38,7 @@ #include "Logger.h" #include #include +#include namespace sfz { using AudioBufferPtr = std::shared_ptr>; @@ -264,8 +265,7 @@ class FilePool { std::vector emptyPromises; std::vector temporaryFilePromises; std::vector promisesToClear; - std::atomic addingPromisesToClear { false }; - std::atomic canAddPromisesToClear { true }; + std::mutex promiseGuard; // Preloaded data absl::flat_hash_map preloadedFiles; diff --git a/src/sfizz/FilterPool.cpp b/src/sfizz/FilterPool.cpp index 062e4e95d..74e185f34 100644 --- a/src/sfizz/FilterPool.cpp +++ b/src/sfizz/FilterPool.cpp @@ -1,7 +1,7 @@ #include "FilterPool.h" #include "SIMDHelpers.h" #include "absl/algorithm/container.h" -#include "AtomicGuard.h" +#include "Defer.h" #include #include @@ -109,9 +109,9 @@ sfz::FilterPool::FilterPool(const MidiState& state, int numFilters) sfz::FilterHolderPtr sfz::FilterPool::getFilter(const FilterDescription& description, unsigned numChannels, int noteNumber, float velocity) { - AtomicGuard guard { givingOutFilters }; - if (!canGiveOutFilters) + if (!filterGuard.try_lock()) return {}; + DEFER { filterGuard.unlock(); }; auto filter = absl::c_find_if(filters, [](const FilterHolderPtr& holder) { return holder.use_count() == 1; @@ -133,10 +133,7 @@ size_t sfz::FilterPool::getActiveFilters() const size_t sfz::FilterPool::setNumFilters(size_t numFilters) { - AtomicDisabler disabler { canGiveOutFilters }; - - while(givingOutFilters) - std::this_thread::sleep_for(std::chrono::milliseconds(1)); + const std::lock_guard filterLock { filterGuard }; auto filterIterator = filters.begin(); auto filterSentinel = filters.rbegin(); diff --git a/src/sfizz/FilterPool.h b/src/sfizz/FilterPool.h index fb1a8aac6..25ba02157 100644 --- a/src/sfizz/FilterPool.h +++ b/src/sfizz/FilterPool.h @@ -4,6 +4,7 @@ #include "MidiState.h" #include #include +#include namespace sfz { @@ -118,8 +119,7 @@ class FilterPool */ void setSampleRate(float sampleRate); private: - std::atomic givingOutFilters { false }; - std::atomic canGiveOutFilters { true }; + std::mutex filterGuard; float sampleRate { config::defaultSampleRate }; const MidiState& midiState; std::vector filters; diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 52dfba4ca..884491a0a 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -5,7 +5,7 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "Synth.h" -#include "AtomicGuard.h" +#include "Defer.h" #include "Config.h" #include "Debug.h" #include "Macros.h" @@ -28,21 +28,16 @@ sfz::Synth::Synth() sfz::Synth::Synth(int numVoices) { + const std::lock_guard disableCallback { callbackGuard }; parser.setListener(this); - effectFactory.registerStandardEffectTypes(); - effectBuses.reserve(5); // sufficient room for main and fx1-4 - resetVoices(numVoices); } sfz::Synth::~Synth() { - AtomicDisabler callbackDisabler { canEnterCallback }; - while (inCallback) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } + const std::lock_guard disableCallback { callbackGuard }; for (auto& voice : voices) voice->reset(); @@ -128,10 +123,7 @@ void sfz::Synth::buildRegion(const std::vector& regionOpcodes) void sfz::Synth::clear() { - AtomicDisabler callbackDisabler { canEnterCallback }; - while (inCallback) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } + const std::lock_guard disableCallback { callbackGuard }; for (auto& voice : voices) voice->reset(); @@ -327,13 +319,9 @@ void addEndpointsToVelocityCurve(sfz::Region& region) bool sfz::Synth::loadSfzFile(const fs::path& file) { - AtomicDisabler callbackDisabler { canEnterCallback }; - while (inCallback) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - clear(); + const std::lock_guard disableCallback { callbackGuard }; parser.parseFile(file); if (parser.getErrorCount() > 0) return false; @@ -508,11 +496,7 @@ void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept { ASSERT(samplesPerBlock < config::maxBlockSize); - AtomicDisabler callbackDisabler { canEnterCallback }; - - while (inCallback) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } + const std::lock_guard disableCallback { callbackGuard }; this->samplesPerBlock = samplesPerBlock; for (auto& voice : voices) @@ -528,10 +512,7 @@ void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept void sfz::Synth::setSampleRate(float sampleRate) noexcept { - AtomicDisabler callbackDisabler { canEnterCallback }; - while (inCallback) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } + const std::lock_guard disableCallback { callbackGuard }; this->sampleRate = sampleRate; for (auto& voice : voices) @@ -558,10 +539,9 @@ void sfz::Synth::renderBlock(AudioSpan buffer) noexcept if (freeWheeling) resources.filePool.waitForBackgroundLoading(); - - AtomicGuard callbackGuard { inCallback }; - if (!canEnterCallback) + if (!callbackGuard.try_lock()) return; + DEFER { callbackGuard.unlock(); }; size_t numFrames = buffer.getNumFrames(); @@ -655,9 +635,9 @@ void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; resources.midiState.noteOnEvent(delay, noteNumber, normalizedVelocity); - AtomicGuard callbackGuard { inCallback }; - if (!canEnterCallback) + if (!callbackGuard.try_lock()) return; + DEFER { callbackGuard.unlock(); }; noteOnDispatch(delay, noteNumber, normalizedVelocity); } @@ -671,9 +651,9 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; resources.midiState.noteOffEvent(delay, noteNumber, normalizedVelocity); - AtomicGuard callbackGuard { inCallback }; - if (!canEnterCallback) + if (!callbackGuard.try_lock()) return; + DEFER { callbackGuard.unlock(); }; // FIXME: Some keyboards (e.g. Casio PX5S) can send a real note-off velocity. In this case, do we have a // way in sfz to specify that a release trigger should NOT use the note-on velocity? @@ -767,9 +747,9 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; resources.midiState.ccEvent(delay, ccNumber, normalizedCC); - AtomicGuard callbackGuard { inCallback }; - if (!canEnterCallback) + if (!callbackGuard.try_lock()) return; + DEFER { callbackGuard.unlock(); }; if (ccNumber == config::resetCC) { resetAllControllers(delay); @@ -961,16 +941,12 @@ int sfz::Synth::getNumVoices() const noexcept void sfz::Synth::setNumVoices(int numVoices) noexcept { ASSERT(numVoices > 0); + const std::lock_guard disableCallback { callbackGuard }; resetVoices(numVoices); } void sfz::Synth::resetVoices(int numVoices) { - AtomicDisabler callbackDisabler { canEnterCallback }; - while (inCallback) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - voices.clear(); for (int i = 0; i < numVoices; ++i) voices.push_back(absl::make_unique(resources)); @@ -986,10 +962,7 @@ void sfz::Synth::resetVoices(int numVoices) void sfz::Synth::setOversamplingFactor(sfz::Oversampling factor) noexcept { - AtomicDisabler callbackDisabler { canEnterCallback }; - while (inCallback) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } + const std::lock_guard disableCallback { callbackGuard }; for (auto& voice : voices) voice->reset(); @@ -1006,10 +979,7 @@ sfz::Oversampling sfz::Synth::getOversamplingFactor() const noexcept void sfz::Synth::setPreloadSize(uint32_t preloadSize) noexcept { - AtomicDisabler callbackDisabler { canEnterCallback }; - while (inCallback) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } + const std::lock_guard disableCallback { callbackGuard }; resources.filePool.setPreloadSize(preloadSize); } @@ -1036,11 +1006,12 @@ void sfz::Synth::disableFreeWheeling() noexcept void sfz::Synth::resetAllControllers(int delay) noexcept { - AtomicGuard callbackGuard { inCallback }; - if (!canEnterCallback) + resources.midiState.resetAllControllers(delay); + + if (!callbackGuard.try_lock()) return; + DEFER { callbackGuard.unlock(); }; - resources.midiState.resetAllControllers(delay); for (auto& voice : voices) { voice->registerPitchWheel(delay, 0); for (int cc = 0; cc < config::numCCs; ++cc) @@ -1086,10 +1057,7 @@ void sfz::Synth::disableLogging() noexcept void sfz::Synth::allSoundOff() noexcept { - AtomicDisabler callbackDisabler { canEnterCallback }; - while (inCallback) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } + const std::lock_guard disableCallback { callbackGuard }; for (auto& voice : voices) voice->reset(); diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index 16017f3a8..ada267a34 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -18,6 +18,7 @@ #include "absl/types/span.h" #include #include +#include #include #include #include @@ -206,6 +207,7 @@ class Synth final : public Parser::Listener { * @param noteNumber the midi note number * @param velocity the midi note velocity */ + void noteOff(int delay, int noteNumber, uint8_t velocity) noexcept; /** * @brief Send a CC event to the synth @@ -436,13 +438,7 @@ class Synth final : public Parser::Listener { * */ void clear(); - /** - * @brief Resets and possibly changes the number of voices (polyphony) in - * the synth. - * - * @param numVoices - */ - void resetVoices(int numVoices); + /** * @brief Helper function to dispatch opcodes * @@ -475,6 +471,13 @@ class Synth final : public Parser::Listener { * @param regionOpcodes the opcodes that are specific to the region */ void buildRegion(const std::vector& regionOpcodes); + /** + * @brief Resets and possibly changes the number of voices (polyphony) in + * the synth. + * + * @param numVoices + */ + void resetVoices(int numVoices); fs::file_time_type checkModificationTime(); @@ -526,9 +529,7 @@ class Synth final : public Parser::Listener { std::uniform_real_distribution randNoteDistribution { 0, 1 }; unsigned fileTicket { 1 }; - // Atomic guards; must be used with AtomicGuard and AtomicDisabler - std::atomic canEnterCallback { true }; - std::atomic inCallback { false }; + std::mutex callbackGuard; bool freeWheeling { false }; // Singletons passed as references to the voices From 5f9bbbede54989b5400d02e3d6a0f4e4869685b3 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 15:45:38 +0200 Subject: [PATCH 030/444] Use RTSemaphore instead of sleeplocking the background threads --- src/sfizz/FilePool.cpp | 12 ++++++++++-- src/sfizz/FilePool.h | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index 0fac55917..981485b44 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -106,6 +106,10 @@ sfz::FilePool::FilePool(sfz::Logger& logger) sfz::FilePool::~FilePool() { quitThread = true; + + for (unsigned i = 0; i < threadPool.size(); ++i) + workerBarrier.post(); + for (auto& thread: threadPool) thread.join(); } @@ -281,8 +285,9 @@ sfz::FilePromisePtr sfz::FilePool::getFilePromise(const std::string& filename) n DBG("[sfizz] Could not enqueue the promise for " << filename << " (queue capacity " << promiseQueue.capacity() << ")"); return {}; } - + workerBarrier.post(); emptyPromises.pop_back(); + return promise; } @@ -330,8 +335,9 @@ void sfz::FilePool::loadingThread() noexcept continue; } + workerBarrier.wait(); + if (!promiseQueue.try_pop(promise)) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } @@ -440,6 +446,8 @@ uint32_t sfz::FilePool::getPreloadSize() const noexcept void sfz::FilePool::emptyFileLoadingQueues() noexcept { emptyQueue = true; + workerBarrier.post(); + while (emptyQueue) std::this_thread::sleep_for(std::chrono::milliseconds(1)); } diff --git a/src/sfizz/FilePool.h b/src/sfizz/FilePool.h index 50939dbbd..187cb1824 100644 --- a/src/sfizz/FilePool.h +++ b/src/sfizz/FilePool.h @@ -27,6 +27,7 @@ #include "Config.h" #include "Defaults.h" #include "LeakDetector.h" +#include "RTSemaphore.h" #include "AudioBuffer.h" #include "AudioSpan.h" #include "SIMDHelpers.h" @@ -260,6 +261,7 @@ class FilePool { bool quitThread { false }; bool emptyQueue { false }; std::atomic threadsLoading { 0 }; + RTSemaphore workerBarrier; // File promises data structures along with their guards. std::vector emptyPromises; From becdff5969f30e1410b852eaa4655bd3d5ec78da Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 20:14:55 +0200 Subject: [PATCH 031/444] Stricter c++11 compliance --- src/sfizz/Defer.h | 14 ++++++++------ src/sfizz/EQPool.cpp | 4 ++-- src/sfizz/FilePool.cpp | 4 ++-- src/sfizz/FilterPool.cpp | 4 ++-- src/sfizz/Synth.cpp | 30 +++++++++++++++--------------- 5 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/sfizz/Defer.h b/src/sfizz/Defer.h index 087ca8d69..e88bd21c7 100644 --- a/src/sfizz/Defer.h +++ b/src/sfizz/Defer.h @@ -7,16 +7,18 @@ template struct deferred { - std::decay_t f; - template - deferred(G&& g) : f{std::forward(g)} {} + F f; + deferred(F f) : f(f) {} ~deferred() { f(); } }; -template -deferred(G&&) -> deferred; + +template +deferred deferred_func(F f) { + return deferred(f); +} #define CAT_(x, y) x##y #define CAT(x, y) CAT_(x, y) #define ANONYMOUS_VAR(x) CAT(x, __LINE__) -#define DEFER deferred ANONYMOUS_VAR(defer_variable) = [&] +#define DEFER(code) auto ANONYMOUS_VAR(defer_variable) = deferred_func([&] { code ; }) diff --git a/src/sfizz/EQPool.cpp b/src/sfizz/EQPool.cpp index 574c5fd07..ccee55bc4 100644 --- a/src/sfizz/EQPool.cpp +++ b/src/sfizz/EQPool.cpp @@ -110,7 +110,7 @@ sfz::EQHolderPtr sfz::EQPool::getEQ(const EQDescription& description, unsigned n { if (!eqGuard.try_lock()) return {}; - DEFER { eqGuard.unlock(); }; + DEFER(eqGuard.unlock()); auto eq = absl::c_find_if(eqs, [](const EQHolderPtr& holder) { return holder.use_count() == 1; @@ -132,7 +132,7 @@ size_t sfz::EQPool::getActiveEQs() const size_t sfz::EQPool::setnumEQs(size_t numEQs) { - const std::lock_guard eqLock { eqGuard }; + const std::lock_guard eqLock { eqGuard }; auto eqIterator = eqs.begin(); auto eqSentinel = eqs.rbegin(); diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index 981485b44..fb120c142 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -306,7 +306,7 @@ void sfz::FilePool::setPreloadSize(uint32_t preloadSize) noexcept void sfz::FilePool::tryToClearPromises() { - const std::lock_guard promiseLock { promiseGuard }; + const std::lock_guard promiseLock { promiseGuard }; for (auto& promise: promisesToClear) { if (promise->dataStatus != FilePromise::DataStatus::Wait) @@ -381,7 +381,7 @@ void sfz::FilePool::cleanupPromises() noexcept { if (!promiseGuard.try_lock()) return; - DEFER { promiseGuard.unlock(); }; + DEFER(promiseGuard.unlock()); // The garbage collection cleared the data from these so we can move them // back to the empty queue diff --git a/src/sfizz/FilterPool.cpp b/src/sfizz/FilterPool.cpp index 74e185f34..ce18be289 100644 --- a/src/sfizz/FilterPool.cpp +++ b/src/sfizz/FilterPool.cpp @@ -111,7 +111,7 @@ sfz::FilterHolderPtr sfz::FilterPool::getFilter(const FilterDescription& descrip { if (!filterGuard.try_lock()) return {}; - DEFER { filterGuard.unlock(); }; + DEFER(filterGuard.unlock()); auto filter = absl::c_find_if(filters, [](const FilterHolderPtr& holder) { return holder.use_count() == 1; @@ -133,7 +133,7 @@ size_t sfz::FilterPool::getActiveFilters() const size_t sfz::FilterPool::setNumFilters(size_t numFilters) { - const std::lock_guard filterLock { filterGuard }; + const std::lock_guard filterLock { filterGuard }; auto filterIterator = filters.begin(); auto filterSentinel = filters.rbegin(); diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 884491a0a..a83743b74 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -28,7 +28,7 @@ sfz::Synth::Synth() sfz::Synth::Synth(int numVoices) { - const std::lock_guard disableCallback { callbackGuard }; + const std::lock_guard disableCallback { callbackGuard }; parser.setListener(this); effectFactory.registerStandardEffectTypes(); effectBuses.reserve(5); // sufficient room for main and fx1-4 @@ -37,7 +37,7 @@ sfz::Synth::Synth(int numVoices) sfz::Synth::~Synth() { - const std::lock_guard disableCallback { callbackGuard }; + const std::lock_guard disableCallback { callbackGuard }; for (auto& voice : voices) voice->reset(); @@ -123,7 +123,7 @@ void sfz::Synth::buildRegion(const std::vector& regionOpcodes) void sfz::Synth::clear() { - const std::lock_guard disableCallback { callbackGuard }; + const std::lock_guard disableCallback { callbackGuard }; for (auto& voice : voices) voice->reset(); @@ -321,7 +321,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) { clear(); - const std::lock_guard disableCallback { callbackGuard }; + const std::lock_guard disableCallback { callbackGuard }; parser.parseFile(file); if (parser.getErrorCount() > 0) return false; @@ -496,7 +496,7 @@ void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept { ASSERT(samplesPerBlock < config::maxBlockSize); - const std::lock_guard disableCallback { callbackGuard }; + const std::lock_guard disableCallback { callbackGuard }; this->samplesPerBlock = samplesPerBlock; for (auto& voice : voices) @@ -512,7 +512,7 @@ void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept void sfz::Synth::setSampleRate(float sampleRate) noexcept { - const std::lock_guard disableCallback { callbackGuard }; + const std::lock_guard disableCallback { callbackGuard }; this->sampleRate = sampleRate; for (auto& voice : voices) @@ -541,7 +541,7 @@ void sfz::Synth::renderBlock(AudioSpan buffer) noexcept if (!callbackGuard.try_lock()) return; - DEFER { callbackGuard.unlock(); }; + DEFER(callbackGuard.unlock()); size_t numFrames = buffer.getNumFrames(); @@ -637,7 +637,7 @@ void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept if (!callbackGuard.try_lock()) return; - DEFER { callbackGuard.unlock(); }; + DEFER(callbackGuard.unlock()); noteOnDispatch(delay, noteNumber, normalizedVelocity); } @@ -653,7 +653,7 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept if (!callbackGuard.try_lock()) return; - DEFER { callbackGuard.unlock(); }; + DEFER(callbackGuard.unlock()); // FIXME: Some keyboards (e.g. Casio PX5S) can send a real note-off velocity. In this case, do we have a // way in sfz to specify that a release trigger should NOT use the note-on velocity? @@ -749,7 +749,7 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept if (!callbackGuard.try_lock()) return; - DEFER { callbackGuard.unlock(); }; + DEFER(callbackGuard.unlock()); if (ccNumber == config::resetCC) { resetAllControllers(delay); @@ -941,7 +941,7 @@ int sfz::Synth::getNumVoices() const noexcept void sfz::Synth::setNumVoices(int numVoices) noexcept { ASSERT(numVoices > 0); - const std::lock_guard disableCallback { callbackGuard }; + const std::lock_guard disableCallback { callbackGuard }; resetVoices(numVoices); } @@ -962,7 +962,7 @@ void sfz::Synth::resetVoices(int numVoices) void sfz::Synth::setOversamplingFactor(sfz::Oversampling factor) noexcept { - const std::lock_guard disableCallback { callbackGuard }; + const std::lock_guard disableCallback { callbackGuard }; for (auto& voice : voices) voice->reset(); @@ -979,7 +979,7 @@ sfz::Oversampling sfz::Synth::getOversamplingFactor() const noexcept void sfz::Synth::setPreloadSize(uint32_t preloadSize) noexcept { - const std::lock_guard disableCallback { callbackGuard }; + const std::lock_guard disableCallback { callbackGuard }; resources.filePool.setPreloadSize(preloadSize); } @@ -1010,7 +1010,7 @@ void sfz::Synth::resetAllControllers(int delay) noexcept if (!callbackGuard.try_lock()) return; - DEFER { callbackGuard.unlock(); }; + DEFER(callbackGuard.unlock()); for (auto& voice : voices) { voice->registerPitchWheel(delay, 0); @@ -1057,7 +1057,7 @@ void sfz::Synth::disableLogging() noexcept void sfz::Synth::allSoundOff() noexcept { - const std::lock_guard disableCallback { callbackGuard }; + const std::lock_guard disableCallback { callbackGuard }; for (auto& voice : voices) voice->reset(); From 758da22bd767d7368bf294e5d7173c91ca6330e4 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 22:42:27 +0200 Subject: [PATCH 032/444] Remove noexcept with RTSemaphores in --- src/sfizz/FilePool.cpp | 12 ++++++++---- src/sfizz/FilePool.h | 6 +++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index fb120c142..7ccada70e 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -108,7 +108,11 @@ sfz::FilePool::~FilePool() quitThread = true; for (unsigned i = 0; i < threadPool.size(); ++i) - workerBarrier.post(); + try { + workerBarrier.post(); + } catch (std::exception e) { + continue; + } for (auto& thread: threadPool) thread.join(); @@ -261,7 +265,7 @@ absl::optional sfz::FilePool::loadFile(const std::string& f } } -sfz::FilePromisePtr sfz::FilePool::getFilePromise(const std::string& filename) noexcept +sfz::FilePromisePtr sfz::FilePool::getFilePromise(const std::string& filename) { if (emptyPromises.empty()) { DBG("[sfizz] No empty promises left to honor the one for " << filename); @@ -322,7 +326,7 @@ void sfz::FilePool::clearingThread() } } -void sfz::FilePool::loadingThread() noexcept +void sfz::FilePool::loadingThread() { FilePromisePtr promise; while (!quitThread) { @@ -443,7 +447,7 @@ uint32_t sfz::FilePool::getPreloadSize() const noexcept return preloadSize; } -void sfz::FilePool::emptyFileLoadingQueues() noexcept +void sfz::FilePool::emptyFileLoadingQueues() { emptyQueue = true; workerBarrier.post(); diff --git a/src/sfizz/FilePool.h b/src/sfizz/FilePool.h index 187cb1824..6e997c87c 100644 --- a/src/sfizz/FilePool.h +++ b/src/sfizz/FilePool.h @@ -207,7 +207,7 @@ class FilePool { * @param filename the file to preload * @return FilePromisePtr a file promise */ - FilePromisePtr getFilePromise(const std::string& filename) noexcept; + FilePromisePtr getFilePromise(const std::string& filename); /** * @brief Change the preloading size. This will trigger a full * reload of all samples, so don't call it on the audio thread. @@ -240,7 +240,7 @@ class FilePool { * method on the audio thread as it will spinlock. * */ - void emptyFileLoadingQueues() noexcept; + void emptyFileLoadingQueues(); /** * @brief Wait for the background loading to finish for all promises * in the queue. @@ -249,7 +249,7 @@ class FilePool { private: Logger& logger; fs::path rootDirectory; - void loadingThread() noexcept; + void loadingThread(); void clearingThread(); void tryToClearPromises(); From 3e010a4ba6c0cf38570d069335ebb82a9d93d471 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 22:49:34 +0200 Subject: [PATCH 033/444] Catch by ref --- src/sfizz/FilePool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index 7ccada70e..55f57f242 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -110,7 +110,7 @@ sfz::FilePool::~FilePool() for (unsigned i = 0; i < threadPool.size(); ++i) try { workerBarrier.post(); - } catch (std::exception e) { + } catch (std::exception& e) { continue; } From 07b5d7b3d06ae009007e49246fa899c3f6c2b196 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 13 Apr 2020 00:28:11 +0200 Subject: [PATCH 034/444] Use unique_locks --- src/sfizz/Defer.h | 24 ------------------------ src/sfizz/EQPool.cpp | 6 +++--- src/sfizz/FilePool.cpp | 5 ++--- src/sfizz/FilterPool.cpp | 5 ++--- src/sfizz/Synth.cpp | 21 ++++++++++----------- 5 files changed, 17 insertions(+), 44 deletions(-) delete mode 100644 src/sfizz/Defer.h diff --git a/src/sfizz/Defer.h b/src/sfizz/Defer.h deleted file mode 100644 index e88bd21c7..000000000 --- a/src/sfizz/Defer.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -// From https://stackoverflow.com/questions/48117908/is-the-a-practical-way-to-emulate-go-language-defer-in-c-or-c-destructors -#include -#include - -template -struct deferred -{ - F f; - deferred(F f) : f(f) {} - ~deferred() { f(); } -}; - - -template -deferred deferred_func(F f) { - return deferred(f); -} - -#define CAT_(x, y) x##y -#define CAT(x, y) CAT_(x, y) -#define ANONYMOUS_VAR(x) CAT(x, __LINE__) -#define DEFER(code) auto ANONYMOUS_VAR(defer_variable) = deferred_func([&] { code ; }) diff --git a/src/sfizz/EQPool.cpp b/src/sfizz/EQPool.cpp index ccee55bc4..94aac145e 100644 --- a/src/sfizz/EQPool.cpp +++ b/src/sfizz/EQPool.cpp @@ -1,5 +1,4 @@ #include "EQPool.h" -#include "Defer.h" #include #include "absl/algorithm/container.h" #include "SIMDHelpers.h" @@ -108,9 +107,10 @@ sfz::EQPool::EQPool(const MidiState& state, int numEQs) sfz::EQHolderPtr sfz::EQPool::getEQ(const EQDescription& description, unsigned numChannels, float velocity) { - if (!eqGuard.try_lock()) + const std::unique_lock lock { eqGuard, std::try_to_lock }; + if (!lock.owns_lock()) return {}; - DEFER(eqGuard.unlock()); + auto eq = absl::c_find_if(eqs, [](const EQHolderPtr& holder) { return holder.use_count() == 1; diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index 55f57f242..a356dca27 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -29,7 +29,6 @@ #include "Config.h" #include "Debug.h" #include "Oversampler.h" -#include "Defer.h" #include "absl/types/span.h" #include "absl/strings/match.h" #include "absl/memory/memory.h" @@ -383,9 +382,9 @@ void sfz::FilePool::clear() void sfz::FilePool::cleanupPromises() noexcept { - if (!promiseGuard.try_lock()) + const std::unique_lock lock { promiseGuard, std::try_to_lock }; + if (!lock.owns_lock()) return; - DEFER(promiseGuard.unlock()); // The garbage collection cleared the data from these so we can move them // back to the empty queue diff --git a/src/sfizz/FilterPool.cpp b/src/sfizz/FilterPool.cpp index ce18be289..9e4f90914 100644 --- a/src/sfizz/FilterPool.cpp +++ b/src/sfizz/FilterPool.cpp @@ -1,7 +1,6 @@ #include "FilterPool.h" #include "SIMDHelpers.h" #include "absl/algorithm/container.h" -#include "Defer.h" #include #include @@ -109,9 +108,9 @@ sfz::FilterPool::FilterPool(const MidiState& state, int numFilters) sfz::FilterHolderPtr sfz::FilterPool::getFilter(const FilterDescription& description, unsigned numChannels, int noteNumber, float velocity) { - if (!filterGuard.try_lock()) + const std::unique_lock lock { filterGuard, std::try_to_lock }; + if (!lock.owns_lock()) return {}; - DEFER(filterGuard.unlock()); auto filter = absl::c_find_if(filters, [](const FilterHolderPtr& holder) { return holder.use_count() == 1; diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index a83743b74..1141624c3 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -5,7 +5,6 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "Synth.h" -#include "Defer.h" #include "Config.h" #include "Debug.h" #include "Macros.h" @@ -539,9 +538,9 @@ void sfz::Synth::renderBlock(AudioSpan buffer) noexcept if (freeWheeling) resources.filePool.waitForBackgroundLoading(); - if (!callbackGuard.try_lock()) + const std::unique_lock lock { callbackGuard, std::try_to_lock }; + if (!lock.owns_lock()) return; - DEFER(callbackGuard.unlock()); size_t numFrames = buffer.getNumFrames(); @@ -635,9 +634,9 @@ void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; resources.midiState.noteOnEvent(delay, noteNumber, normalizedVelocity); - if (!callbackGuard.try_lock()) + const std::unique_lock lock { callbackGuard, std::try_to_lock }; + if (!lock.owns_lock()) return; - DEFER(callbackGuard.unlock()); noteOnDispatch(delay, noteNumber, normalizedVelocity); } @@ -651,9 +650,9 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; resources.midiState.noteOffEvent(delay, noteNumber, normalizedVelocity); - if (!callbackGuard.try_lock()) + const std::unique_lock lock { callbackGuard, std::try_to_lock }; + if (!lock.owns_lock()) return; - DEFER(callbackGuard.unlock()); // FIXME: Some keyboards (e.g. Casio PX5S) can send a real note-off velocity. In this case, do we have a // way in sfz to specify that a release trigger should NOT use the note-on velocity? @@ -747,9 +746,9 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; resources.midiState.ccEvent(delay, ccNumber, normalizedCC); - if (!callbackGuard.try_lock()) + const std::unique_lock lock { callbackGuard, std::try_to_lock }; + if (!lock.owns_lock()) return; - DEFER(callbackGuard.unlock()); if (ccNumber == config::resetCC) { resetAllControllers(delay); @@ -1008,9 +1007,9 @@ void sfz::Synth::resetAllControllers(int delay) noexcept { resources.midiState.resetAllControllers(delay); - if (!callbackGuard.try_lock()) + const std::unique_lock lock { callbackGuard, std::try_to_lock }; + if (!lock.owns_lock()) return; - DEFER(callbackGuard.unlock()); for (auto& voice : voices) { voice->registerPitchWheel(delay, 0); From eda78c4604c9b9c36f863ff60cd6b5684a177ac5 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 13 Apr 2020 19:14:05 +0200 Subject: [PATCH 035/444] Avoid use of `catch` in the destructor --- src/sfizz/FilePool.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index a356dca27..140beaa09 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -106,12 +106,10 @@ sfz::FilePool::~FilePool() { quitThread = true; - for (unsigned i = 0; i < threadPool.size(); ++i) - try { - workerBarrier.post(); - } catch (std::exception& e) { - continue; - } + for (unsigned i = 0; i < threadPool.size(); ++i) { + std::error_code ec; + workerBarrier.post(ec); + } for (auto& thread: threadPool) thread.join(); From 6d7b70e7c07e1808c5650bb38e3683447c6fec38 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 13 Apr 2020 19:17:10 +0200 Subject: [PATCH 036/444] Add volatile qualifier at some places it should be --- src/sfizz/FilePool.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sfizz/FilePool.h b/src/sfizz/FilePool.h index 6e997c87c..6471c75f7 100644 --- a/src/sfizz/FilePool.h +++ b/src/sfizz/FilePool.h @@ -258,8 +258,8 @@ class FilePool { uint32_t preloadSize { config::preloadSize }; Oversampling oversamplingFactor { config::defaultOversamplingFactor }; // Signals - bool quitThread { false }; - bool emptyQueue { false }; + volatile bool quitThread { false }; + volatile bool emptyQueue { false }; std::atomic threadsLoading { 0 }; RTSemaphore workerBarrier; From b494835fdd49dc78e1bd8e0bcede279092118996 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 13 Apr 2020 19:19:10 +0200 Subject: [PATCH 037/444] Remove extra newline [ci skip] --- src/sfizz/Synth.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index ada267a34..6b3d4c378 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -207,7 +207,6 @@ class Synth final : public Parser::Listener { * @param noteNumber the midi note number * @param velocity the midi note velocity */ - void noteOff(int delay, int noteNumber, uint8_t velocity) noexcept; /** * @brief Send a CC event to the synth From 7c07df4f660412560926cfb2453abb331df608f2 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 13 Apr 2020 21:35:36 +0200 Subject: [PATCH 038/444] Add back the noexcepts --- src/sfizz/FilePool.cpp | 20 ++++++++++++++------ src/sfizz/FilePool.h | 6 +++--- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index 140beaa09..c474b6348 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -262,7 +262,7 @@ absl::optional sfz::FilePool::loadFile(const std::string& f } } -sfz::FilePromisePtr sfz::FilePool::getFilePromise(const std::string& filename) +sfz::FilePromisePtr sfz::FilePool::getFilePromise(const std::string& filename) noexcept { if (emptyPromises.empty()) { DBG("[sfizz] No empty promises left to honor the one for " << filename); @@ -286,7 +286,11 @@ sfz::FilePromisePtr sfz::FilePool::getFilePromise(const std::string& filename) DBG("[sfizz] Could not enqueue the promise for " << filename << " (queue capacity " << promiseQueue.capacity() << ")"); return {}; } - workerBarrier.post(); + + std::error_code ec; + workerBarrier.post(ec); + ASSERT(!ec); + emptyPromises.pop_back(); return promise; @@ -323,7 +327,7 @@ void sfz::FilePool::clearingThread() } } -void sfz::FilePool::loadingThread() +void sfz::FilePool::loadingThread() noexcept { FilePromisePtr promise; while (!quitThread) { @@ -336,7 +340,9 @@ void sfz::FilePool::loadingThread() continue; } - workerBarrier.wait(); + std::error_code ec; + workerBarrier.wait(ec); + ASSERT(!ec); if (!promiseQueue.try_pop(promise)) { continue; @@ -444,10 +450,12 @@ uint32_t sfz::FilePool::getPreloadSize() const noexcept return preloadSize; } -void sfz::FilePool::emptyFileLoadingQueues() +void sfz::FilePool::emptyFileLoadingQueues() noexcept { emptyQueue = true; - workerBarrier.post(); + std::error_code ec; + workerBarrier.post(ec); + ASSERT(!ec); while (emptyQueue) std::this_thread::sleep_for(std::chrono::milliseconds(1)); diff --git a/src/sfizz/FilePool.h b/src/sfizz/FilePool.h index 6471c75f7..c2600ecee 100644 --- a/src/sfizz/FilePool.h +++ b/src/sfizz/FilePool.h @@ -207,7 +207,7 @@ class FilePool { * @param filename the file to preload * @return FilePromisePtr a file promise */ - FilePromisePtr getFilePromise(const std::string& filename); + FilePromisePtr getFilePromise(const std::string& filename) noexcept; /** * @brief Change the preloading size. This will trigger a full * reload of all samples, so don't call it on the audio thread. @@ -240,7 +240,7 @@ class FilePool { * method on the audio thread as it will spinlock. * */ - void emptyFileLoadingQueues(); + void emptyFileLoadingQueues() noexcept; /** * @brief Wait for the background loading to finish for all promises * in the queue. @@ -249,7 +249,7 @@ class FilePool { private: Logger& logger; fs::path rootDirectory; - void loadingThread(); + void loadingThread() noexcept; void clearingThread(); void tryToClearPromises(); From e274084d2fea576adf07a2323da5f95838ac6341 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 13 Apr 2020 21:38:22 +0200 Subject: [PATCH 039/444] clang-format step --- src/sfizz/EQPool.cpp | 1 - src/sfizz/RTSemaphore.cpp | 40 +++++++++++++++++++-------------------- src/sfizz/RTSemaphore.h | 18 +++++++++--------- tests/FilesT.cpp | 3 +-- 4 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/sfizz/EQPool.cpp b/src/sfizz/EQPool.cpp index 94aac145e..65d5866ce 100644 --- a/src/sfizz/EQPool.cpp +++ b/src/sfizz/EQPool.cpp @@ -111,7 +111,6 @@ sfz::EQHolderPtr sfz::EQPool::getEQ(const EQDescription& description, unsigned n if (!lock.owns_lock()) return {}; - auto eq = absl::c_find_if(eqs, [](const EQHolderPtr& holder) { return holder.use_count() == 1; }); diff --git a/src/sfizz/RTSemaphore.cpp b/src/sfizz/RTSemaphore.cpp index 7a3340d5d..685166e47 100644 --- a/src/sfizz/RTSemaphore.cpp +++ b/src/sfizz/RTSemaphore.cpp @@ -18,7 +18,7 @@ RTSemaphore::RTSemaphore(unsigned value) good_ = true; } -RTSemaphore::RTSemaphore(std::error_code &ec, unsigned value) noexcept +RTSemaphore::RTSemaphore(std::error_code& ec, unsigned value) noexcept { init(ec, value); good_ = ec ? false : true; @@ -58,7 +58,7 @@ bool RTSemaphore::try_wait() } #if defined(__APPLE__) -void RTSemaphore::init(std::error_code &ec, unsigned value) +void RTSemaphore::init(std::error_code& ec, unsigned value) { ec.clear(); kern_return_t ret = semaphore_create(mach_task_self(), &sem_, SYNC_POLICY_FIFO, value); @@ -66,7 +66,7 @@ void RTSemaphore::init(std::error_code &ec, unsigned value) ec = std::error_code(ret, mach_category()); } -void RTSemaphore::destroy(std::error_code &ec) +void RTSemaphore::destroy(std::error_code& ec) { ec.clear(); kern_return_t ret = semaphore_destroy(mach_task_self(), sem_); @@ -74,7 +74,7 @@ void RTSemaphore::destroy(std::error_code &ec) ec = std::error_code(ret, mach_category()); } -void RTSemaphore::post(std::error_code &ec) noexcept +void RTSemaphore::post(std::error_code& ec) noexcept { ec.clear(); kern_return_t ret = semaphore_signal(sem_); @@ -82,7 +82,7 @@ void RTSemaphore::post(std::error_code &ec) noexcept ec = std::error_code(ret, mach_category()); } -void RTSemaphore::wait(std::error_code &ec) noexcept +void RTSemaphore::wait(std::error_code& ec) noexcept { ec.clear(); do { @@ -99,11 +99,11 @@ void RTSemaphore::wait(std::error_code &ec) noexcept } while (1); } -bool RTSemaphore::try_wait(std::error_code &ec) noexcept +bool RTSemaphore::try_wait(std::error_code& ec) noexcept { ec.clear(); do { - const mach_timespec_t timeout = {0, 0}; + const mach_timespec_t timeout = { 0, 0 }; kern_return_t ret = semaphore_timedwait(sem_, timeout); switch (ret) { case KERN_SUCCESS: @@ -119,18 +119,18 @@ bool RTSemaphore::try_wait(std::error_code &ec) noexcept } while (1); } -const std::error_category &RTSemaphore::mach_category() +const std::error_category& RTSemaphore::mach_category() { class mach_category : public std::error_category { public: - const char *name() const noexcept override + const char* name() const noexcept override { return "kern_return_t"; } std::string message(int condition) const override { - const char *str = mach_error_string(condition); + const char* str = mach_error_string(condition); return str ? str : ""; } }; @@ -139,7 +139,7 @@ const std::error_category &RTSemaphore::mach_category() return cat; } #elif defined(_WIN32) -void RTSemaphore::init(std::error_code &ec, unsigned value) +void RTSemaphore::init(std::error_code& ec, unsigned value) { ec.clear(); sem_ = CreateSemaphore(nullptr, value, LONG_MAX, nullptr); @@ -147,21 +147,21 @@ void RTSemaphore::init(std::error_code &ec, unsigned value) ec = std::error_code(GetLastError(), std::system_category()); } -void RTSemaphore::destroy(std::error_code &ec) +void RTSemaphore::destroy(std::error_code& ec) { ec.clear(); if (CloseHandle(sem_) == 0) ec = std::error_code(GetLastError(), std::system_category()); } -void RTSemaphore::post(std::error_code &ec) noexcept +void RTSemaphore::post(std::error_code& ec) noexcept { ec.clear(); if (ReleaseSemaphore(sem_, 1, nullptr) == 0) ec = std::error_code(GetLastError(), std::system_category()); } -void RTSemaphore::wait(std::error_code &ec) noexcept +void RTSemaphore::wait(std::error_code& ec) noexcept { ec.clear(); DWORD ret = WaitForSingleObject(sem_, INFINITE); @@ -177,7 +177,7 @@ void RTSemaphore::wait(std::error_code &ec) noexcept } } -bool RTSemaphore::try_wait(std::error_code &ec) noexcept +bool RTSemaphore::try_wait(std::error_code& ec) noexcept { ec.clear(); DWORD ret = WaitForSingleObject(sem_, 0); @@ -195,21 +195,21 @@ bool RTSemaphore::try_wait(std::error_code &ec) noexcept } } #else -void RTSemaphore::init(std::error_code &ec, unsigned value) +void RTSemaphore::init(std::error_code& ec, unsigned value) { ec.clear(); if (sem_init(&sem_, 0, value) != 0) ec = std::error_code(errno, std::generic_category()); } -void RTSemaphore::destroy(std::error_code &ec) +void RTSemaphore::destroy(std::error_code& ec) { ec.clear(); if (sem_destroy(&sem_) != 0) ec = std::error_code(errno, std::generic_category()); } -void RTSemaphore::post(std::error_code &ec) noexcept +void RTSemaphore::post(std::error_code& ec) noexcept { ec.clear(); while (sem_post(&sem_) != 0) { @@ -221,7 +221,7 @@ void RTSemaphore::post(std::error_code &ec) noexcept } } -void RTSemaphore::wait(std::error_code &ec) noexcept +void RTSemaphore::wait(std::error_code& ec) noexcept { ec.clear(); while (sem_wait(&sem_) != 0) { @@ -233,7 +233,7 @@ void RTSemaphore::wait(std::error_code &ec) noexcept } } -bool RTSemaphore::try_wait(std::error_code &ec) noexcept +bool RTSemaphore::try_wait(std::error_code& ec) noexcept { ec.clear(); do { diff --git a/src/sfizz/RTSemaphore.h b/src/sfizz/RTSemaphore.h index 4ce011126..c17e5c2a1 100644 --- a/src/sfizz/RTSemaphore.h +++ b/src/sfizz/RTSemaphore.h @@ -17,11 +17,11 @@ class RTSemaphore { public: explicit RTSemaphore(unsigned value = 0); - explicit RTSemaphore(std::error_code &ec, unsigned value = 0) noexcept; + explicit RTSemaphore(std::error_code& ec, unsigned value = 0) noexcept; ~RTSemaphore() noexcept; - RTSemaphore(const RTSemaphore &) = delete; - RTSemaphore &operator=(const RTSemaphore &) = delete; + RTSemaphore(const RTSemaphore&) = delete; + RTSemaphore& operator=(const RTSemaphore&) = delete; explicit operator bool() const noexcept { return good_; } @@ -29,18 +29,18 @@ class RTSemaphore { void wait(); bool try_wait(); - void post(std::error_code &ec) noexcept; - void wait(std::error_code &ec) noexcept; - bool try_wait(std::error_code &ec) noexcept; + void post(std::error_code& ec) noexcept; + void wait(std::error_code& ec) noexcept; + bool try_wait(std::error_code& ec) noexcept; private: - void init(std::error_code &ec, unsigned value); - void destroy(std::error_code &ec); + void init(std::error_code& ec, unsigned value); + void destroy(std::error_code& ec); private: #if defined(__APPLE__) semaphore_t sem_ {}; - static const std::error_category &mach_category(); + static const std::error_category& mach_category(); #elif defined(_WIN32) HANDLE sem_ {}; #else diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 75941298e..2442d8dfc 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -532,8 +532,7 @@ TEST_CASE("[Files] Looped regions taken from files and possibly overriden") TEST_CASE("[Files] Case sentitiveness") { - const fs::path sfzFilePath = fs::current_path() / - "tests/TestFiles/case_insensitive.sfz"; + const fs::path sfzFilePath = fs::current_path() / "tests/TestFiles/case_insensitive.sfz"; #if defined(_WIN32) const bool caseSensitiveFs = false; From b932afb910df0366e7627874c40134de87f988d4 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 14 Apr 2020 14:12:11 +0200 Subject: [PATCH 040/444] Set the default key=69 in CaptureEG, ensures correct length --- devtools/CaptureEG.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/devtools/CaptureEG.cpp b/devtools/CaptureEG.cpp index 558b0d149..b53132542 100644 --- a/devtools/CaptureEG.cpp +++ b/devtools/CaptureEG.cpp @@ -284,6 +284,7 @@ void Application::performSfzUpdate() QString code; code += "\n"; + code += "key=69\n"; code += "sample="; code += QFileInfo(samplePath).fileName(); code += "\n"; code += _ui->envelopeEdit->toPlainText(); From 4ea11a31bfacf69246cb4d145585a900a64eb55d Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 6 Apr 2020 00:20:11 +0200 Subject: [PATCH 041/444] Moved curves to resources --- src/sfizz/Resources.h | 2 ++ src/sfizz/Synth.cpp | 8 ++++---- src/sfizz/Synth.h | 4 ---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/sfizz/Resources.h b/src/sfizz/Resources.h index 1367d7144..2c6adcfce 100644 --- a/src/sfizz/Resources.h +++ b/src/sfizz/Resources.h @@ -11,6 +11,7 @@ #include "EQPool.h" #include "Logger.h" #include "Wavetables.h" +#include "Curve.h" namespace sfz { @@ -21,6 +22,7 @@ struct Resources BufferPool bufferPool; MidiState midiState; Logger logger; + CurveSet curves; FilePool filePool { logger }; FilterPool filterPool { midiState }; EQPool eqPool { midiState }; diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 0870905fe..5e4d7fe9d 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -71,7 +71,7 @@ void sfz::Synth::onParseFullBlock(const std::string& header, const std::vectorsetGainToMain(1.0); effectBuses[0]->setSamplesPerBlock(samplesPerBlock); effectBuses[0]->setSampleRate(sampleRate); - curves = CurveSet::createPredefined(); + resources.curves = CurveSet::createPredefined(); resources.filePool.clear(); resources.wavePool.clearFileWaves(); resources.logger.clear(); + resources.midiState.reset(); numGroups = 0; numMasters = 0; fileTicket = -1; defaultSwitch = absl::nullopt; defaultPath = ""; - resources.midiState.reset(); ccNames.clear(); globalOpcodes.clear(); masterOpcodes.clear(); @@ -814,7 +814,7 @@ int sfz::Synth::getNumMasters() const noexcept } int sfz::Synth::getNumCurves() const noexcept { - return static_cast(curves.getNumCurves()); + return static_cast(resources.curves.getNumCurves()); } std::string sfz::Synth::exportMidnam(absl::string_view model) const diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index 6b3d4c378..6ee6da730 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -10,7 +10,6 @@ #include "Voice.h" #include "Region.h" #include "Effects.h" -#include "Curve.h" #include "LeakDetector.h" #include "MidiState.h" #include "AudioSpan.h" @@ -515,9 +514,6 @@ class Synth final : public Parser::Listener { typedef std::unique_ptr EffectBusPtr; std::vector effectBuses; // 0 is "main", 1-N are "fx1"-"fxN" - // Curves - CurveSet curves; - int samplesPerBlock { config::defaultSamplesPerBlock }; float sampleRate { config::defaultSampleRate }; float volume { Default::globalVolume }; From 13edb6dfbab23e968d7a3f812c987a401eda2f1a Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 6 Apr 2020 00:29:42 +0200 Subject: [PATCH 042/444] Change the midiState observer to a resource observer for tests --- src/sfizz/Synth.h | 2 +- tests/FilesT.cpp | 5 +++-- tests/SynthT.cpp | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index 6ee6da730..9f053cc99 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -339,7 +339,7 @@ class Synth final : public Parser::Listener { */ void disableFreeWheeling() noexcept; - const MidiState& getMidiState() const noexcept { return resources.midiState; } + const Resources& getResources() const noexcept { return resources; } /** * @brief Check if the SFZ should be reloaded. diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 2442d8dfc..876aef89e 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -394,9 +394,10 @@ TEST_CASE("[Files] Default path is ignored for generators") TEST_CASE("[Files] Set CC applies properly") { sfz::Synth synth; + const auto& midiState = synth.getResources().midiState; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_cc.sfz"); - REQUIRE(synth.getMidiState().getCCValue(142) == 63_norm); - REQUIRE(synth.getMidiState().getCCValue(61) == 122_norm); + REQUIRE(midiState.getCCValue(142) == 63_norm); + REQUIRE(midiState.getCCValue(61) == 122_norm); } TEST_CASE("[Files] Note and octave offsets") diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index bd0d28cce..29b55645d 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -140,10 +140,11 @@ TEST_CASE("[Synth] All notes offs/all sounds off") TEST_CASE("[Synth] Reset all controllers") { sfz::Synth synth; + const auto& midiState = synth.getResources().midiState; synth.cc(0, 12, 64); - REQUIRE(synth.getMidiState().getCCValue(12) == 64_norm); + REQUIRE(midiState.getCCValue(12) == 64_norm); synth.cc(0, 121, 64); - REQUIRE(synth.getMidiState().getCCValue(12) == 0_norm); + REQUIRE(midiState.getCCValue(12) == 0_norm); } TEST_CASE("[Synth] Releasing before the EG started smoothing (initial delay) kills the voice") From 0fd1c235de2a50524161800f90187a15fbfce735 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 6 Apr 2020 00:26:44 +0200 Subject: [PATCH 043/444] Added a basic curve test --- tests/SynthT.cpp | 14 ++++++++++++++ tests/TestFiles/curves.sfz | 10 ++++++++++ 2 files changed, 24 insertions(+) create mode 100644 tests/TestFiles/curves.sfz diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 29b55645d..6d7022baf 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -420,3 +420,17 @@ TEST_CASE("[Synth] Polyphony in master") synth.noteOn(0, 61, 64); REQUIRE(synth.getNumActiveVoices() == 3); } + +TEST_CASE("[Synth] Basic curves") +{ + sfz::Synth synth; + const auto& curves = synth.getResources().curves; + synth.loadSfzFile(fs::current_path() / "tests/TestFiles/curves.sfz"); + REQUIRE( synth.getNumCurves() == 19 ); + REQUIRE( curves.getCurve(18).evalCC7(127) == 1.0f ); + REQUIRE( curves.getCurve(18).evalCC7(95) == 0.5f ); + REQUIRE( curves.getCurve(17).evalCC7(100) == 1.0f ); + REQUIRE( curves.getCurve(17).evalCC7(95) == 0.5f ); + // Default linear + REQUIRE( curves.getCurve(16).evalCC7(63) == Approx(63_norm) ); +} diff --git a/tests/TestFiles/curves.sfz b/tests/TestFiles/curves.sfz new file mode 100644 index 000000000..8c24cca47 --- /dev/null +++ b/tests/TestFiles/curves.sfz @@ -0,0 +1,10 @@ + sample=*sine +curve_index=18 +v000=0 +v095=0.5 +v127=1 + +curve_index=17 +v000=0 +v095=0.5 +v100=1 From 61d1c568a8af657ffe4cdb1aa70a51c665e36094 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 6 Apr 2020 01:19:40 +0200 Subject: [PATCH 044/444] Add a modifier structure and rename the ccvalue pair --- src/sfizz/CCMap.h | 18 +++++------ src/sfizz/Defaults.h | 1 - src/sfizz/EGDescription.h | 18 +++++------ src/sfizz/EQPool.cpp | 12 ++++---- src/sfizz/FilterPool.cpp | 12 ++++---- src/sfizz/Opcode.h | 2 +- src/sfizz/Region.cpp | 24 +++++++-------- src/sfizz/Region.h | 12 ++++---- src/sfizz/SfzHelpers.h | 41 ++++++++++--------------- src/sfizz/Voice.cpp | 20 ++++++------ tests/FilesT.cpp | 2 +- tests/RegionT.cpp | 64 +++++++++++++++++++-------------------- 12 files changed, 108 insertions(+), 118 deletions(-) diff --git a/src/sfizz/CCMap.h b/src/sfizz/CCMap.h index 09e8830cd..1b44cf8cc 100644 --- a/src/sfizz/CCMap.h +++ b/src/sfizz/CCMap.h @@ -44,11 +44,11 @@ class CCMap { */ const ValueType& getWithDefault(int index) const noexcept { - auto it = absl::c_lower_bound(container, index, CCValuePairComparator{}); + auto it = absl::c_lower_bound(container, index, CCDataComparator{}); if (it == container.end() || it->cc != index) { return defaultValue; } else { - return it->value; + return it->data; } } @@ -60,12 +60,12 @@ class CCMap { */ ValueType& operator[](const int& index) noexcept { - auto it = absl::c_lower_bound(container, index, CCValuePairComparator{}); + auto it = absl::c_lower_bound(container, index, CCDataComparator{}); if (it == container.end() || it->cc != index) { auto inserted = container.insert(it, { index, defaultValue }); - return inserted->value; + return inserted->data; } else { - return it->value; + return it->data; } } @@ -85,16 +85,16 @@ class CCMap { */ bool contains(int index) const noexcept { - return absl::c_binary_search(container, index, CCValuePairComparator{}); + return absl::c_binary_search(container, index, CCDataComparator{}); } - typename std::vector>::const_iterator begin() const { return container.cbegin(); } - typename std::vector>::const_iterator end() const { return container.cend(); } + typename std::vector>::const_iterator begin() const { return container.cbegin(); } + typename std::vector>::const_iterator end() const { return container.cend(); } private: // typename std::vector>::iterator begin() { return container.begin(); } // typename std::vector>::iterator end() { return container.end(); } const ValueType defaultValue; - std::vector> container; + std::vector> container; LEAK_DETECTOR(CCMap); }; } diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 158da5970..a0cae7a14 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -58,7 +58,6 @@ namespace Default constexpr Range midi7Range { 0, 127 }; constexpr Range normalizedRange { 0.0f, 1.0f }; constexpr Range symmetricNormalizedRange { -1.0, 1.0 }; - constexpr float zeroModifier { 0.0f }; // Wavetable oscillator constexpr float oscillatorPhase { 0.0 }; diff --git a/src/sfizz/EGDescription.h b/src/sfizz/EGDescription.h index ee44f7549..e64fa6428 100644 --- a/src/sfizz/EGDescription.h +++ b/src/sfizz/EGDescription.h @@ -50,10 +50,10 @@ namespace sfz { * @param value * @return float */ -inline float ccSwitchedValue(const MidiState& state, const absl::optional>& ccSwitch, float value) noexcept +inline float ccSwitchedValue(const MidiState& state, const absl::optional>& ccSwitch, float value) noexcept { if (ccSwitch) - return value + ccSwitch->value * state.getCCValue(ccSwitch->cc); + return value + ccSwitch->data * state.getCCValue(ccSwitch->cc); else return value; } @@ -80,13 +80,13 @@ struct EGDescription { float vel2sustain { Default::vel2sustain }; int vel2depth { Default::depth }; - absl::optional> ccAttack; - absl::optional> ccDecay; - absl::optional> ccDelay; - absl::optional> ccHold; - absl::optional> ccRelease; - absl::optional> ccStart; - absl::optional> ccSustain; + absl::optional> ccAttack; + absl::optional> ccDecay; + absl::optional> ccDelay; + absl::optional> ccHold; + absl::optional> ccRelease; + absl::optional> ccStart; + absl::optional> ccSustain; /** * @brief Get the attack with possibly a CC modifier and a velocity modifier diff --git a/src/sfizz/EQPool.cpp b/src/sfizz/EQPool.cpp index 65d5866ce..adfdfec73 100644 --- a/src/sfizz/EQPool.cpp +++ b/src/sfizz/EQPool.cpp @@ -29,17 +29,17 @@ void sfz::EQHolder::setup(const EQDescription& description, unsigned numChannels // Setup the modulated values lastFrequency = baseFrequency; for (const auto& mod : description.frequencyCC) - lastFrequency += midiState.getCCValue(mod.cc) * mod.value; + lastFrequency += midiState.getCCValue(mod.cc) * mod.data; lastFrequency = Default::eqFrequencyRange.clamp(lastFrequency); lastBandwidth = baseBandwidth; for (const auto& mod : description.bandwidthCC) - lastBandwidth += midiState.getCCValue(mod.cc) * mod.value; + lastBandwidth += midiState.getCCValue(mod.cc) * mod.data; lastBandwidth = Default::eqBandwidthRange.clamp(lastBandwidth); lastGain = baseGain; for (const auto& mod : description.gainCC) - lastGain += midiState.getCCValue(mod.cc) * mod.value; + lastGain += midiState.getCCValue(mod.cc) * mod.data; lastGain = Default::filterGainRange.clamp(lastGain); // Initialize the EQ @@ -62,17 +62,17 @@ void sfz::EQHolder::process(const float** inputs, float** outputs, unsigned numF // For now we take the last value lastFrequency = baseFrequency; for (const auto& mod : description->frequencyCC) - lastFrequency += midiState.getCCValue(mod.cc) * mod.value; + lastFrequency += midiState.getCCValue(mod.cc) * mod.data; lastFrequency = Default::eqFrequencyRange.clamp(lastFrequency); lastBandwidth = baseBandwidth; for (const auto& mod : description->bandwidthCC) - lastBandwidth += midiState.getCCValue(mod.cc) * mod.value; + lastBandwidth += midiState.getCCValue(mod.cc) * mod.data; lastBandwidth = Default::eqBandwidthRange.clamp(lastBandwidth); lastGain = baseGain; for (const auto& mod : description->gainCC) - lastGain += midiState.getCCValue(mod.cc) * mod.value; + lastGain += midiState.getCCValue(mod.cc) * mod.data; lastGain = Default::filterGainRange.clamp(lastGain); if (lastGain == 0.0f) { diff --git a/src/sfizz/FilterPool.cpp b/src/sfizz/FilterPool.cpp index 9e4f90914..3b61276fa 100644 --- a/src/sfizz/FilterPool.cpp +++ b/src/sfizz/FilterPool.cpp @@ -41,17 +41,17 @@ void sfz::FilterHolder::setup(const FilterDescription& description, unsigned num // Setup the modulated values lastCutoff = baseCutoff; for (const auto& mod : description.cutoffCC) - lastCutoff *= centsFactor(midiState.getCCValue(mod.cc) * mod.value); + lastCutoff *= centsFactor(midiState.getCCValue(mod.cc) * mod.data); lastCutoff = Default::filterCutoffRange.clamp(lastCutoff); lastResonance = baseResonance; for (const auto& mod : description.resonanceCC) - lastResonance += midiState.getCCValue(mod.cc) * mod.value; + lastResonance += midiState.getCCValue(mod.cc) * mod.data; lastResonance = Default::filterResonanceRange.clamp(lastResonance); lastGain = baseGain; for (const auto& mod : description.gainCC) - lastGain += midiState.getCCValue(mod.cc) * mod.value; + lastGain += midiState.getCCValue(mod.cc) * mod.data; lastGain = Default::filterGainRange.clamp(lastGain); // Initialize the filter @@ -71,17 +71,17 @@ void sfz::FilterHolder::process(const float** inputs, float** outputs, unsigned // TODO: the template deduction could be automatic here? lastCutoff = baseCutoff; for (const auto& mod : description->cutoffCC) - lastCutoff *= centsFactor(midiState.getCCValue(mod.cc) * mod.value); + lastCutoff *= centsFactor(midiState.getCCValue(mod.cc) * mod.data); lastCutoff = Default::filterCutoffRange.clamp(lastCutoff); lastResonance = baseResonance; for (const auto& mod : description->resonanceCC) - lastResonance += midiState.getCCValue(mod.cc) * mod.value; + lastResonance += midiState.getCCValue(mod.cc) * mod.data; lastResonance = Default::filterResonanceRange.clamp(lastResonance); lastGain = baseGain; for (const auto& mod : description->gainCC) - lastGain += midiState.getCCValue(mod.cc) * mod.value; + lastGain += midiState.getCCValue(mod.cc) * mod.data; lastGain = Default::filterGainRange.clamp(lastGain); filter.process(inputs, outputs, lastCutoff, lastResonance, lastGain, numFrames); diff --git a/src/sfizz/Opcode.h b/src/sfizz/Opcode.h index e4ff6c263..492cfb1e9 100644 --- a/src/sfizz/Opcode.h +++ b/src/sfizz/Opcode.h @@ -185,7 +185,7 @@ inline void setRangeStartFromOpcode(const Opcode& opcode, Range& targ * @param validRange the range of admitted values used to clamp the opcode */ template -inline void setCCPairFromOpcode(const Opcode& opcode, absl::optional>& target, const Range& validRange) +inline void setCCPairFromOpcode(const Opcode& opcode, absl::optional>& target, const Range& validRange) { auto value = readOpcode(opcode.value, validRange); if (value && Default::ccNumberRange.containsWithEnd(opcode.parameters.back())) diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 234878e93..85b014d22 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -303,7 +303,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) if (opcode.parameters.back() > config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::volumeCCRange)) - volumeCC[opcode.parameters.back()] = *value; + volumeCC[opcode.parameters.back()].value = *value; break; case hash("amplitude"): if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) @@ -314,7 +314,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) if (opcode.parameters.back() > config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) - amplitudeCC[opcode.parameters.back()] = normalizePercents(*value); + amplitudeCC[opcode.parameters.back()].value = normalizePercents(*value); break; case hash("pan"): if (auto value = readOpcode(opcode.value, Default::panRange)) @@ -325,7 +325,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) if (opcode.parameters.back() > config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::panCCRange)) - panCC[opcode.parameters.back()] = normalizePercents(*value); + panCC[opcode.parameters.back()].value = normalizePercents(*value); break; case hash("position"): if (auto value = readOpcode(opcode.value, Default::positionRange)) @@ -335,7 +335,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) if (opcode.parameters.back() > config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::positionCCRange)) - positionCC[opcode.parameters.back()] = normalizePercents(*value); + positionCC[opcode.parameters.back()].value = normalizePercents(*value); break; case hash("width"): if (auto value = readOpcode(opcode.value, Default::widthRange)) @@ -345,7 +345,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) if (opcode.parameters.back() > config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::widthCCRange)) - widthCC[opcode.parameters.back()] = normalizePercents(*value); + widthCC[opcode.parameters.back()].value = normalizePercents(*value); break; case hash("amp_keycenter"): setValueFromOpcode(opcode, ampKeycenter, Default::keyRange); @@ -733,7 +733,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) if (opcode.parameters.back() > config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::tuneCCRange)) - tuneCC[opcode.parameters.back()] = *value; + tuneCC[opcode.parameters.back()].value = *value; break; case hash("bend_up"): setValueFromOpcode(opcode, bendUp, Default::bendBoundRange); @@ -1078,15 +1078,15 @@ float sfz::Region::getCrossfadeGain() const noexcept float gain { 1.0f }; // Crossfades due to CC states - for (const auto& valuePair : crossfadeCCInRange) { - const auto ccValue = midiState.getCCValue(valuePair.cc); - const auto crossfadeRange = valuePair.value; + for (const auto& ccData : crossfadeCCInRange) { + const auto ccValue = midiState.getCCValue(ccData.cc); + const auto crossfadeRange = ccData.data; gain *= crossfadeIn(crossfadeRange, ccValue, crossfadeCCCurve); } - for (const auto& valuePair : crossfadeCCOutRange) { - const auto ccValue = midiState.getCCValue(valuePair.cc); - const auto crossfadeRange = valuePair.value; + for (const auto& ccData : crossfadeCCOutRange) { + const auto ccValue = midiState.getCCValue(ccData.cc); + const auto crossfadeRange = ccData.data; gain *= crossfadeOut(crossfadeRange, ccValue, crossfadeCCCurve); } diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 73f3bc531..572d3eeb4 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -285,11 +285,11 @@ struct Region { float pan { normalizePercents(Default::pan) }; // pan float width { normalizePercents(Default::width) }; // width float position { normalizePercents(Default::position) }; // position - CCMap volumeCC { Default::zeroModifier }; // volume_oncc - CCMap amplitudeCC { Default::zeroModifier }; // amplitude_oncc - CCMap panCC { Default::zeroModifier }; // pan_oncc - CCMap widthCC { Default::zeroModifier }; // width_oncc - CCMap positionCC { Default::zeroModifier }; // position_oncc + CCMap volumeCC { {} }; // volume_oncc + CCMap amplitudeCC { {} }; // amplitude_oncc + CCMap panCC { {} }; // pan_oncc + CCMap widthCC { {} }; // width_oncc + CCMap positionCC { {} }; // position_oncc uint8_t ampKeycenter { Default::ampKeycenter }; // amp_keycenter float ampKeytrack { Default::ampKeytrack }; // amp_keytrack float ampVeltrack { Default::ampVeltrack }; // amp_keytrack @@ -317,7 +317,7 @@ struct Region { int pitchVeltrack { Default::pitchVeltrack }; // pitch_veltrack int transpose { Default::transpose }; // transpose int tune { Default::tune }; // tune - CCMap tuneCC { Default::tune }; + CCMap tuneCC { {} }; int bendUp { Default::bendUp }; int bendDown { Default::bendDown }; int bendStep { Default::bendStep }; diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index 64d6ed6cc..fb1d06360 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -24,44 +24,35 @@ using CCNamePair = std::pair; template using MidiNoteArray = std::array; template -struct CCValuePair { +struct CCData { int cc; - ValueType value; + ValueType data; + static_assert(config::numCCs - 1 < std::numeric_limits::max()); }; -template -struct CCValuePairComparator { - bool operator()(const CCValuePair& valuePair, const int& cc) - { - return (valuePair.cc < cc); - } - - bool operator()(const int& cc, const CCValuePair& valuePair) - { - return (cc < valuePair.cc); - } - - bool operator()(const CCValuePair& lhs, const CCValuePair& rhs) - { - return (lhs.cc < rhs.cc); - } +struct Modifier { + float value { 0.0f }; + uint8_t curve { 0 }; + uint8_t steps { 0 }; + uint8_t smooth { 0 }; + static_assert(config::maxCurves - 1 <= std::numeric_limits::max()); }; template -struct CCValuePairComparator { - bool operator()(const CCValuePair& valuePair, const ValueType& value) +struct CCDataComparator { + bool operator()(const CCData& ccData, const int& cc) { - return (valuePair.value < value); + return (ccData.cc < cc); } - bool operator()(const ValueType& value, const CCValuePair& valuePair) + bool operator()(const int& cc, const CCData& ccData) { - return (value < valuePair.value); + return (cc < ccData.cc); } - bool operator()(const CCValuePair& lhs, const CCValuePair& rhs) + bool operator()(const CCData& lhs, const CCData& rhs) { - return (lhs.value < rhs.value); + return (lhs.cc < rhs.cc); } }; diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index fcb1d9fd8..370d29d88 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -265,19 +265,19 @@ void sfz::Voice::amplitudeEnvelope(absl::Span modulationSpan) noexcept applyGain(baseGain, modulationSpan); for (const auto& mod : region->amplitudeCC) { const auto events = resources.midiState.getCCEvents(mod.cc); - linearEnvelope(events, *tempSpan, [&mod](float x) { return x * mod.value; }); + linearEnvelope(events, *tempSpan, [&mod](float x) { return x * mod.data.value; }); applyGain(*tempSpan, modulationSpan); } // Crossfade envelopes for (const auto& mod : region->crossfadeCCInRange) { const auto events = resources.midiState.getCCEvents(mod.cc); - linearEnvelope(events, *tempSpan, [&](float x) { return crossfadeIn(mod.value, x, xfCurve); }); + linearEnvelope(events, *tempSpan, [&](float x) { return crossfadeIn(mod.data, x, xfCurve); }); applyGain(*tempSpan, modulationSpan); } for (const auto& mod : region->crossfadeCCOutRange) { const auto events = resources.midiState.getCCEvents(mod.cc); - linearEnvelope(events, *tempSpan, [&](float x) { return crossfadeOut(mod.value, x, xfCurve); }); + linearEnvelope(events, *tempSpan, [&](float x) { return crossfadeOut(mod.data, x, xfCurve); }); applyGain(*tempSpan, modulationSpan); } @@ -285,7 +285,7 @@ void sfz::Voice::amplitudeEnvelope(absl::Span modulationSpan) noexcept applyGain(db2mag(baseVolumedB), modulationSpan); for (const auto& mod : region->volumeCC) { const auto events = resources.midiState.getCCEvents(mod.cc); - multiplicativeEnvelope(events, *tempSpan, [&](float x) { return db2mag(x * mod.value); }); + multiplicativeEnvelope(events, *tempSpan, [&](float x) { return db2mag(x * mod.data.value); }); applyGain(*tempSpan, modulationSpan); } } @@ -338,7 +338,7 @@ void sfz::Voice::panStageMono(AudioSpan buffer) noexcept fill(*modulationSpan, region->pan); for (const auto& mod : region->panCC) { const auto events = resources.midiState.getCCEvents(mod.cc); - linearEnvelope(events, *tempSpan, [&mod](float x) { return x * mod.value; }); + linearEnvelope(events, *tempSpan, [&mod](float x) { return x * mod.data.value; }); add(*tempSpan, *modulationSpan); } pan(*modulationSpan, leftBuffer, rightBuffer); @@ -361,7 +361,7 @@ void sfz::Voice::panStageStereo(AudioSpan buffer) noexcept fill(*modulationSpan, region->pan); for (const auto& mod : region->panCC) { const auto events = resources.midiState.getCCEvents(mod.cc); - linearEnvelope(events, *tempSpan, [&mod](float x) { return x * mod.value; }); + linearEnvelope(events, *tempSpan, [&mod](float x) { return x * mod.data.value; }); add(*tempSpan, *modulationSpan); } pan(*modulationSpan, leftBuffer, rightBuffer); @@ -371,7 +371,7 @@ void sfz::Voice::panStageStereo(AudioSpan buffer) noexcept fill(*modulationSpan, region->width); for (const auto& mod : region->widthCC) { const auto events = resources.midiState.getCCEvents(mod.cc); - linearEnvelope(events, *tempSpan, [&mod](float x) { return x * mod.value; }); + linearEnvelope(events, *tempSpan, [&mod](float x) { return x * mod.data.value; }); add(*tempSpan, *modulationSpan); } width(*modulationSpan, leftBuffer, rightBuffer); @@ -380,7 +380,7 @@ void sfz::Voice::panStageStereo(AudioSpan buffer) noexcept fill(*modulationSpan, region->position); for (const auto& mod : region->positionCC) { const auto events = resources.midiState.getCCEvents(mod.cc); - linearEnvelope(events, *tempSpan, [&mod](float x) { return x * mod.value; }); + linearEnvelope(events, *tempSpan, [&mod](float x) { return x * mod.data.value; }); add(*tempSpan, *modulationSpan); } pan(*modulationSpan, leftBuffer, rightBuffer); @@ -458,7 +458,7 @@ void sfz::Voice::fillWithData(AudioSpan buffer) noexcept for (const auto& mod : region->tuneCC) { const auto events = resources.midiState.getCCEvents(mod.cc); - multiplicativeEnvelope(events, *bends, [&](float x) { return centsFactor(x * mod.value); }); + multiplicativeEnvelope(events, *bends, [&](float x) { return centsFactor(x * mod.data.value); }); applyGain(*bends, *jumps); } @@ -562,7 +562,7 @@ void sfz::Voice::fillWithGenerator(AudioSpan buffer) noexcept for (const auto& mod : region->tuneCC) { const auto events = resources.midiState.getCCEvents(mod.cc); - multiplicativeEnvelope(events, *bends, [&](float x) { return centsFactor(x * mod.value); }); + multiplicativeEnvelope(events, *bends, [&](float x) { return centsFactor(x * mod.data.value); }); applyGain(*bends, *frequencies); } diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 876aef89e..4dd43c8fc 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -351,7 +351,7 @@ TEST_CASE("[Files] wrong (overlapping) replacement for defines") REQUIRE( synth.getRegionView(1)->keyRange.getEnd() == 57 ); REQUIRE(!synth.getRegionView(2)->amplitudeCC.empty()); REQUIRE(synth.getRegionView(2)->amplitudeCC.contains(10)); - REQUIRE(synth.getRegionView(2)->amplitudeCC.getWithDefault(10) == 0.34f); + REQUIRE(synth.getRegionView(2)->amplitudeCC.getWithDefault(10).value == 0.34f); } TEST_CASE("[Files] Specific bug: relative path with backslashes") diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index 86fde09c1..b98f9f51d 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -470,7 +470,7 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.panCC.empty()); region.parseOpcode({ "pan_oncc45", "4.2" }); REQUIRE(region.panCC.contains(45)); - REQUIRE(region.panCC[45] == 0.042_a); + REQUIRE(region.panCC[45].value == 0.042_a); } SECTION("width") @@ -491,7 +491,7 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.widthCC.empty()); region.parseOpcode({ "width_oncc45", "4.2" }); REQUIRE(region.widthCC.contains(45)); - REQUIRE(region.widthCC[45] == 0.042_a); + REQUIRE(region.widthCC[45].value == 0.042_a); } SECTION("position") @@ -512,7 +512,7 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.positionCC.empty()); region.parseOpcode({ "position_oncc45", "4.2" }); REQUIRE(region.positionCC.contains(45)); - REQUIRE(region.positionCC[45] == 0.042_a); + REQUIRE(region.positionCC[45].value == 0.042_a); } SECTION("amp_keycenter") @@ -974,13 +974,13 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.amplitudeEG.ccRelease->cc == 5); REQUIRE(region.amplitudeEG.ccStart->cc == 6); REQUIRE(region.amplitudeEG.ccSustain->cc == 7); - REQUIRE(region.amplitudeEG.ccAttack->value == 1.0f); - REQUIRE(region.amplitudeEG.ccDecay->value == 2.0f); - REQUIRE(region.amplitudeEG.ccDelay->value == 3.0f); - REQUIRE(region.amplitudeEG.ccHold->value == 4.0f); - REQUIRE(region.amplitudeEG.ccRelease->value == 5.0f); - REQUIRE(region.amplitudeEG.ccStart->value == 6.0f); - REQUIRE(region.amplitudeEG.ccSustain->value == 7.0f); + REQUIRE(region.amplitudeEG.ccAttack->data == 1.0f); + REQUIRE(region.amplitudeEG.ccDecay->data == 2.0f); + REQUIRE(region.amplitudeEG.ccDelay->data == 3.0f); + REQUIRE(region.amplitudeEG.ccHold->data == 4.0f); + REQUIRE(region.amplitudeEG.ccRelease->data == 5.0f); + REQUIRE(region.amplitudeEG.ccStart->data == 6.0f); + REQUIRE(region.amplitudeEG.ccSustain->data == 7.0f); // region.parseOpcode({ "ampeg_attack_oncc1", "101" }); region.parseOpcode({ "ampeg_decay_oncc2", "101" }); @@ -989,13 +989,13 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "ampeg_release_oncc5", "101" }); region.parseOpcode({ "ampeg_start_oncc6", "101" }); region.parseOpcode({ "ampeg_sustain_oncc7", "101" }); - REQUIRE(region.amplitudeEG.ccAttack->value == 100.0f); - REQUIRE(region.amplitudeEG.ccDecay->value == 100.0f); - REQUIRE(region.amplitudeEG.ccDelay->value == 100.0f); - REQUIRE(region.amplitudeEG.ccHold->value == 100.0f); - REQUIRE(region.amplitudeEG.ccRelease->value == 100.0f); - REQUIRE(region.amplitudeEG.ccStart->value == 100.0f); - REQUIRE(region.amplitudeEG.ccSustain->value == 100.0f); + REQUIRE(region.amplitudeEG.ccAttack->data == 100.0f); + REQUIRE(region.amplitudeEG.ccDecay->data == 100.0f); + REQUIRE(region.amplitudeEG.ccDelay->data == 100.0f); + REQUIRE(region.amplitudeEG.ccHold->data == 100.0f); + REQUIRE(region.amplitudeEG.ccRelease->data == 100.0f); + REQUIRE(region.amplitudeEG.ccStart->data == 100.0f); + REQUIRE(region.amplitudeEG.ccSustain->data == 100.0f); // region.parseOpcode({ "ampeg_attack_oncc1", "-101" }); region.parseOpcode({ "ampeg_decay_oncc2", "-101" }); @@ -1004,13 +1004,13 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "ampeg_release_oncc5", "-101" }); region.parseOpcode({ "ampeg_start_oncc6", "-101" }); region.parseOpcode({ "ampeg_sustain_oncc7", "-101" }); - REQUIRE(region.amplitudeEG.ccAttack->value == -100.0f); - REQUIRE(region.amplitudeEG.ccDecay->value == -100.0f); - REQUIRE(region.amplitudeEG.ccDelay->value == -100.0f); - REQUIRE(region.amplitudeEG.ccHold->value == -100.0f); - REQUIRE(region.amplitudeEG.ccRelease->value == -100.0f); - REQUIRE(region.amplitudeEG.ccStart->value == -100.0f); - REQUIRE(region.amplitudeEG.ccSustain->value == -100.0f); + REQUIRE(region.amplitudeEG.ccAttack->data == -100.0f); + REQUIRE(region.amplitudeEG.ccDecay->data == -100.0f); + REQUIRE(region.amplitudeEG.ccDelay->data == -100.0f); + REQUIRE(region.amplitudeEG.ccHold->data == -100.0f); + REQUIRE(region.amplitudeEG.ccRelease->data == -100.0f); + REQUIRE(region.amplitudeEG.ccStart->data == -100.0f); + REQUIRE(region.amplitudeEG.ccSustain->data == -100.0f); } SECTION("sustain_sw and sostenuto_sw") @@ -1448,10 +1448,10 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.amplitudeCC.empty()); region.parseOpcode({ "amplitude_cc1", "40" }); REQUIRE(region.amplitudeCC.contains(1)); - REQUIRE(region.amplitudeCC[1] == 0.40_a); + REQUIRE(region.amplitudeCC[1].value == 0.40_a); region.parseOpcode({ "amplitude_oncc2", "30" }); REQUIRE(region.amplitudeCC.contains(2)); - REQUIRE(region.amplitudeCC[2] == 0.30_a); + REQUIRE(region.amplitudeCC[2].value == 0.30_a); } SECTION("volume_oncc/gain_cc") @@ -1459,13 +1459,13 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.volumeCC.empty()); region.parseOpcode({ "gain_cc1", "40" }); REQUIRE(region.volumeCC.contains(1)); - REQUIRE(region.volumeCC[1] == 40_a); + REQUIRE(region.volumeCC[1].value == 40_a); region.parseOpcode({ "volume_oncc2", "-76" }); REQUIRE(region.volumeCC.contains(2)); - REQUIRE(region.volumeCC[2] == -76.0_a); + REQUIRE(region.volumeCC[2].value == -76.0_a); region.parseOpcode({ "gain_oncc4", "-1" }); REQUIRE(region.volumeCC.contains(4)); - REQUIRE(region.volumeCC[4] == -1.0_a); + REQUIRE(region.volumeCC[4].value == -1.0_a); } SECTION("tune_cc/pitch_cc") @@ -1473,13 +1473,13 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.tuneCC.empty()); region.parseOpcode({ "pitch_cc1", "40" }); REQUIRE(region.tuneCC.contains(1)); - REQUIRE(region.tuneCC[1] == 40); + REQUIRE(region.tuneCC[1].value == 40.0); region.parseOpcode({ "tune_oncc2", "-76" }); REQUIRE(region.tuneCC.contains(2)); - REQUIRE(region.tuneCC[2] == -76.0); + REQUIRE(region.tuneCC[2].value == -76.0); region.parseOpcode({ "pitch_oncc4", "-1" }); REQUIRE(region.tuneCC.contains(4)); - REQUIRE(region.tuneCC[4] == -1.0); + REQUIRE(region.tuneCC[4].value == -1.0); } } From fbb9ebabb28057ed51e168404828598cc997852a Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 6 Apr 2020 01:22:14 +0200 Subject: [PATCH 045/444] Check that the index passed to the curves arenot too crazy --- src/sfizz/Config.h | 1 + src/sfizz/Curve.cpp | 6 ++++++ tests/CurveT.cpp | 11 +++++++++++ 3 files changed, 18 insertions(+) diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index 727a125fb..48aeb70f6 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -56,6 +56,7 @@ namespace config { constexpr size_t powerHistoryLength { 16 }; constexpr float voiceStealingThreshold { 0.00001f }; constexpr uint16_t numCCs { 512 }; + constexpr int maxCurves { 256 }; constexpr int chunkSize { 1024 }; constexpr int filtersInPool { maxVoices * 2 }; constexpr int filtersPerVoice { 2 }; diff --git a/src/sfizz/Curve.cpp b/src/sfizz/Curve.cpp index 44aa3c24d..f349efadf 100644 --- a/src/sfizz/Curve.cpp +++ b/src/sfizz/Curve.cpp @@ -196,6 +196,12 @@ void CurveSet::addCurve(const Curve& curve, int explicitIndex) { std::unique_ptr* slot; + if (explicitIndex < -1) + return; + + if (explicitIndex >= config::maxCurves) + return; + if (explicitIndex == -1) { if (_useExplicitIndexing) return; // reject implicit indices if any were explicit before diff --git a/tests/CurveT.cpp b/tests/CurveT.cpp index 9c8b054b0..1aa9e161d 100644 --- a/tests/CurveT.cpp +++ b/tests/CurveT.cpp @@ -199,6 +199,17 @@ TEST_CASE("[Curve] Add curves to CurveSet") REQUIRE( curveSet.getCurve(4).evalCC7(0) == 1.0f ); } +TEST_CASE("[Curve] Add bad indices") +{ + sfz::CurveSet curveSet; + curveSet.addCurve(sfz::Curve::buildPredefinedCurve(0), -2); + REQUIRE( curveSet.getNumCurves() == 0 ); + curveSet.addCurve(sfz::Curve::buildPredefinedCurve(0), 256); + REQUIRE( curveSet.getNumCurves() == 0 ); + curveSet.addCurve(sfz::Curve::buildPredefinedCurve(0), 512); + REQUIRE( curveSet.getNumCurves() == 0 ); +} + TEST_CASE("[Curve] Default CurveSet") { auto curveSet = sfz::CurveSet::createPredefined(); From 3620316caf0b45265bc65945f989e50810f46c56 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 6 Apr 2020 01:40:17 +0200 Subject: [PATCH 046/444] Parse curve/step/smooth --- src/sfizz/Defaults.h | 3 ++ src/sfizz/Region.cpp | 112 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index a0cae7a14..822e9cd39 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -81,6 +81,9 @@ namespace Default // Region logic: MIDI conditions constexpr Range channelRange { 1, 16 }; constexpr Range midiChannelRange { 0, 15 }; + constexpr Range stepCCRange { 0, 127 }; + constexpr Range smoothCCRange { 0, 127 }; + constexpr Range curveCCRange { 0, 255 }; constexpr Range ccNumberRange { 0, config::numCCs }; constexpr auto ccValueRange = normalizedRange; constexpr Range bendRange = { -8192, 8192 }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 85b014d22..f150728d5 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -297,6 +297,24 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) case hash("volume"): setValueFromOpcode(opcode, volume, Default::volumeRange); break; + case hash("volume_curvecc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::curveCCRange)) + volumeCC[opcode.parameters.back()].curve = *value; + break; + case hash("volume_stepcc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::stepCCRange)) + volumeCC[opcode.parameters.back()].steps = *value; + break; + case hash("volume_smoothcc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::smoothCCRange)) + volumeCC[opcode.parameters.back()].smooth = *value; + break; case hash("gain_cc&"): case hash("gain_oncc&"): // fallthrough case hash("volume_oncc&"): @@ -309,6 +327,24 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) amplitude = normalizePercents(*value); break; + case hash("amplitude_curvecc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::curveCCRange)) + amplitudeCC[opcode.parameters.back()].curve = *value; + break; + case hash("amplitude_stepcc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::stepCCRange)) + amplitudeCC[opcode.parameters.back()].steps = *value; + break; + case hash("amplitude_smoothcc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::smoothCCRange)) + amplitudeCC[opcode.parameters.back()].smooth = *value; + break; case hash("amplitude_cc&"): // fallthrough case hash("amplitude_oncc&"): if (opcode.parameters.back() > config::numCCs) @@ -320,6 +356,24 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) if (auto value = readOpcode(opcode.value, Default::panRange)) pan = normalizePercents(*value); break; + case hash("pan_curvecc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::curveCCRange)) + panCC[opcode.parameters.back()].curve = *value; + break; + case hash("pan_stepcc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::stepCCRange)) + panCC[opcode.parameters.back()].steps = *value; + break; + case hash("pan_smoothcc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::smoothCCRange)) + panCC[opcode.parameters.back()].smooth = *value; + break; case hash("pan_cc&"): case hash("pan_oncc&"): if (opcode.parameters.back() > config::numCCs) @@ -331,6 +385,25 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) if (auto value = readOpcode(opcode.value, Default::positionRange)) position = normalizePercents(*value); break; + case hash("position_curvecc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::curveCCRange)) + positionCC[opcode.parameters.back()].curve = *value; + break; + case hash("position_stepcc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::stepCCRange)) + positionCC[opcode.parameters.back()].steps = *value; + break; + case hash("position_smoothcc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::smoothCCRange)) + positionCC[opcode.parameters.back()].smooth = *value; + break; + case hash("position_cc&"): // fallthrough case hash("position_oncc&"): if (opcode.parameters.back() > config::numCCs) return false; @@ -341,6 +414,24 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) if (auto value = readOpcode(opcode.value, Default::widthRange)) width = normalizePercents(*value); break; + case hash("width_curvecc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::curveCCRange)) + widthCC[opcode.parameters.back()].curve = *value; + break; + case hash("width_stepcc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::stepCCRange)) + widthCC[opcode.parameters.back()].steps = *value; + break; + case hash("width_smoothcc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::smoothCCRange)) + widthCC[opcode.parameters.back()].smooth = *value; + break; case hash("width_oncc&"): if (opcode.parameters.back() > config::numCCs) return false; @@ -726,6 +817,27 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) case hash("pitch"): setValueFromOpcode(opcode, tune, Default::tuneRange); break; + case hash("pitch_curvecc&"): // fallthrough + case hash("tune_curvecc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::curveCCRange)) + tuneCC[opcode.parameters.back()].curve = *value; + break; + case hash("pitch_stepcc&"): // fallthrough + case hash("tune_stepcc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::stepCCRange)) + tuneCC[opcode.parameters.back()].steps = *value; + break; + case hash("pitch_smoothcc&"): // fallthrough + case hash("tune_smoothcc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::smoothCCRange)) + tuneCC[opcode.parameters.back()].smooth = *value; + break; case hash("tune_cc&"): case hash("tune_oncc&"): case hash("pitch_cc&"): From 0aad8533eaae5a2ecd8fec293de582290cf15375 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 6 Apr 2020 12:20:00 +0200 Subject: [PATCH 047/444] Add helpers to handle curve and cc modifiersfor both linear and multiplicative curves --- src/sfizz/Voice.cpp | 84 +++++++++++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 21 deletions(-) diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 370d29d88..ee3484d10 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -249,6 +249,51 @@ void sfz::Voice::renderBlock(AudioSpan buffer) noexcept #endif } +template +void linearModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData, F&& lambda) +{ + const auto events = resources.midiState.getCCEvents(ccData.cc); + const auto curve = resources.curves.getCurve(ccData.data.curve); + if (ccData.data.steps == 0) { + linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) { + return lambda(curve.evalNormalized(x) * ccData.data.value); + }); + } else { + const float stepSize { ccData.data.value / ccData.data.steps }; + linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) { + return lambda(curve.evalNormalized(x) * ccData.data.value); + }, stepSize); + } +} + +template +void multiplicativeModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData, F&& lambda) +{ + const auto events = resources.midiState.getCCEvents(ccData.cc); + const auto curve = resources.curves.getCurve(ccData.data.curve); + if (ccData.data.steps == 0) { + multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { + return lambda(curve.evalNormalized(x) * ccData.data.value); + }); + } else { + // FIXME: not sure about this step size for multiplicative envelopes + const float stepSize { ccData.data.value / ccData.data.steps }; + multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { + return lambda(curve.evalNormalized(x) * ccData.data.value); + }, stepSize); + } +} + +void linearModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData) +{ + linearModifier(resources, span, ccData, [](float x) { return x; }); +} + +void multiplicativeModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData) +{ + multiplicativeModifier(resources, span, ccData, [](float x) { return x; }); +} + void sfz::Voice::amplitudeEnvelope(absl::Span modulationSpan) noexcept { const auto numSamples = modulationSpan.size(); @@ -264,28 +309,32 @@ void sfz::Voice::amplitudeEnvelope(absl::Span modulationSpan) noexcept // Amplitude envelope applyGain(baseGain, modulationSpan); for (const auto& mod : region->amplitudeCC) { - const auto events = resources.midiState.getCCEvents(mod.cc); - linearEnvelope(events, *tempSpan, [&mod](float x) { return x * mod.data.value; }); + linearModifier(resources, *tempSpan, mod); applyGain(*tempSpan, modulationSpan); } // Crossfade envelopes for (const auto& mod : region->crossfadeCCInRange) { const auto events = resources.midiState.getCCEvents(mod.cc); - linearEnvelope(events, *tempSpan, [&](float x) { return crossfadeIn(mod.data, x, xfCurve); }); + linearEnvelope(events, *tempSpan, [&](float x) { + return crossfadeIn(mod.data, x, xfCurve); + }); applyGain(*tempSpan, modulationSpan); } for (const auto& mod : region->crossfadeCCOutRange) { const auto events = resources.midiState.getCCEvents(mod.cc); - linearEnvelope(events, *tempSpan, [&](float x) { return crossfadeOut(mod.data, x, xfCurve); }); + linearEnvelope(events, *tempSpan, [&](float x) { + return crossfadeOut(mod.data, x, xfCurve); + }); applyGain(*tempSpan, modulationSpan); } // Volume envelope applyGain(db2mag(baseVolumedB), modulationSpan); for (const auto& mod : region->volumeCC) { - const auto events = resources.midiState.getCCEvents(mod.cc); - multiplicativeEnvelope(events, *tempSpan, [&](float x) { return db2mag(x * mod.data.value); }); + multiplicativeModifier(resources, *tempSpan, mod, [](float x) { + return db2mag(x); + }); applyGain(*tempSpan, modulationSpan); } } @@ -337,8 +386,7 @@ void sfz::Voice::panStageMono(AudioSpan buffer) noexcept // Apply panning fill(*modulationSpan, region->pan); for (const auto& mod : region->panCC) { - const auto events = resources.midiState.getCCEvents(mod.cc); - linearEnvelope(events, *tempSpan, [&mod](float x) { return x * mod.data.value; }); + linearModifier(resources, *tempSpan, mod); add(*tempSpan, *modulationSpan); } pan(*modulationSpan, leftBuffer, rightBuffer); @@ -357,30 +405,24 @@ void sfz::Voice::panStageStereo(AudioSpan buffer) noexcept return; // Apply panning - // panningModulation(*modulationSpan); fill(*modulationSpan, region->pan); for (const auto& mod : region->panCC) { - const auto events = resources.midiState.getCCEvents(mod.cc); - linearEnvelope(events, *tempSpan, [&mod](float x) { return x * mod.data.value; }); + linearModifier(resources, *tempSpan, mod); add(*tempSpan, *modulationSpan); } pan(*modulationSpan, leftBuffer, rightBuffer); // Apply the width/position process - // widthModulation(*modulationSpan); fill(*modulationSpan, region->width); for (const auto& mod : region->widthCC) { - const auto events = resources.midiState.getCCEvents(mod.cc); - linearEnvelope(events, *tempSpan, [&mod](float x) { return x * mod.data.value; }); + linearModifier(resources, *tempSpan, mod); add(*tempSpan, *modulationSpan); } width(*modulationSpan, leftBuffer, rightBuffer); - // positionModulation(*modulationSpan); fill(*modulationSpan, region->position); for (const auto& mod : region->positionCC) { - const auto events = resources.midiState.getCCEvents(mod.cc); - linearEnvelope(events, *tempSpan, [&mod](float x) { return x * mod.data.value; }); + linearModifier(resources, *tempSpan, mod); add(*tempSpan, *modulationSpan); } pan(*modulationSpan, leftBuffer, rightBuffer); @@ -457,8 +499,7 @@ void sfz::Voice::fillWithData(AudioSpan buffer) noexcept applyGain(*bends, *jumps); for (const auto& mod : region->tuneCC) { - const auto events = resources.midiState.getCCEvents(mod.cc); - multiplicativeEnvelope(events, *bends, [&](float x) { return centsFactor(x * mod.data.value); }); + multiplicativeModifier(resources, *bends, mod, [](float x) { return centsFactor(x); }); applyGain(*bends, *jumps); } @@ -561,8 +602,9 @@ void sfz::Voice::fillWithGenerator(AudioSpan buffer) noexcept applyGain(*bends, *frequencies); for (const auto& mod : region->tuneCC) { - const auto events = resources.midiState.getCCEvents(mod.cc); - multiplicativeEnvelope(events, *bends, [&](float x) { return centsFactor(x * mod.data.value); }); + multiplicativeModifier(resources, *bends, mod, [](float x) { + return centsFactor(x); + }); applyGain(*bends, *frequencies); } From da6df7db8a87a6a690c8faf8c89cdb1c3f987f78 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 6 Apr 2020 13:07:44 +0200 Subject: [PATCH 048/444] Try to solve the C++11 static asserts --- src/sfizz/SfzHelpers.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index fb1d06360..838ac8631 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -27,7 +27,7 @@ template struct CCData { int cc; ValueType data; - static_assert(config::numCCs - 1 < std::numeric_limits::max()); + static_assert(config::numCCs - 1 < std::numeric_limits::max(), "The cc type in the CCData struct cannot support the required number of CCs"); }; struct Modifier { @@ -35,7 +35,7 @@ struct Modifier { uint8_t curve { 0 }; uint8_t steps { 0 }; uint8_t smooth { 0 }; - static_assert(config::maxCurves - 1 <= std::numeric_limits::max()); + static_assert(config::maxCurves - 1 <= std::numeric_limits::max(), "The curve type in the Modifier struct cannot support the required number of curves"); }; template From 434868e52e466fa189dc4a79b521616d60eaa67b Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 6 Apr 2020 19:07:46 +0200 Subject: [PATCH 049/444] Add a clear() method in the resources --- src/sfizz/Resources.h | 9 +++++++++ src/sfizz/Synth.cpp | 7 ++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/sfizz/Resources.h b/src/sfizz/Resources.h index 2c6adcfce..17f924a97 100644 --- a/src/sfizz/Resources.h +++ b/src/sfizz/Resources.h @@ -40,5 +40,14 @@ struct Resources bufferPool.setBufferSize(samplesPerBlock); midiState.setSamplesPerBlock(samplesPerBlock); } + + void clear() + { + curves = CurveSet::createPredefined(); + filePool.clear(); + wavePool.clearFileWaves(); + logger.clear(); + midiState.reset(); + } }; } diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 5e4d7fe9d..c618f1546 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -130,17 +130,14 @@ void sfz::Synth::clear() list.clear(); for (auto& list : ccActivationLists) list.clear(); + regions.clear(); effectBuses.clear(); effectBuses.emplace_back(new EffectBus); effectBuses[0]->setGainToMain(1.0); effectBuses[0]->setSamplesPerBlock(samplesPerBlock); effectBuses[0]->setSampleRate(sampleRate); - resources.curves = CurveSet::createPredefined(); - resources.filePool.clear(); - resources.wavePool.clearFileWaves(); - resources.logger.clear(); - resources.midiState.reset(); + resources.clear(); numGroups = 0; numMasters = 0; fileTicket = -1; From 9d6b2ede2f490f9d5bd5cd5242931ac9b6bca206 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 6 Apr 2020 19:11:13 +0200 Subject: [PATCH 050/444] Removed the deprecated fileTicket --- src/sfizz/Synth.cpp | 1 - src/sfizz/Synth.h | 1 - 2 files changed, 2 deletions(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index c618f1546..19a3ba5bf 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -140,7 +140,6 @@ void sfz::Synth::clear() resources.clear(); numGroups = 0; numMasters = 0; - fileTicket = -1; defaultSwitch = absl::nullopt; defaultPath = ""; ccNames.clear(); diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index 9f053cc99..4d34d8e80 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -522,7 +522,6 @@ class Synth final : public Parser::Listener { // Distribution used to generate random value for the *rand opcodes std::uniform_real_distribution randNoteDistribution { 0, 1 }; - unsigned fileTicket { 1 }; std::mutex callbackGuard; bool freeWheeling { false }; From 232eada0c4b72c85a94f4a1638f4f7c1aa2796ac Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 6 Apr 2020 19:26:39 +0200 Subject: [PATCH 051/444] Add the modifier helpers to another file --- src/sfizz/ModifierHelpers.h | 234 ++++++++++++++++++++++++++++++++++++ src/sfizz/Region.cpp | 3 +- src/sfizz/Region.h | 4 +- src/sfizz/SfzHelpers.h | 195 ------------------------------ src/sfizz/Voice.cpp | 51 +------- src/sfizz/Voice.h | 5 +- tests/EventEnvelopesT.cpp | 2 +- 7 files changed, 240 insertions(+), 254 deletions(-) create mode 100644 src/sfizz/ModifierHelpers.h diff --git a/src/sfizz/ModifierHelpers.h b/src/sfizz/ModifierHelpers.h new file mode 100644 index 000000000..5dd3fd78b --- /dev/null +++ b/src/sfizz/ModifierHelpers.h @@ -0,0 +1,234 @@ +#pragma once + +#include "Range.h" +#include "Defaults.h" +#include "SfzHelpers.h" +#include "Resources.h" +#include "absl/types/span.h" + +namespace sfz { + +/** + * @brief Compute a crossfade in value with respect to a crossfade range (note, velocity, cc, ...) + */ +template +float crossfadeIn(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCurve curve) +{ + if (value < crossfadeRange.getStart()) + return 0.0f; + + const auto length = static_cast(crossfadeRange.length()); + if (length == 0.0f) + return 1.0f; + + else if (value < crossfadeRange.getEnd()) { + const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / length; + if (curve == SfzCrossfadeCurve::power) + return sqrt(crossfadePosition); + if (curve == SfzCrossfadeCurve::gain) + return crossfadePosition; + } + + return 1.0f; +} + +/** + * @brief Compute a crossfade out value with respect to a crossfade range (note, velocity, cc, ...) + */ +template +float crossfadeOut(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCurve curve) +{ + if (value > crossfadeRange.getEnd()) + return 0.0f; + + const auto length = static_cast(crossfadeRange.length()); + if (length == 0.0f) + return 1.0f; + + else if (value > crossfadeRange.getStart()) { + const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / length; + if (curve == SfzCrossfadeCurve::power) + return std::sqrt(1 - crossfadePosition); + if (curve == SfzCrossfadeCurve::gain) + return 1 - crossfadePosition; + } + + return 1.0f; +} + +template +void linearEnvelope(const EventVector& events, absl::Span envelope, F&& lambda) +{ + ASSERT(events.size() > 0); + ASSERT(events[0].delay == 0); + if (envelope.size() == 0) + return; + + const auto maxDelay = static_cast(envelope.size() - 1); + + auto lastValue = lambda(events[0].value); + auto lastDelay = events[0].delay; + for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) { + const auto length = min(events[i].delay, maxDelay) - lastDelay; + const auto step = (lambda(events[i].value) - lastValue) / length; + lastValue = linearRamp(envelope.subspan(lastDelay, length), lastValue, step); + lastDelay += length; + } + fill(envelope.subspan(lastDelay), lastValue); +} + +template +void linearEnvelope(const EventVector& events, absl::Span envelope, F&& lambda, float step) +{ + ASSERT(events.size() > 0); + ASSERT(events[0].delay == 0); + ASSERT(step != 0.0); + + if (envelope.size() == 0) + return; + + auto quantize = [step](float value) -> float { + return std::round(value / step) * step; + }; + const auto maxDelay = static_cast(envelope.size() - 1); + + auto lastValue = quantize(lambda(events[0].value)); + auto lastDelay = events[0].delay; + for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) { + const auto nextValue = quantize(lambda(events[i].value)); + const auto difference = std::abs(nextValue - lastValue); + const auto length = min(events[i].delay, maxDelay) - lastDelay; + + if (difference < step) { + fill(envelope.subspan(lastDelay, length), lastValue); + lastValue = nextValue; + lastDelay += length; + continue; + } + + const auto numSteps = static_cast(difference / step); + const auto stepLength = static_cast(length / numSteps); + for (int i = 0; i < numSteps; ++i) { + fill(envelope.subspan(lastDelay, stepLength), lastValue); + lastValue += lastValue <= nextValue ? step : -step; + lastDelay += stepLength; + } + } + fill(envelope.subspan(lastDelay), lastValue); +} + +template +void multiplicativeEnvelope(const EventVector& events, absl::Span envelope, F&& lambda) +{ + ASSERT(events.size() > 0); + ASSERT(events[0].delay == 0); + + if (envelope.size() == 0) + return; + const auto maxDelay = static_cast(envelope.size() - 1); + + auto lastValue = lambda(events[0].value); + auto lastDelay = events[0].delay; + for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) { + const auto length = min(events[i].delay, maxDelay) - lastDelay; + const auto nextValue = lambda(events[i].value); + const auto step = std::exp((std::log(nextValue) - std::log(lastValue)) / length); + multiplicativeRamp(envelope.subspan(lastDelay, length), lastValue, step); + lastValue = nextValue; + lastDelay += length; + } + fill(envelope.subspan(lastDelay), lastValue); +} + +template +void multiplicativeEnvelope(const EventVector& events, absl::Span envelope, F&& lambda, float step) +{ + ASSERT(events.size() > 0); + ASSERT(events[0].delay == 0); + ASSERT(step != 0.0f); + + if (envelope.size() == 0) + return; + const auto maxDelay = static_cast(envelope.size() - 1); + + const auto logStep = std::log(step); + // If we assume that a = b.q^r for b in (1, q) then + // log a log b + // ----- = ----- + r + // log q log q + // and log(b)\log(q) is between 0 and 1. + auto quantize = [logStep](float value) -> float { + return std::exp(logStep * std::round(std::log(value) / logStep)); + }; + + auto lastValue = quantize(lambda(events[0].value)); + auto lastDelay = events[0].delay; + for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) { + const auto length = min(events[i].delay, maxDelay) - lastDelay; + const auto nextValue = quantize(lambda(events[i].value)); + const auto difference = nextValue > lastValue ? nextValue / lastValue : lastValue / nextValue; + + if (difference < step) { + fill(envelope.subspan(lastDelay, length), lastValue); + lastValue = nextValue; + lastDelay += length; + continue; + } + + const auto numSteps = static_cast(std::log(difference) / logStep); + const auto stepLength = static_cast(length / numSteps); + for (int i = 0; i < numSteps; ++i) { + fill(envelope.subspan(lastDelay, stepLength), lastValue); + lastValue = nextValue > lastValue ? lastValue * step : lastValue / step; + lastDelay += stepLength; + } + } + fill(envelope.subspan(lastDelay), lastValue); +} + +template +void linearModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData, F&& lambda) +{ + const auto events = resources.midiState.getCCEvents(ccData.cc); + const auto curve = resources.curves.getCurve(ccData.data.curve); + if (ccData.data.steps == 0) { + linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) { + return lambda(curve.evalNormalized(x) * ccData.data.value); + }); + } else { + const float stepSize { ccData.data.value / ccData.data.steps }; + linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) { + return lambda(curve.evalNormalized(x) * ccData.data.value); + }, stepSize); + } +} + +template +void multiplicativeModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData, F&& lambda) +{ + const auto events = resources.midiState.getCCEvents(ccData.cc); + const auto curve = resources.curves.getCurve(ccData.data.curve); + if (ccData.data.steps == 0) { + multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { + return lambda(curve.evalNormalized(x) * ccData.data.value); + }); + } else { + // FIXME: not sure about this step size for multiplicative envelopes + const float stepSize { ccData.data.value / ccData.data.steps }; + multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { + return lambda(curve.evalNormalized(x) * ccData.data.value); + }, stepSize); + } +} + +inline void linearModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData) +{ + linearModifier(resources, span, ccData, [](float x) { return x; }); +} + +inline void multiplicativeModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData) +{ + multiplicativeModifier(resources, span, ccData, [](float x) { return x; }); +} + +} diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index f150728d5..066c75e3c 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -5,13 +5,12 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "Region.h" -#include "Defaults.h" #include "MathHelpers.h" #include "Macros.h" #include "Debug.h" #include "Opcode.h" #include "StringViewHelpers.h" -#include "MidiState.h" +#include "ModifierHelpers.h" #include "absl/strings/str_replace.h" #include "absl/strings/str_cat.h" #include "absl/algorithm/container.h" diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 572d3eeb4..fa9526755 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -14,10 +14,8 @@ #include "Opcode.h" #include "AudioBuffer.h" #include "MidiState.h" -#include "absl/strings/str_cat.h" +#include "absl/types/optional.h" #include -#include -#include #include #include diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index 838ac8631..de19d2ed4 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -264,200 +264,5 @@ bool findDefine(absl::string_view line, absl::string_view& variable, absl::strin */ bool findInclude(absl::string_view line, std::string& path); -/** - * @brief multiply a value by a factor, in cents. To be used for pitch variations. - * - * @param base - * @param modifier - */ -inline CXX14_CONSTEXPR float multiplyByCentsModifier(int modifier, float base) -{ - return base * centsFactor(modifier); -} - -template -inline CXX14_CONSTEXPR float gainModifier(T modifier, float value) -{ - return value * modifier; -} - -/** - * @brief Compute a crossfade in value with respect to a crossfade range (note, velocity, cc, ...) - */ -template -float crossfadeIn(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCurve curve) -{ - if (value < crossfadeRange.getStart()) - return 0.0f; - - const auto length = static_cast(crossfadeRange.length()); - if (length == 0.0f) - return 1.0f; - - else if (value < crossfadeRange.getEnd()) { - const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / length; - if (curve == SfzCrossfadeCurve::power) - return sqrt(crossfadePosition); - if (curve == SfzCrossfadeCurve::gain) - return crossfadePosition; - } - - return 1.0f; -} - -/** - * @brief Compute a crossfade out value with respect to a crossfade range (note, velocity, cc, ...) - */ -template -float crossfadeOut(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCurve curve) -{ - if (value > crossfadeRange.getEnd()) - return 0.0f; - - const auto length = static_cast(crossfadeRange.length()); - if (length == 0.0f) - return 1.0f; - - else if (value > crossfadeRange.getStart()) { - const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / length; - if (curve == SfzCrossfadeCurve::power) - return std::sqrt(1 - crossfadePosition); - if (curve == SfzCrossfadeCurve::gain) - return 1 - crossfadePosition; - } - - return 1.0f; -} - -template -void linearEnvelope(const EventVector& events, absl::Span envelope, F&& lambda) -{ - ASSERT(events.size() > 0); - ASSERT(events[0].delay == 0); - if (envelope.size() == 0) - return; - - const auto maxDelay = static_cast(envelope.size() - 1); - - auto lastValue = lambda(events[0].value); - auto lastDelay = events[0].delay; - for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) { - const auto length = min(events[i].delay, maxDelay) - lastDelay; - const auto step = (lambda(events[i].value) - lastValue) / length; - lastValue = linearRamp(envelope.subspan(lastDelay, length), lastValue, step); - lastDelay += length; - } - fill(envelope.subspan(lastDelay), lastValue); -} - -template -void linearEnvelope(const EventVector& events, absl::Span envelope, F&& lambda, float step) -{ - ASSERT(events.size() > 0); - ASSERT(events[0].delay == 0); - ASSERT(step != 0.0); - - if (envelope.size() == 0) - return; - - auto quantize = [step](float value) -> float { - return std::round(value / step) * step; - }; - const auto maxDelay = static_cast(envelope.size() - 1); - - auto lastValue = quantize(lambda(events[0].value)); - auto lastDelay = events[0].delay; - for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) { - const auto nextValue = quantize(lambda(events[i].value)); - const auto difference = std::abs(nextValue - lastValue); - const auto length = min(events[i].delay, maxDelay) - lastDelay; - - if (difference < step) { - fill(envelope.subspan(lastDelay, length), lastValue); - lastValue = nextValue; - lastDelay += length; - continue; - } - - const auto numSteps = static_cast(difference / step); - const auto stepLength = static_cast(length / numSteps); - for (int i = 0; i < numSteps; ++i) { - fill(envelope.subspan(lastDelay, stepLength), lastValue); - lastValue += lastValue <= nextValue ? step : -step; - lastDelay += stepLength; - } - } - fill(envelope.subspan(lastDelay), lastValue); -} - -template -void multiplicativeEnvelope(const EventVector& events, absl::Span envelope, F&& lambda) -{ - ASSERT(events.size() > 0); - ASSERT(events[0].delay == 0); - - if (envelope.size() == 0) - return; - const auto maxDelay = static_cast(envelope.size() - 1); - - auto lastValue = lambda(events[0].value); - auto lastDelay = events[0].delay; - for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) { - const auto length = min(events[i].delay, maxDelay) - lastDelay; - const auto nextValue = lambda(events[i].value); - const auto step = std::exp((std::log(nextValue) - std::log(lastValue)) / length); - multiplicativeRamp(envelope.subspan(lastDelay, length), lastValue, step); - lastValue = nextValue; - lastDelay += length; - } - fill(envelope.subspan(lastDelay), lastValue); -} - -template -void multiplicativeEnvelope(const EventVector& events, absl::Span envelope, F&& lambda, float step) -{ - ASSERT(events.size() > 0); - ASSERT(events[0].delay == 0); - ASSERT(step != 0.0f); - - if (envelope.size() == 0) - return; - const auto maxDelay = static_cast(envelope.size() - 1); - - const auto logStep = std::log(step); - // If we assume that a = b.q^r for b in (1, q) then - // log a log b - // ----- = ----- + r - // log q log q - // and log(b)\log(q) is between 0 and 1. - auto quantize = [logStep](float value) -> float { - return std::exp(logStep * std::round(std::log(value) / logStep)); - }; - - auto lastValue = quantize(lambda(events[0].value)); - auto lastDelay = events[0].delay; - for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) { - const auto length = min(events[i].delay, maxDelay) - lastDelay; - const auto nextValue = quantize(lambda(events[i].value)); - const auto difference = nextValue > lastValue ? nextValue / lastValue : lastValue / nextValue; - - if (difference < step) { - fill(envelope.subspan(lastDelay, length), lastValue); - lastValue = nextValue; - lastDelay += length; - continue; - } - - const auto numSteps = static_cast(std::log(difference) / logStep); - const auto stepLength = static_cast(length / numSteps); - for (int i = 0; i < numSteps; ++i) { - fill(envelope.subspan(lastDelay, stepLength), lastValue); - lastValue = nextValue > lastValue ? lastValue * step : lastValue / step; - lastDelay += stepLength; - } - } - fill(envelope.subspan(lastDelay), lastValue); -} - } // namespace sfz diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index ee3484d10..d7deb618d 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -4,16 +4,14 @@ // license. You should have receive a LICENSE.md file along with the code. // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz -#include "Macros.h" #include "Voice.h" -#include "AudioSpan.h" -#include "Config.h" +#include "Macros.h" #include "Defaults.h" +#include "ModifierHelpers.h" #include "MathHelpers.h" #include "SIMDHelpers.h" #include "SfzHelpers.h" #include "absl/algorithm/container.h" -#include sfz::Voice::Voice(sfz::Resources& resources) : resources(resources) @@ -249,51 +247,6 @@ void sfz::Voice::renderBlock(AudioSpan buffer) noexcept #endif } -template -void linearModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData, F&& lambda) -{ - const auto events = resources.midiState.getCCEvents(ccData.cc); - const auto curve = resources.curves.getCurve(ccData.data.curve); - if (ccData.data.steps == 0) { - linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) { - return lambda(curve.evalNormalized(x) * ccData.data.value); - }); - } else { - const float stepSize { ccData.data.value / ccData.data.steps }; - linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) { - return lambda(curve.evalNormalized(x) * ccData.data.value); - }, stepSize); - } -} - -template -void multiplicativeModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData, F&& lambda) -{ - const auto events = resources.midiState.getCCEvents(ccData.cc); - const auto curve = resources.curves.getCurve(ccData.data.curve); - if (ccData.data.steps == 0) { - multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { - return lambda(curve.evalNormalized(x) * ccData.data.value); - }); - } else { - // FIXME: not sure about this step size for multiplicative envelopes - const float stepSize { ccData.data.value / ccData.data.steps }; - multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { - return lambda(curve.evalNormalized(x) * ccData.data.value); - }, stepSize); - } -} - -void linearModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData) -{ - linearModifier(resources, span, ccData, [](float x) { return x; }); -} - -void multiplicativeModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData) -{ - multiplicativeModifier(resources, span, ccData, [](float x) { return x; }); -} - void sfz::Voice::amplitudeEnvelope(absl::Span modulationSpan) noexcept { const auto numSamples = modulationSpan.size(); diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 1723c4165..b4e61c5ef 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -10,13 +10,10 @@ #include "HistoricalBuffer.h" #include "Region.h" #include "AudioBuffer.h" -#include "MidiState.h" -#include "Wavetables.h" #include "Resources.h" #include "AudioSpan.h" #include "LeakDetector.h" -#include -#include +#include "absl/types/span.h" #include #include diff --git a/tests/EventEnvelopesT.cpp b/tests/EventEnvelopesT.cpp index 545ea5947..10954504b 100644 --- a/tests/EventEnvelopesT.cpp +++ b/tests/EventEnvelopesT.cpp @@ -4,7 +4,7 @@ // license. You should have receive a LICENSE.md file along with the code. // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz -#include "sfizz/SfzHelpers.h" +#include "sfizz/ModifierHelpers.h" #include "sfizz/Buffer.h" #include "catch2/catch.hpp" #include From 5e6584a51a61b2613788fbed65f2d7e8e9b382a0 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 6 Apr 2020 19:41:10 +0200 Subject: [PATCH 052/444] Basic parsing tests --- tests/RegionT.cpp | 108 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index b98f9f51d..31e58fcd9 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -471,6 +471,24 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "pan_oncc45", "4.2" }); REQUIRE(region.panCC.contains(45)); REQUIRE(region.panCC[45].value == 0.042_a); + region.parseOpcode({ "pan_curvecc17", "18" }); + REQUIRE(region.panCC[17].curve == 18); + region.parseOpcode({ "pan_curvecc17", "15482" }); + REQUIRE(region.panCC[17].curve == 255); + region.parseOpcode({ "pan_curvecc17", "-2" }); + REQUIRE(region.panCC[17].curve == 0); + region.parseOpcode({ "pan_smoothcc14", "85" }); + REQUIRE(region.panCC[14].smooth == 85); + region.parseOpcode({ "pan_smoothcc14", "15482" }); + REQUIRE(region.panCC[14].smooth == 127); + region.parseOpcode({ "pan_smoothcc14", "-2" }); + REQUIRE(region.panCC[14].smooth == 0); + region.parseOpcode({ "pan_stepcc120", "24" }); + REQUIRE(region.panCC[120].steps == 24); + region.parseOpcode({ "pan_stepcc120", "15482" }); + REQUIRE(region.panCC[120].steps == 127); + region.parseOpcode({ "pan_stepcc120", "-2" }); + REQUIRE(region.panCC[120].steps == 0); } SECTION("width") @@ -492,6 +510,24 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "width_oncc45", "4.2" }); REQUIRE(region.widthCC.contains(45)); REQUIRE(region.widthCC[45].value == 0.042_a); + region.parseOpcode({ "width_curvecc17", "18" }); + REQUIRE(region.widthCC[17].curve == 18); + region.parseOpcode({ "width_curvecc17", "15482" }); + REQUIRE(region.widthCC[17].curve == 255); + region.parseOpcode({ "width_curvecc17", "-2" }); + REQUIRE(region.widthCC[17].curve == 0); + region.parseOpcode({ "width_smoothcc14", "85" }); + REQUIRE(region.widthCC[14].smooth == 85); + region.parseOpcode({ "width_smoothcc14", "15482" }); + REQUIRE(region.widthCC[14].smooth == 127); + region.parseOpcode({ "width_smoothcc14", "-2" }); + REQUIRE(region.widthCC[14].smooth == 0); + region.parseOpcode({ "width_stepcc120", "24" }); + REQUIRE(region.widthCC[120].steps == 24); + region.parseOpcode({ "width_stepcc120", "15482" }); + REQUIRE(region.widthCC[120].steps == 127); + region.parseOpcode({ "width_stepcc120", "-2" }); + REQUIRE(region.widthCC[120].steps == 0); } SECTION("position") @@ -513,6 +549,24 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "position_oncc45", "4.2" }); REQUIRE(region.positionCC.contains(45)); REQUIRE(region.positionCC[45].value == 0.042_a); + region.parseOpcode({ "position_curvecc17", "18" }); + REQUIRE(region.positionCC[17].curve == 18); + region.parseOpcode({ "position_curvecc17", "15482" }); + REQUIRE(region.positionCC[17].curve == 255); + region.parseOpcode({ "position_curvecc17", "-2" }); + REQUIRE(region.positionCC[17].curve == 0); + region.parseOpcode({ "position_smoothcc14", "85" }); + REQUIRE(region.positionCC[14].smooth == 85); + region.parseOpcode({ "position_smoothcc14", "15482" }); + REQUIRE(region.positionCC[14].smooth == 127); + region.parseOpcode({ "position_smoothcc14", "-2" }); + REQUIRE(region.positionCC[14].smooth == 0); + region.parseOpcode({ "position_stepcc120", "24" }); + REQUIRE(region.positionCC[120].steps == 24); + region.parseOpcode({ "position_stepcc120", "15482" }); + REQUIRE(region.positionCC[120].steps == 127); + region.parseOpcode({ "position_stepcc120", "-2" }); + REQUIRE(region.positionCC[120].steps == 0); } SECTION("amp_keycenter") @@ -1452,6 +1506,24 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "amplitude_oncc2", "30" }); REQUIRE(region.amplitudeCC.contains(2)); REQUIRE(region.amplitudeCC[2].value == 0.30_a); + region.parseOpcode({ "amplitude_curvecc17", "18" }); + REQUIRE(region.amplitudeCC[17].curve == 18); + region.parseOpcode({ "amplitude_curvecc17", "15482" }); + REQUIRE(region.amplitudeCC[17].curve == 255); + region.parseOpcode({ "amplitude_curvecc17", "-2" }); + REQUIRE(region.amplitudeCC[17].curve == 0); + region.parseOpcode({ "amplitude_smoothcc14", "85" }); + REQUIRE(region.amplitudeCC[14].smooth == 85); + region.parseOpcode({ "amplitude_smoothcc14", "15482" }); + REQUIRE(region.amplitudeCC[14].smooth == 127); + region.parseOpcode({ "amplitude_smoothcc14", "-2" }); + REQUIRE(region.amplitudeCC[14].smooth == 0); + region.parseOpcode({ "amplitude_stepcc120", "24" }); + REQUIRE(region.amplitudeCC[120].steps == 24); + region.parseOpcode({ "amplitude_stepcc120", "15482" }); + REQUIRE(region.amplitudeCC[120].steps == 127); + region.parseOpcode({ "amplitude_stepcc120", "-2" }); + REQUIRE(region.amplitudeCC[120].steps == 0); } SECTION("volume_oncc/gain_cc") @@ -1466,6 +1538,24 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "gain_oncc4", "-1" }); REQUIRE(region.volumeCC.contains(4)); REQUIRE(region.volumeCC[4].value == -1.0_a); + region.parseOpcode({ "volume_curvecc17", "18" }); + REQUIRE(region.volumeCC[17].curve == 18); + region.parseOpcode({ "volume_curvecc17", "15482" }); + REQUIRE(region.volumeCC[17].curve == 255); + region.parseOpcode({ "volume_curvecc17", "-2" }); + REQUIRE(region.volumeCC[17].curve == 0); + region.parseOpcode({ "volume_smoothcc14", "85" }); + REQUIRE(region.volumeCC[14].smooth == 85); + region.parseOpcode({ "volume_smoothcc14", "15482" }); + REQUIRE(region.volumeCC[14].smooth == 127); + region.parseOpcode({ "volume_smoothcc14", "-2" }); + REQUIRE(region.volumeCC[14].smooth == 0); + region.parseOpcode({ "volume_stepcc120", "24" }); + REQUIRE(region.volumeCC[120].steps == 24); + region.parseOpcode({ "volume_stepcc120", "15482" }); + REQUIRE(region.volumeCC[120].steps == 127); + region.parseOpcode({ "volume_stepcc120", "-2" }); + REQUIRE(region.volumeCC[120].steps == 0); } SECTION("tune_cc/pitch_cc") @@ -1480,6 +1570,24 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "pitch_oncc4", "-1" }); REQUIRE(region.tuneCC.contains(4)); REQUIRE(region.tuneCC[4].value == -1.0); + region.parseOpcode({ "tune_curvecc17", "18" }); + REQUIRE(region.tuneCC[17].curve == 18); + region.parseOpcode({ "pitch_curvecc17", "15482" }); + REQUIRE(region.tuneCC[17].curve == 255); + region.parseOpcode({ "tune_curvecc17", "-2" }); + REQUIRE(region.tuneCC[17].curve == 0); + region.parseOpcode({ "pitch_smoothcc14", "85" }); + REQUIRE(region.tuneCC[14].smooth == 85); + region.parseOpcode({ "tune_smoothcc14", "15482" }); + REQUIRE(region.tuneCC[14].smooth == 127); + region.parseOpcode({ "pitch_smoothcc14", "-2" }); + REQUIRE(region.tuneCC[14].smooth == 0); + region.parseOpcode({ "tune_stepcc120", "24" }); + REQUIRE(region.tuneCC[120].steps == 24); + region.parseOpcode({ "pitch_stepcc120", "15482" }); + REQUIRE(region.tuneCC[120].steps == 127); + region.parseOpcode({ "tune_stepcc120", "-2" }); + REQUIRE(region.tuneCC[120].steps == 0); } } From e975877569b9768e392d58b99cca330822e14fd6 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 6 Apr 2020 20:12:59 +0200 Subject: [PATCH 053/444] - Add some expected behavior tests- Correct the evalNormalized in curves- Remove the no-lambda multiplicative modifiers... it's a trap that makes you create nans and infs --- src/sfizz/Curve.h | 2 +- src/sfizz/ModifierHelpers.h | 8 +-- tests/CurveT.cpp | 8 +-- tests/EventEnvelopesT.cpp | 101 ++++++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 12 deletions(-) diff --git a/src/sfizz/Curve.h b/src/sfizz/Curve.h index ad9667a30..829a2d8b2 100644 --- a/src/sfizz/Curve.h +++ b/src/sfizz/Curve.h @@ -45,7 +45,7 @@ class Curve { */ float evalNormalized(float value) const { - return evalCC7(denormalize7Bits(value)); + return evalCC7(value * 127.0f); } /** diff --git a/src/sfizz/ModifierHelpers.h b/src/sfizz/ModifierHelpers.h index 5dd3fd78b..02502fcc0 100644 --- a/src/sfizz/ModifierHelpers.h +++ b/src/sfizz/ModifierHelpers.h @@ -214,7 +214,7 @@ void multiplicativeModifier(const sfz::Resources& resources, absl::Span s }); } else { // FIXME: not sure about this step size for multiplicative envelopes - const float stepSize { ccData.data.value / ccData.data.steps }; + const float stepSize { lambda(ccData.data.value / ccData.data.steps) }; multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { return lambda(curve.evalNormalized(x) * ccData.data.value); }, stepSize); @@ -225,10 +225,4 @@ inline void linearModifier(const sfz::Resources& resources, absl::Span sp { linearModifier(resources, span, ccData, [](float x) { return x; }); } - -inline void multiplicativeModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData) -{ - multiplicativeModifier(resources, span, ccData, [](float x) { return x; }); -} - } diff --git a/tests/CurveT.cpp b/tests/CurveT.cpp index 1aa9e161d..4c3c9f9d9 100644 --- a/tests/CurveT.cpp +++ b/tests/CurveT.cpp @@ -33,7 +33,7 @@ TEST_CASE("[Curve] Bipolar -1 to 1") REQUIRE( curve.evalCC7(85) == Approx(0.3386).margin(1e-3) ); REQUIRE( curve.evalNormalized(0.0f) == -1.0f ); REQUIRE( curve.evalNormalized(1.0f) == 1.0f ); - REQUIRE( curve.evalNormalized(0.3f) == Approx(-0.402).margin(1e-3) ); + REQUIRE( curve.evalNormalized(0.3f) == Approx(-0.4).margin(1e-3) ); } TEST_CASE("[Curve] Bipolar 1 to 0") @@ -59,7 +59,7 @@ TEST_CASE("[Curve] Bipolar 1 to -1") REQUIRE( curve.evalCC7(85) == Approx(-0.3386).margin(1e-3) ); REQUIRE( curve.evalNormalized(0.0f) == 1.0f ); REQUIRE( curve.evalNormalized(1.0f) == -1.0f ); - REQUIRE( curve.evalNormalized(0.3f) == Approx(0.402).margin(1e-3) ); + REQUIRE( curve.evalNormalized(0.3f) == Approx(0.4).margin(1e-3) ); } TEST_CASE("[Curve] x**2") @@ -220,7 +220,7 @@ TEST_CASE("[Curve] Default CurveSet") REQUIRE( curveSet.getCurve(1).evalNormalized(0.0f) == -1.0f ); REQUIRE( curveSet.getCurve(1).evalNormalized(1.0f) == 1.0f ); - REQUIRE( curveSet.getCurve(1).evalNormalized(0.3f) == Approx(-0.402).margin(1e-3) ); + REQUIRE( curveSet.getCurve(1).evalNormalized(0.3f) == Approx(-0.4).margin(1e-3) ); REQUIRE( curveSet.getCurve(2).evalNormalized(0.0f) == 1.0f ); REQUIRE( curveSet.getCurve(2).evalNormalized(1.0f) == 0.0f ); @@ -228,7 +228,7 @@ TEST_CASE("[Curve] Default CurveSet") REQUIRE( curveSet.getCurve(3).evalNormalized(0.0f) == 1.0f ); REQUIRE( curveSet.getCurve(3).evalNormalized(1.0f) == -1.0f ); - REQUIRE( curveSet.getCurve(3).evalNormalized(0.3f) == Approx(0.402).margin(1e-3) ); + REQUIRE( curveSet.getCurve(3).evalNormalized(0.3f) == Approx(0.4).margin(1e-3) ); REQUIRE( curveSet.getCurve(4).evalNormalized(0.0f) == 0.0f ); REQUIRE( curveSet.getCurve(4).evalNormalized(1.0f) == 1.0f ); diff --git a/tests/EventEnvelopesT.cpp b/tests/EventEnvelopesT.cpp index 10954504b..71df4a79e 100644 --- a/tests/EventEnvelopesT.cpp +++ b/tests/EventEnvelopesT.cpp @@ -273,3 +273,104 @@ TEST_CASE("[MultiplicativeEnvelope] Get quantized with unclean events") multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f); REQUIRE(output == expected); } + +TEST_CASE("[linearModifiers] Compare with envelopes") +{ + sfz::Resources resources; + resources.curves = sfz::CurveSet::createPredefined(); + + sfz::CCData ccData; + ccData.cc = 20; + ccData.data.value = 100.0f; + + resources.midiState.ccEvent(5, 20, 0.1); + resources.midiState.ccEvent(10, 20, 0.2); + + std::array output; + std::array envelope; + + linearEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { + return ccData.data.value * x; + }); + linearModifier(resources, absl::MakeSpan(output), ccData); + REQUIRE(approxEqual(output, envelope)); + + ccData.data.curve = 1; + linearEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { + return ccData.data.value * (2 * x - 1); + }); + linearModifier(resources, absl::MakeSpan(output), ccData); + REQUIRE(approxEqual(output, envelope)); + + ccData.data.curve = 3; + ccData.data.value = 10.0f; + linearEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { + return ccData.data.value * (1 - 2 * x); + }); + linearModifier(resources, absl::MakeSpan(output), ccData); + REQUIRE(approxEqual(output, envelope)); + + ccData.data.curve = 2; + ccData.data.value = 20.0f; + ccData.data.steps = 10; + linearEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { + return ccData.data.value * (1 - x); + }, ccData.data.value / ccData.data.steps); + linearModifier(resources, absl::MakeSpan(output), ccData); + REQUIRE(approxEqual(output, envelope)); +} + +TEST_CASE("[multiplicativeModifiers] Compare with envelopes") +{ + sfz::Resources resources; + resources.curves = sfz::CurveSet::createPredefined(); + + sfz::CCData ccData; + ccData.cc = 20; + ccData.data.value = 100.0f; + + resources.midiState.ccEvent(5, 20, 0.1); + resources.midiState.ccEvent(10, 20, 0.2); + + std::array output; + std::array envelope; + + multiplicativeEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { + return db2mag(ccData.data.value * x); + }); + multiplicativeModifier(resources, absl::MakeSpan(output), ccData, [](float x) { + return db2mag(x); + }); + REQUIRE(approxEqual(output, envelope)); + + ccData.data.curve = 1; + multiplicativeEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { + return db2mag(ccData.data.value * (2 * x - 1)); + }); + multiplicativeModifier(resources, absl::MakeSpan(output), ccData, [](float x) { + return db2mag(x); + }); + REQUIRE(approxEqual(output, envelope)); + + ccData.data.curve = 3; + ccData.data.value = 10.0f; + multiplicativeEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { + return db2mag(ccData.data.value * (1 - 2 * x)); + }); + multiplicativeModifier(resources, absl::MakeSpan(output), ccData, [](float x) { + return db2mag(x); + }); + REQUIRE(approxEqual(output, envelope)); + + ccData.data.curve = 2; + ccData.data.value = 20.0f; + ccData.data.steps = 10; + multiplicativeEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { + return db2mag(ccData.data.value * (1 - x)); + }, db2mag(ccData.data.value / ccData.data.steps) ); + multiplicativeModifier(resources, absl::MakeSpan(output), ccData, [](float x) { + return db2mag(x); + }); + REQUIRE(approxEqual(output, envelope)); +} + From b9ba7fab0b50d3053da88b9d25a857d7bc8d7a0b Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 00:21:47 +0200 Subject: [PATCH 054/444] Match sforzando's behavior (use floor on steps) --- src/sfizz/ModifierHelpers.h | 12 ++++++------ tests/EventEnvelopesT.cpp | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sfizz/ModifierHelpers.h b/src/sfizz/ModifierHelpers.h index 02502fcc0..6aa757fe0 100644 --- a/src/sfizz/ModifierHelpers.h +++ b/src/sfizz/ModifierHelpers.h @@ -88,7 +88,7 @@ void linearEnvelope(const EventVector& events, absl::Span envelope, F&& l return; auto quantize = [step](float value) -> float { - return std::round(value / step) * step; + return std::floor(value / step) * step; }; const auto maxDelay = static_cast(envelope.size() - 1); @@ -158,7 +158,7 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span envelop // log q log q // and log(b)\log(q) is between 0 and 1. auto quantize = [logStep](float value) -> float { - return std::exp(logStep * std::round(std::log(value) / logStep)); + return std::exp(logStep * std::floor(std::log(value) / logStep)); }; auto lastValue = quantize(lambda(events[0].value)); @@ -191,12 +191,12 @@ void linearModifier(const sfz::Resources& resources, absl::Span span, con { const auto events = resources.midiState.getCCEvents(ccData.cc); const auto curve = resources.curves.getCurve(ccData.data.curve); - if (ccData.data.steps == 0) { + if (ccData.data.steps < 2) { linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) { return lambda(curve.evalNormalized(x) * ccData.data.value); }); } else { - const float stepSize { ccData.data.value / ccData.data.steps }; + const float stepSize { ccData.data.value / (ccData.data.steps - 1) }; linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) { return lambda(curve.evalNormalized(x) * ccData.data.value); }, stepSize); @@ -208,13 +208,13 @@ void multiplicativeModifier(const sfz::Resources& resources, absl::Span s { const auto events = resources.midiState.getCCEvents(ccData.cc); const auto curve = resources.curves.getCurve(ccData.data.curve); - if (ccData.data.steps == 0) { + if (ccData.data.steps < 2) { multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { return lambda(curve.evalNormalized(x) * ccData.data.value); }); } else { // FIXME: not sure about this step size for multiplicative envelopes - const float stepSize { lambda(ccData.data.value / ccData.data.steps) }; + const float stepSize { lambda(ccData.data.value / (ccData.data.steps - 1)) }; multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { return lambda(curve.evalNormalized(x) * ccData.data.value); }, stepSize); diff --git a/tests/EventEnvelopesT.cpp b/tests/EventEnvelopesT.cpp index 71df4a79e..634d98d60 100644 --- a/tests/EventEnvelopesT.cpp +++ b/tests/EventEnvelopesT.cpp @@ -137,7 +137,7 @@ TEST_CASE("[LinearEnvelope] Get quantized with unquantized targets") { 6, 1.9f } }; std::array output; - std::array expected { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f }; + std::array expected { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); REQUIRE(output == expected); } @@ -315,7 +315,7 @@ TEST_CASE("[linearModifiers] Compare with envelopes") ccData.data.steps = 10; linearEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { return ccData.data.value * (1 - x); - }, ccData.data.value / ccData.data.steps); + }, ccData.data.value / (ccData.data.steps - 1)); linearModifier(resources, absl::MakeSpan(output), ccData); REQUIRE(approxEqual(output, envelope)); } @@ -367,7 +367,7 @@ TEST_CASE("[multiplicativeModifiers] Compare with envelopes") ccData.data.steps = 10; multiplicativeEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { return db2mag(ccData.data.value * (1 - x)); - }, db2mag(ccData.data.value / ccData.data.steps) ); + }, db2mag(ccData.data.value / (ccData.data.steps - 1)) ); multiplicativeModifier(resources, absl::MakeSpan(output), ccData, [](float x) { return db2mag(x); }); From 6bce14673e6bb6439f10e688360acdbf473c9e73 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 00:42:11 +0200 Subject: [PATCH 055/444] Saner behavior for the quantized envelopes with bends --- src/sfizz/ModifierHelpers.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sfizz/ModifierHelpers.h b/src/sfizz/ModifierHelpers.h index 6aa757fe0..0ee1e9006 100644 --- a/src/sfizz/ModifierHelpers.h +++ b/src/sfizz/ModifierHelpers.h @@ -158,7 +158,10 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span envelop // log q log q // and log(b)\log(q) is between 0 and 1. auto quantize = [logStep](float value) -> float { - return std::exp(logStep * std::floor(std::log(value) / logStep)); + if (value > 1) + return std::exp(logStep * std::floor(std::log(value) / logStep)); + else + return std::exp(logStep * std::ceil(std::log(value) / logStep)); }; auto lastValue = quantize(lambda(events[0].value)); From 872e7c68e05d781d6a1389e214fcfd9fca85e552 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 00:50:30 +0200 Subject: [PATCH 056/444] Use round rather than trunc; more stable.. --- src/sfizz/ModifierHelpers.h | 5 +---- tests/EventEnvelopesT.cpp | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/sfizz/ModifierHelpers.h b/src/sfizz/ModifierHelpers.h index 0ee1e9006..1a5d5abaf 100644 --- a/src/sfizz/ModifierHelpers.h +++ b/src/sfizz/ModifierHelpers.h @@ -5,7 +5,6 @@ #include "SfzHelpers.h" #include "Resources.h" #include "absl/types/span.h" - namespace sfz { /** @@ -150,7 +149,6 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span envelop if (envelope.size() == 0) return; const auto maxDelay = static_cast(envelope.size() - 1); - const auto logStep = std::log(step); // If we assume that a = b.q^r for b in (1, q) then // log a log b @@ -178,7 +176,7 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span envelop continue; } - const auto numSteps = static_cast(std::log(difference) / logStep); + const auto numSteps = std::round(std::log(difference) / logStep); const auto stepLength = static_cast(length / numSteps); for (int i = 0; i < numSteps; ++i) { fill(envelope.subspan(lastDelay, stepLength), lastValue); @@ -216,7 +214,6 @@ void multiplicativeModifier(const sfz::Resources& resources, absl::Span s return lambda(curve.evalNormalized(x) * ccData.data.value); }); } else { - // FIXME: not sure about this step size for multiplicative envelopes const float stepSize { lambda(ccData.data.value / (ccData.data.steps - 1)) }; multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { return lambda(curve.evalNormalized(x) * ccData.data.value); diff --git a/tests/EventEnvelopesT.cpp b/tests/EventEnvelopesT.cpp index 634d98d60..a2dc6b306 100644 --- a/tests/EventEnvelopesT.cpp +++ b/tests/EventEnvelopesT.cpp @@ -330,7 +330,7 @@ TEST_CASE("[multiplicativeModifiers] Compare with envelopes") ccData.data.value = 100.0f; resources.midiState.ccEvent(5, 20, 0.1); - resources.midiState.ccEvent(10, 20, 0.2); + resources.midiState.ccEvent(10, 20, 0.8); std::array output; std::array envelope; From e03f1a8038fde6d4f0ee0894b0562e400abe6ab1 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 00:50:38 +0200 Subject: [PATCH 057/444] Repair benchmark --- benchmarks/BM_envelopes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/BM_envelopes.cpp b/benchmarks/BM_envelopes.cpp index 836d6b0a4..1fa3ae919 100644 --- a/benchmarks/BM_envelopes.cpp +++ b/benchmarks/BM_envelopes.cpp @@ -10,7 +10,7 @@ #include #include #include -#include "SfzHelpers.h" +#include "ModifierHelpers.h" #include "absl/types/span.h" class EnvelopeFixture : public benchmark::Fixture { From b3dd8b3ce91ae8158a5b45cde8117aae3043dcb4 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 13 Apr 2020 23:33:57 +0200 Subject: [PATCH 058/444] Use trunc in modifiers --- src/sfizz/ModifierHelpers.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/sfizz/ModifierHelpers.h b/src/sfizz/ModifierHelpers.h index 1a5d5abaf..4aaffa753 100644 --- a/src/sfizz/ModifierHelpers.h +++ b/src/sfizz/ModifierHelpers.h @@ -87,7 +87,7 @@ void linearEnvelope(const EventVector& events, absl::Span envelope, F&& l return; auto quantize = [step](float value) -> float { - return std::floor(value / step) * step; + return std::trunc(value / step) * step; }; const auto maxDelay = static_cast(envelope.size() - 1); @@ -156,10 +156,7 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span envelop // log q log q // and log(b)\log(q) is between 0 and 1. auto quantize = [logStep](float value) -> float { - if (value > 1) - return std::exp(logStep * std::floor(std::log(value) / logStep)); - else - return std::exp(logStep * std::ceil(std::log(value) / logStep)); + return std::exp(logStep * std::trunc(std::log(value) / logStep)); }; auto lastValue = quantize(lambda(events[0].value)); From 6a7977b6c253ce4abc01b021b7a3c7213cc29831 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Tue, 14 Apr 2020 13:41:24 +0200 Subject: [PATCH 059/444] Stepcc actually gives the step and not the number of steps... --- src/sfizz/Defaults.h | 5 +++++ src/sfizz/ModifierHelpers.h | 8 ++++---- src/sfizz/Region.cpp | 24 +++++++++++------------ src/sfizz/SfzHelpers.h | 2 +- tests/EventEnvelopesT.cpp | 8 ++++---- tests/RegionT.cpp | 38 ++++++++++++++++++------------------- 6 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 822e9cd39..8506d90f2 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -110,17 +110,21 @@ namespace Default constexpr float volume { 0.0f }; constexpr Range volumeRange { -144.0, 6.0 }; constexpr Range volumeCCRange { -144.0, 48.0 }; + constexpr Range volumeStepRange { 0, 48.0 }; constexpr float amplitude { 100.0 }; constexpr Range amplitudeRange { 0.0, 100.0 }; constexpr float pan { 0.0 }; constexpr Range panRange { -100.0, 100.0 }; constexpr Range panCCRange { -200.0, 200.0 }; + constexpr Range panStepRange { 0.0, 200.0 }; constexpr float position { 0.0 }; constexpr Range positionRange { -100.0, 100.0 }; constexpr Range positionCCRange { -200.0, 200.0 }; + constexpr Range positionStepRange { 0.0, 200.0 }; constexpr float width { 100.0 }; constexpr Range widthRange { -100.0, 100.0 }; constexpr Range widthCCRange { -200.0, 200.0 }; + constexpr Range widthStepRange { 0.0, 200.0 }; constexpr uint8_t ampKeycenter { 60 }; constexpr float ampKeytrack { 0.0 }; constexpr Range ampKeytrackRange { -96, 12 }; @@ -196,6 +200,7 @@ namespace Default constexpr int tune { 0 }; constexpr Range tuneRange { -9600, 9600 }; // ±100 in SFZv1, more in ARIA constexpr Range tuneCCRange { -9600, 9600 }; + constexpr Range tuneStepRange { 0, 9600 }; constexpr Range bendBoundRange { -9600, 9600 }; constexpr Range bendStepRange { 1, 1200 }; constexpr int bendUp { 200 }; // No range here because the bounds can be inverted diff --git a/src/sfizz/ModifierHelpers.h b/src/sfizz/ModifierHelpers.h index 4aaffa753..b46bbc617 100644 --- a/src/sfizz/ModifierHelpers.h +++ b/src/sfizz/ModifierHelpers.h @@ -189,12 +189,12 @@ void linearModifier(const sfz::Resources& resources, absl::Span span, con { const auto events = resources.midiState.getCCEvents(ccData.cc); const auto curve = resources.curves.getCurve(ccData.data.curve); - if (ccData.data.steps < 2) { + if (ccData.data.step == 0.0f) { linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) { return lambda(curve.evalNormalized(x) * ccData.data.value); }); } else { - const float stepSize { ccData.data.value / (ccData.data.steps - 1) }; + const float stepSize { lambda(ccData.data.step) }; linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) { return lambda(curve.evalNormalized(x) * ccData.data.value); }, stepSize); @@ -206,12 +206,12 @@ void multiplicativeModifier(const sfz::Resources& resources, absl::Span s { const auto events = resources.midiState.getCCEvents(ccData.cc); const auto curve = resources.curves.getCurve(ccData.data.curve); - if (ccData.data.steps < 2) { + if (ccData.data.step == 0.0f) { multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { return lambda(curve.evalNormalized(x) * ccData.data.value); }); } else { - const float stepSize { lambda(ccData.data.value / (ccData.data.steps - 1)) }; + const float stepSize { lambda(ccData.data.step) }; multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { return lambda(curve.evalNormalized(x) * ccData.data.value); }, stepSize); diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 066c75e3c..3411e9159 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -305,8 +305,8 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) case hash("volume_stepcc&"): if (opcode.parameters.back() > config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::stepCCRange)) - volumeCC[opcode.parameters.back()].steps = *value; + if (auto value = readOpcode(opcode.value, Default::volumeStepRange)) + volumeCC[opcode.parameters.back()].step = *value; break; case hash("volume_smoothcc&"): if (opcode.parameters.back() > config::numCCs) @@ -335,8 +335,8 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) case hash("amplitude_stepcc&"): if (opcode.parameters.back() > config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::stepCCRange)) - amplitudeCC[opcode.parameters.back()].steps = *value; + if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) + amplitudeCC[opcode.parameters.back()].step = normalizePercents(*value); break; case hash("amplitude_smoothcc&"): if (opcode.parameters.back() > config::numCCs) @@ -364,8 +364,8 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) case hash("pan_stepcc&"): if (opcode.parameters.back() > config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::stepCCRange)) - panCC[opcode.parameters.back()].steps = *value; + if (auto value = readOpcode(opcode.value, Default::panStepRange)) + panCC[opcode.parameters.back()].step = normalizePercents(*value); break; case hash("pan_smoothcc&"): if (opcode.parameters.back() > config::numCCs) @@ -393,8 +393,8 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) case hash("position_stepcc&"): if (opcode.parameters.back() > config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::stepCCRange)) - positionCC[opcode.parameters.back()].steps = *value; + if (auto value = readOpcode(opcode.value, Default::positionStepRange)) + positionCC[opcode.parameters.back()].step = normalizePercents(*value); break; case hash("position_smoothcc&"): if (opcode.parameters.back() > config::numCCs) @@ -422,8 +422,8 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) case hash("width_stepcc&"): if (opcode.parameters.back() > config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::stepCCRange)) - widthCC[opcode.parameters.back()].steps = *value; + if (auto value = readOpcode(opcode.value, Default::widthStepRange)) + widthCC[opcode.parameters.back()].step = normalizePercents(*value); break; case hash("width_smoothcc&"): if (opcode.parameters.back() > config::numCCs) @@ -827,8 +827,8 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) case hash("tune_stepcc&"): if (opcode.parameters.back() > config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::stepCCRange)) - tuneCC[opcode.parameters.back()].steps = *value; + if (auto value = readOpcode(opcode.value, Default::tuneStepRange)) + tuneCC[opcode.parameters.back()].step = *value; break; case hash("pitch_smoothcc&"): // fallthrough case hash("tune_smoothcc&"): diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index de19d2ed4..60979282c 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -32,8 +32,8 @@ struct CCData { struct Modifier { float value { 0.0f }; + float step { 0.0f }; uint8_t curve { 0 }; - uint8_t steps { 0 }; uint8_t smooth { 0 }; static_assert(config::maxCurves - 1 <= std::numeric_limits::max(), "The curve type in the Modifier struct cannot support the required number of curves"); }; diff --git a/tests/EventEnvelopesT.cpp b/tests/EventEnvelopesT.cpp index a2dc6b306..551cc807b 100644 --- a/tests/EventEnvelopesT.cpp +++ b/tests/EventEnvelopesT.cpp @@ -312,10 +312,10 @@ TEST_CASE("[linearModifiers] Compare with envelopes") ccData.data.curve = 2; ccData.data.value = 20.0f; - ccData.data.steps = 10; + ccData.data.step = 2.0f; linearEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { return ccData.data.value * (1 - x); - }, ccData.data.value / (ccData.data.steps - 1)); + }, ccData.data.step); linearModifier(resources, absl::MakeSpan(output), ccData); REQUIRE(approxEqual(output, envelope)); } @@ -364,10 +364,10 @@ TEST_CASE("[multiplicativeModifiers] Compare with envelopes") ccData.data.curve = 2; ccData.data.value = 20.0f; - ccData.data.steps = 10; + ccData.data.step = 2.0f; multiplicativeEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { return db2mag(ccData.data.value * (1 - x)); - }, db2mag(ccData.data.value / (ccData.data.steps - 1)) ); + }, db2mag(ccData.data.step) ); multiplicativeModifier(resources, absl::MakeSpan(output), ccData, [](float x) { return db2mag(x); }); diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index 31e58fcd9..f982c424f 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -484,11 +484,11 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "pan_smoothcc14", "-2" }); REQUIRE(region.panCC[14].smooth == 0); region.parseOpcode({ "pan_stepcc120", "24" }); - REQUIRE(region.panCC[120].steps == 24); + REQUIRE(region.panCC[120].step == 0.24_a); region.parseOpcode({ "pan_stepcc120", "15482" }); - REQUIRE(region.panCC[120].steps == 127); + REQUIRE(region.panCC[120].step == 2.0_a); region.parseOpcode({ "pan_stepcc120", "-2" }); - REQUIRE(region.panCC[120].steps == 0); + REQUIRE(region.panCC[120].step == 0.0f); } SECTION("width") @@ -523,11 +523,11 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "width_smoothcc14", "-2" }); REQUIRE(region.widthCC[14].smooth == 0); region.parseOpcode({ "width_stepcc120", "24" }); - REQUIRE(region.widthCC[120].steps == 24); + REQUIRE(region.widthCC[120].step == 0.24_a); region.parseOpcode({ "width_stepcc120", "15482" }); - REQUIRE(region.widthCC[120].steps == 127); - region.parseOpcode({ "width_stepcc120", "-2" }); - REQUIRE(region.widthCC[120].steps == 0); + REQUIRE(region.widthCC[120].step == 2.0_a); + region.parseOpcode({ "width_stepcc120", "-20" }); + REQUIRE(region.widthCC[120].step == 0.0f); } SECTION("position") @@ -562,11 +562,11 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "position_smoothcc14", "-2" }); REQUIRE(region.positionCC[14].smooth == 0); region.parseOpcode({ "position_stepcc120", "24" }); - REQUIRE(region.positionCC[120].steps == 24); + REQUIRE(region.positionCC[120].step == 0.24_a); region.parseOpcode({ "position_stepcc120", "15482" }); - REQUIRE(region.positionCC[120].steps == 127); + REQUIRE(region.positionCC[120].step == 2.0_a); region.parseOpcode({ "position_stepcc120", "-2" }); - REQUIRE(region.positionCC[120].steps == 0); + REQUIRE(region.positionCC[120].step == 0.0f); } SECTION("amp_keycenter") @@ -1519,11 +1519,11 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "amplitude_smoothcc14", "-2" }); REQUIRE(region.amplitudeCC[14].smooth == 0); region.parseOpcode({ "amplitude_stepcc120", "24" }); - REQUIRE(region.amplitudeCC[120].steps == 24); + REQUIRE(region.amplitudeCC[120].step == 0.24_a); region.parseOpcode({ "amplitude_stepcc120", "15482" }); - REQUIRE(region.amplitudeCC[120].steps == 127); + REQUIRE(region.amplitudeCC[120].step == 1.0_a); region.parseOpcode({ "amplitude_stepcc120", "-2" }); - REQUIRE(region.amplitudeCC[120].steps == 0); + REQUIRE(region.amplitudeCC[120].step == 0.0f); } SECTION("volume_oncc/gain_cc") @@ -1551,11 +1551,11 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "volume_smoothcc14", "-2" }); REQUIRE(region.volumeCC[14].smooth == 0); region.parseOpcode({ "volume_stepcc120", "24" }); - REQUIRE(region.volumeCC[120].steps == 24); + REQUIRE(region.volumeCC[120].step == 24.0f); region.parseOpcode({ "volume_stepcc120", "15482" }); - REQUIRE(region.volumeCC[120].steps == 127); + REQUIRE(region.volumeCC[120].step == 48.0f); region.parseOpcode({ "volume_stepcc120", "-2" }); - REQUIRE(region.volumeCC[120].steps == 0); + REQUIRE(region.volumeCC[120].step == 0.0f); } SECTION("tune_cc/pitch_cc") @@ -1583,11 +1583,11 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "pitch_smoothcc14", "-2" }); REQUIRE(region.tuneCC[14].smooth == 0); region.parseOpcode({ "tune_stepcc120", "24" }); - REQUIRE(region.tuneCC[120].steps == 24); + REQUIRE(region.tuneCC[120].step == 24.0f); region.parseOpcode({ "pitch_stepcc120", "15482" }); - REQUIRE(region.tuneCC[120].steps == 127); + REQUIRE(region.tuneCC[120].step == 9600.0f); region.parseOpcode({ "tune_stepcc120", "-2" }); - REQUIRE(region.tuneCC[120].steps == 0); + REQUIRE(region.tuneCC[120].step == 0.0f); } } From aeba9dfaabece616bb04c910893b3385b94f3f4e Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Tue, 14 Apr 2020 13:52:18 +0200 Subject: [PATCH 060/444] Dispatch between "normal"multiplicativ eenvelopes and the pitch bends --- src/sfizz/Macros.h | 7 ++++++ src/sfizz/ModifierHelpers.h | 43 ++++++++++++++++++++++++++++--------- src/sfizz/Voice.cpp | 8 +++---- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/src/sfizz/Macros.h b/src/sfizz/Macros.h index 3769b34dd..024dfe24e 100644 --- a/src/sfizz/Macros.h +++ b/src/sfizz/Macros.h @@ -16,3 +16,10 @@ #define CXX11_MOVE(x) std::move(x) #endif +#if __cplusplus >= 201703L +#define IF_CONSTEXPR if constexpr +#else +#define IF_CONSTEXPR if +#endif + + diff --git a/src/sfizz/ModifierHelpers.h b/src/sfizz/ModifierHelpers.h index b46bbc617..a476f6ae9 100644 --- a/src/sfizz/ModifierHelpers.h +++ b/src/sfizz/ModifierHelpers.h @@ -139,7 +139,7 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span envelop fill(envelope.subspan(lastDelay), lastValue); } -template +template void multiplicativeEnvelope(const EventVector& events, absl::Span envelope, F&& lambda, float step) { ASSERT(events.size() > 0); @@ -156,7 +156,14 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span envelop // log q log q // and log(b)\log(q) is between 0 and 1. auto quantize = [logStep](float value) -> float { - return std::exp(logStep * std::trunc(std::log(value) / logStep)); + IF_CONSTEXPR(Round) + { + return std::exp(logStep * std::round(std::log(value) / logStep)); + } + else + { + return std::exp(logStep * std::trunc(std::log(value) / logStep)); + } }; auto lastValue = quantize(lambda(events[0].value)); @@ -184,7 +191,19 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span envelop fill(envelope.subspan(lastDelay), lastValue); } -template +template +void pitchBendEnvelope(const EventVector& events, absl::Span envelope, F&& lambda, float step) +{ + multiplicativeEnvelope(events, envelope, std::forward(lambda), step); +} + +template +void pitchBendEnvelope(const EventVector& events, absl::Span envelope, F&& lambda) +{ + multiplicativeEnvelope(events, envelope, std::forward(lambda)); +} + +template void linearModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData, F&& lambda) { const auto events = resources.midiState.getCCEvents(ccData.cc); @@ -195,13 +214,15 @@ void linearModifier(const sfz::Resources& resources, absl::Span span, con }); } else { const float stepSize { lambda(ccData.data.step) }; - linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) { - return lambda(curve.evalNormalized(x) * ccData.data.value); - }, stepSize); + linearEnvelope( + events, span, [&ccData, &curve, &lambda](float x) { + return lambda(curve.evalNormalized(x) * ccData.data.value); + }, + stepSize); } } -template +template void multiplicativeModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData, F&& lambda) { const auto events = resources.midiState.getCCEvents(ccData.cc); @@ -212,9 +233,11 @@ void multiplicativeModifier(const sfz::Resources& resources, absl::Span s }); } else { const float stepSize { lambda(ccData.data.step) }; - multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { - return lambda(curve.evalNormalized(x) * ccData.data.value); - }, stepSize); + multiplicativeEnvelope( + events, span, [&ccData, &curve, &lambda](float x) { + return lambda(curve.evalNormalized(x) * ccData.data.value); + }, + stepSize); } } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index d7deb618d..cfa8bfbef 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -446,9 +446,9 @@ void sfz::Voice::fillWithData(AudioSpan buffer) noexcept }; if (region->bendStep > 1) - multiplicativeEnvelope(events, *bends, bendLambda, bendStepFactor); + pitchBendEnvelope(events, *bends, bendLambda, bendStepFactor); else - multiplicativeEnvelope(events, *bends, bendLambda); + pitchBendEnvelope(events, *bends, bendLambda); applyGain(*bends, *jumps); for (const auto& mod : region->tuneCC) { @@ -549,9 +549,9 @@ void sfz::Voice::fillWithGenerator(AudioSpan buffer) noexcept return centsFactor(bendInCents); }; if (region->bendStep > 1) - multiplicativeEnvelope(events, *bends, bendLambda, bendStepFactor); + pitchBendEnvelope(events, *bends, bendLambda, bendStepFactor); else - multiplicativeEnvelope(events, *bends, bendLambda); + pitchBendEnvelope(events, *bends, bendLambda); applyGain(*bends, *frequencies); for (const auto& mod : region->tuneCC) { From d120c2bc9a5ecac373ef61ca72339936f894d75e Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 14 Apr 2020 17:59:12 +0200 Subject: [PATCH 061/444] Use rint, rounding modes, and corrected tests --- src/sfizz/MathHelpers.h | 92 ++++++++++++++++++++++++------------- src/sfizz/ModifierHelpers.h | 14 ++---- tests/EventEnvelopesT.cpp | 44 +++++++++--------- 3 files changed, 87 insertions(+), 63 deletions(-) diff --git a/src/sfizz/MathHelpers.h b/src/sfizz/MathHelpers.h index 9fa734f12..a70446ca5 100644 --- a/src/sfizz/MathHelpers.h +++ b/src/sfizz/MathHelpers.h @@ -15,26 +15,27 @@ #include #include #include +#include -template +template constexpr T max(T op1, T op2) { return op1 > op2 ? op1 : op2; } -template +template constexpr T max(T op1, Args... rest) { return max(op1, max(rest...)); } -template +template constexpr T min(T op1, T op2) { return op1 > op2 ? op2 : op1; } -template +template constexpr T min(T op1, Args... rest) { return min(op1, min(rest...)); @@ -46,7 +47,7 @@ constexpr T min(T op1, Args... rest) * @param op * @return T */ -template +template constexpr T power2(T in) { return in * in; @@ -111,8 +112,8 @@ constexpr Type mag2db(Type in) * */ namespace Random { - static std::random_device randomDevice; - static std::minstd_rand randomGenerator { randomDevice() }; +static std::random_device randomDevice; +static std::minstd_rand randomGenerator { randomDevice() }; } // namespace Random /** @@ -135,42 +136,42 @@ inline float midiNoteFrequency(const int noteNumber) * @param hi * @return T */ -template -constexpr T clamp( T v, T lo, T hi ) +template +constexpr T clamp(T v, T lo, T hi) { return max(min(v, hi), lo); } -template +template inline CXX14_CONSTEXPR void incrementAll(T& only) { only += Increment; } -template +template inline CXX14_CONSTEXPR void incrementAll(T& first, Args&... rest) { first += Increment; incrementAll(rest...); } -template +template constexpr ValueType linearInterpolation(ValueType left, ValueType right, ValueType leftCoeff, ValueType rightCoeff) { return left * leftCoeff + right * rightCoeff; } -template +template constexpr Type pi() { return static_cast(3.141592653589793238462643383279502884); }; -template +template constexpr Type twoPi() { return pi() * 2; }; -template +template constexpr Type piTwo() { return pi() / 2; }; -template +template constexpr Type piFour() { return pi() / 4; }; -template +template constexpr Type sqrtTwo() { return static_cast(1.414213562373095048801688724209698078569671875376948073176); }; -template +template constexpr Type sqrtTwoInv() { return static_cast(0.707106781186547524400844362104849039284835937688474036588); }; /** @@ -205,23 +206,23 @@ inline Fraction::operator float() const noexcept template struct FP_traits; -template <> struct FP_traits -{ +template <> +struct FP_traits { typedef double type; typedef uint64_t same_size_int; static_assert(sizeof(type) == sizeof(same_size_int), - "Unexpected size of floating point type"); + "Unexpected size of floating point type"); static constexpr int e_bits = 11; static constexpr int m_bits = 52; static constexpr int e_offset = -1023; }; -template <> struct FP_traits -{ +template <> +struct FP_traits { typedef float type; typedef uint32_t same_size_int; static_assert(sizeof(type) == sizeof(same_size_int), - "Unexpected size of floating point type"); + "Unexpected size of floating point type"); static constexpr int e_bits = 8; static constexpr int m_bits = 23; static constexpr int e_offset = -127; @@ -237,7 +238,10 @@ template inline bool fp_sign(F x) { typedef FP_traits T; - union { F real; typename T::same_size_int integer; } u; + union { + F real; + typename T::same_size_int integer; + } u; u.real = x; return ((u.integer >> (T::e_bits + T::m_bits)) & 1) != 0; } @@ -254,7 +258,10 @@ template inline int fp_exponent(F x) { typedef FP_traits T; - union { F real; typename T::same_size_int integer; } u; + union { + F real; + typename T::same_size_int integer; + } u; u.real = x; int ex = (u.integer >> T::m_bits) & ((1u << T::e_bits) - 1); return ex + T::e_offset; @@ -269,10 +276,13 @@ template inline Fraction fp_mantissa(F x) { typedef FP_traits T; - union { F real; typename T::same_size_int integer; } u; + union { + F real; + typename T::same_size_int integer; + } u; u.real = x; Fraction f; - f.den = uint64_t{1} << T::m_bits; + f.den = uint64_t { 1 } << T::m_bits; f.num = u.integer & (f.den - 1); return f; } @@ -287,10 +297,11 @@ inline F fp_from_parts(bool sgn, int ex, uint64_t mant) { typedef FP_traits T; typedef typename T::same_size_int I; - union { F real; I integer; } u; - u.integer = mant | - (static_cast(ex - T::e_offset) << T::m_bits) | - (static_cast(sgn) << (T::e_bits + T::m_bits)); + union { + F real; + I integer; + } u; + u.integer = mant | (static_cast(ex - T::e_offset) << T::m_bits) | (static_cast(sgn) << (T::e_bits + T::m_bits)); return u.real; } @@ -328,3 +339,20 @@ bool isValidAudio(absl::Span span) return true; } + +class ScopedRoundingMode { +public: + ScopedRoundingMode() = delete; + ScopedRoundingMode(int newRoundingMode) + : savedFloatMode(std::fegetround()) + { + std::fesetround(newRoundingMode); + } + ~ScopedRoundingMode() + { + std::fesetround(savedFloatMode); + } + +private: + const int savedFloatMode; +}; diff --git a/src/sfizz/ModifierHelpers.h b/src/sfizz/ModifierHelpers.h index a476f6ae9..8e01610a3 100644 --- a/src/sfizz/ModifierHelpers.h +++ b/src/sfizz/ModifierHelpers.h @@ -5,6 +5,7 @@ #include "SfzHelpers.h" #include "Resources.h" #include "absl/types/span.h" + namespace sfz { /** @@ -146,6 +147,8 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span envelop ASSERT(events[0].delay == 0); ASSERT(step != 0.0f); + ScopedRoundingMode roundingMode { Round ? FE_TONEAREST : FE_TOWARDZERO }; + if (envelope.size() == 0) return; const auto maxDelay = static_cast(envelope.size() - 1); @@ -156,14 +159,7 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span envelop // log q log q // and log(b)\log(q) is between 0 and 1. auto quantize = [logStep](float value) -> float { - IF_CONSTEXPR(Round) - { - return std::exp(logStep * std::round(std::log(value) / logStep)); - } - else - { - return std::exp(logStep * std::trunc(std::log(value) / logStep)); - } + return std::exp(logStep * std::rint(std::log(value) / logStep)); }; auto lastValue = quantize(lambda(events[0].value)); @@ -182,7 +178,7 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span envelop const auto numSteps = std::round(std::log(difference) / logStep); const auto stepLength = static_cast(length / numSteps); - for (int i = 0; i < numSteps; ++i) { + for (int i = 0; i < static_cast(numSteps); ++i) { fill(envelope.subspan(lastDelay, stepLength), lastValue); lastValue = nextValue > lastValue ? lastValue * step : lastValue / step; lastDelay += stepLength; diff --git a/tests/EventEnvelopesT.cpp b/tests/EventEnvelopesT.cpp index 551cc807b..8c7e2d555 100644 --- a/tests/EventEnvelopesT.cpp +++ b/tests/EventEnvelopesT.cpp @@ -42,13 +42,13 @@ TEST_CASE("[Envelopes] Empty") std::array expected { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; std::array expectedMul { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); multiplicativeEnvelope(events, absl::MakeSpan(output), expModifier); - REQUIRE(output == expectedMul); + REQUIRE(approxEqual(output, expectedMul)); multiplicativeEnvelope(events, absl::MakeSpan(output), expModifier, 2.0f); - REQUIRE(output == expectedMul); + REQUIRE(approxEqual(output, expectedMul)); } TEST_CASE("[Envelopes] Linear basic") @@ -60,7 +60,7 @@ TEST_CASE("[Envelopes] Linear basic") std::array output; std::array expected { 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] 2 events, close") @@ -73,7 +73,7 @@ TEST_CASE("[LinearEnvelope] 2 events, close") std::array output; std::array expected { 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] 2 events, far") @@ -86,7 +86,7 @@ TEST_CASE("[LinearEnvelope] 2 events, far") std::array output; std::array expected { 0.0f, 0.5f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.0f, 2.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] 3 events, out of block") @@ -100,7 +100,7 @@ TEST_CASE("[LinearEnvelope] 3 events, out of block") std::array output; std::array expected { 0.0f, 0.5f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.5f, 3.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] 2 events, function") @@ -113,7 +113,7 @@ TEST_CASE("[LinearEnvelope] 2 events, function") std::array output; std::array expected { 0.0f, 1.0f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.0f, 4.0f }; linearEnvelope(events, absl::MakeSpan(output), twiceModifier); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] Get quantized") @@ -126,7 +126,7 @@ TEST_CASE("[LinearEnvelope] Get quantized") std::array output; std::array expected { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] Get quantized with unquantized targets") @@ -139,7 +139,7 @@ TEST_CASE("[LinearEnvelope] Get quantized with unquantized targets") std::array output; std::array expected { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] Get quantized with 2 steps") @@ -152,7 +152,7 @@ TEST_CASE("[LinearEnvelope] Get quantized with 2 steps") std::array output; std::array expected { 0.0f, 0.0f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 3.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] Get quantized with 2 steps and an unquantized out of block step") @@ -166,7 +166,7 @@ TEST_CASE("[LinearEnvelope] Get quantized with 2 steps and an unquantized out of std::array output; std::array expected { 0.0f, 0.0f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 4.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } @@ -180,7 +180,7 @@ TEST_CASE("[LinearEnvelope] Going down quantized with 2 steps") std::array output; std::array expected { 3.0f, 3.0f, 2.0f, 2.0f, 1.0f, 1.0f, 0.0f, 0.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[MultiplicativeEnvelope] Basic event") @@ -231,7 +231,7 @@ TEST_CASE("[MultiplicativeEnvelope] Get quantized with 2 steps") std::array output; std::array expected { 1.0f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f, 4.0f, 4.0f }; multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[MultiplicativeEnvelope] Get quantized with an unquantized out of range step") @@ -245,7 +245,7 @@ TEST_CASE("[MultiplicativeEnvelope] Get quantized with an unquantized out of ran std::array output; std::array expected { 1.0f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f, 4.0f, 8.0f }; multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[MultiplicativeEnvelope] Going down quantized with 2 steps") @@ -258,7 +258,7 @@ TEST_CASE("[MultiplicativeEnvelope] Going down quantized with 2 steps") std::array output; std::array expected { 4.0f, 4.0f, 2.0f, 2.0f, 1.0f, 1.0f, 0.5f, 0.5f }; multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[MultiplicativeEnvelope] Get quantized with unclean events") @@ -271,7 +271,7 @@ TEST_CASE("[MultiplicativeEnvelope] Get quantized with unclean events") std::array output; std::array expected { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f }; multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[linearModifiers] Compare with envelopes") @@ -284,7 +284,7 @@ TEST_CASE("[linearModifiers] Compare with envelopes") ccData.data.value = 100.0f; resources.midiState.ccEvent(5, 20, 0.1); - resources.midiState.ccEvent(10, 20, 0.2); + resources.midiState.ccEvent(10, 20, 0.8); std::array output; std::array envelope; @@ -312,7 +312,7 @@ TEST_CASE("[linearModifiers] Compare with envelopes") ccData.data.curve = 2; ccData.data.value = 20.0f; - ccData.data.step = 2.0f; + ccData.data.step = 2.5f; linearEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { return ccData.data.value * (1 - x); }, ccData.data.step); @@ -330,7 +330,7 @@ TEST_CASE("[multiplicativeModifiers] Compare with envelopes") ccData.data.value = 100.0f; resources.midiState.ccEvent(5, 20, 0.1); - resources.midiState.ccEvent(10, 20, 0.8); + resources.midiState.ccEvent(15, 20, 0.8); std::array output; std::array envelope; @@ -364,7 +364,7 @@ TEST_CASE("[multiplicativeModifiers] Compare with envelopes") ccData.data.curve = 2; ccData.data.value = 20.0f; - ccData.data.step = 2.0f; + ccData.data.step = 2.5f; multiplicativeEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { return db2mag(ccData.data.value * (1 - x)); }, db2mag(ccData.data.step) ); From 442e15b8e920cd3636861914cbb7546e75ee4487 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 14 Apr 2020 23:08:28 +0200 Subject: [PATCH 062/444] Adapted the tests --- tests/EventEnvelopesT.cpp | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/tests/EventEnvelopesT.cpp b/tests/EventEnvelopesT.cpp index 8c7e2d555..64bcb7e41 100644 --- a/tests/EventEnvelopesT.cpp +++ b/tests/EventEnvelopesT.cpp @@ -224,9 +224,9 @@ TEST_CASE("[MultiplicativeEnvelope] 2 events, far") TEST_CASE("[MultiplicativeEnvelope] Get quantized with 2 steps") { sfz::EventVector events { - { 0, 1.0f }, - { 2, 2.0f }, - { 6, 4.0f } + { 0, 1.3f }, + { 2, 2.1f }, + { 6, 4.2f } }; std::array output; std::array expected { 1.0f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f, 4.0f, 4.0f }; @@ -237,9 +237,9 @@ TEST_CASE("[MultiplicativeEnvelope] Get quantized with 2 steps") TEST_CASE("[MultiplicativeEnvelope] Get quantized with an unquantized out of range step") { sfz::EventVector events { - { 0, 1.0f }, - { 2, 2.0f }, - { 6, 4.0f }, + { 0, 1.3f }, + { 2, 2.1f }, + { 6, 4.1f }, { 10, 8.2f } }; std::array output; @@ -251,9 +251,9 @@ TEST_CASE("[MultiplicativeEnvelope] Get quantized with an unquantized out of ran TEST_CASE("[MultiplicativeEnvelope] Going down quantized with 2 steps") { sfz::EventVector events { - { 0, 4.0f }, - { 2, 2.0f }, - { 6, 0.5f } + { 0, 4.1f }, + { 2, 2.2f }, + { 6, 0.4f } }; std::array output; std::array expected { 4.0f, 4.0f, 2.0f, 2.0f, 1.0f, 1.0f, 0.5f, 0.5f }; @@ -261,19 +261,6 @@ TEST_CASE("[MultiplicativeEnvelope] Going down quantized with 2 steps") REQUIRE(approxEqual(output, expected)); } -TEST_CASE("[MultiplicativeEnvelope] Get quantized with unclean events") -{ - sfz::EventVector events { - { 0, 1.0f }, - { 2, 1.2f }, - { 6, 2.5f } - }; - std::array output; - std::array expected { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f }; - multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f); - REQUIRE(approxEqual(output, expected)); -} - TEST_CASE("[linearModifiers] Compare with envelopes") { sfz::Resources resources; From e4d63ec2f56dc4e16c4655a687a9c777f3e937a1 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 15 Apr 2020 17:24:11 +0200 Subject: [PATCH 063/444] Avoid raising an exception on file removed --- src/sfizz/Synth.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 0870905fe..6a9c2d3a6 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -1032,8 +1032,9 @@ fs::file_time_type sfz::Synth::checkModificationTime() { auto returnedTime = modificationTime; for (const auto& file: parser.getIncludedFiles()) { - const auto fileTime = fs::last_write_time(file); - if (returnedTime < fileTime) + std::error_code ec; + const auto fileTime = fs::last_write_time(file, ec); + if (!ec && returnedTime < fileTime) returnedTime = fileTime; } return returnedTime; From 5a0aafa793716bdc23b0831a8d61c541f21b690a Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 8 Apr 2020 12:23:17 +0200 Subject: [PATCH 064/444] Parse label_noteNN and add getters for the labels in the internal API --- src/sfizz/SfzHelpers.h | 4 +++- src/sfizz/Synth.cpp | 12 +++++++++--- src/sfizz/Synth.h | 20 ++++++++++++++++++-- tests/FilesT.cpp | 18 ++++++++++++++++++ tests/TestFiles/labels.sfz | 7 +++++++ 5 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 tests/TestFiles/labels.sfz diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index 60979282c..57bf109f8 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -20,7 +20,9 @@ namespace sfz { -using CCNamePair = std::pair; +using CCNamePair = std::pair; +using NoteNamePair = std::pair; + template using MidiNoteArray = std::array; template diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index fd12b218b..fd3b55873 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -142,7 +142,9 @@ void sfz::Synth::clear() numMasters = 0; defaultSwitch = absl::nullopt; defaultPath = ""; - ccNames.clear(); + resources.midiState.reset(); + ccLabels.clear(); + noteLabels.clear(); globalOpcodes.clear(); masterOpcodes.clear(); groupOpcodes.clear(); @@ -208,7 +210,11 @@ void sfz::Synth::handleControlOpcodes(const std::vector& members) case hash("Label_cc&"): // fallthrough case hash("label_cc&"): if (Default::ccNumberRange.containsWithEnd(member.parameters.back())) - ccNames.emplace_back(member.parameters.back(), std::string(member.value)); + ccLabels.emplace_back(member.parameters.back(), std::string(member.value)); + break; + case hash("label_note&"): + if (Default::keyRange.containsWithEnd(member.parameters.back())) + noteLabels.emplace_back(member.parameters.back(), std::string(member.value)); break; case hash("Default_path"): // fallthrough @@ -871,7 +877,7 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const { pugi::xml_node cns = device.append_child("ControlNameList"); cns.append_attribute("Name").set_value("Controls"); - for (const CCNamePair& pair : ccNames) { + for (const CCNamePair& pair : ccLabels) { pugi::xml_node cn = cns.append_child("Control"); cn.append_attribute("Type").set_value("7bit"); cn.append_attribute("Number").set_value(std::to_string(pair.first).c_str()); diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index 4d34d8e80..0f34fdb73 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -388,6 +388,19 @@ class Synth final : public Parser::Listener { */ const Parser& getParser() const noexcept { return parser; } + /** + * @brief Get the note labels, if any + * + * @return const std::vector& + */ + const std::vector& getNoteLabels() const noexcept { return noteLabels; } + /** + * @brief Get the CC labels, if any + * + * @return const std::vector& + */ + const std::vector& getCCLabels() const noexcept { return ccLabels; } + protected: /** * @brief The parser callback; this is called by the parent object each time @@ -494,8 +507,11 @@ class Synth final : public Parser::Listener { * @return Voice* */ Voice* findFreeVoice() noexcept; - // Names for the cc as set by the label_cc opcode - std::vector ccNames; + + // Names for the CC and notes as set by label_cc and label_note + std::vector ccLabels; + std::vector noteLabels; + // Default active switch if multiple keyswitchable regions are present absl::optional defaultSwitch; std::vector unknownOpcodes; diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 4dd43c8fc..d7bedade1 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -563,3 +563,21 @@ TEST_CASE("[Files] Empty file") REQUIRE(!synth.loadSfzFile({})); REQUIRE(parser.getIncludedFiles().empty()); } + +TEST_CASE("[Files] Labels") +{ + sfz::Synth synth; + synth.loadSfzFile(fs::current_path() / "tests/TestFiles/labels.sfz"); + auto noteLabels = synth.getNoteLabels(); + auto ccLabels = synth.getCCLabels(); + REQUIRE( noteLabels.size() == 2); + REQUIRE( noteLabels[0].first == 12 ); + REQUIRE( noteLabels[0].second == "Cymbals" ); + REQUIRE( noteLabels[1].first == 65 ); + REQUIRE( noteLabels[1].second == "Crash" ); + REQUIRE( ccLabels.size() == 2); + REQUIRE( ccLabels[0].first == 54 ); + REQUIRE( ccLabels[0].second == "Gain" ); + REQUIRE( ccLabels[1].first == 2 ); + REQUIRE( ccLabels[1].second == "Other" ); +} diff --git a/tests/TestFiles/labels.sfz b/tests/TestFiles/labels.sfz new file mode 100644 index 000000000..49f51ed57 --- /dev/null +++ b/tests/TestFiles/labels.sfz @@ -0,0 +1,7 @@ + +label_cc54=Gain +label_cc2=Other +label_note12=Cymbals +label_note65=Crash +label_note128=Ignored + sample=*sine From c075e40c23003f47637c97b736314859341140fc Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 8 Apr 2020 12:57:03 +0200 Subject: [PATCH 065/444] Added public API bindings --- src/sfizz.h | 51 +++++++++++++++++++++++++ src/sfizz.hpp | 16 +++++++- src/sfizz/SfzHelpers.h | 2 +- src/sfizz/sfizz.cpp | 10 +++++ src/sfizz/sfizz_wrapper.cpp | 75 +++++++++++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+), 2 deletions(-) diff --git a/src/sfizz.h b/src/sfizz.h index 69faa3ed9..63482393f 100644 --- a/src/sfizz.h +++ b/src/sfizz.h @@ -394,6 +394,57 @@ SFIZZ_EXPORTED_API void sfizz_add_external_definitions(sfizz_synth_t* synth, con */ SFIZZ_EXPORTED_API void sfizz_clear_external_definitions(sfizz_synth_t* synth); +#define SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX -1 + +/** + * @brief Get the number of note labels registered in the current sfz file + * @version 0.4.0-dev + */ +SFIZZ_EXPORTED_API unsigned int sfizz_get_num_note_labels(sfizz_synth_t* synth); + +/** + * @brief Get the note number for the label registered at index label_index. + * @version 0.4.0-dev + * + * @returns SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX if the index is out of bounds. + * @returns the number + */ +SFIZZ_EXPORTED_API int sfizz_get_note_label_number(sfizz_synth_t* synth, int label_index); + +/** + * @brief Get the note text for the label registered at index label_index. + * @version 0.4.0-dev + * + * @returns NULL if the index is out of bounds. + * @returns the label + */ +SFIZZ_EXPORTED_API const char * sfizz_get_note_label_text(sfizz_synth_t* synth, int label_index); + +/** + * @brief Get the number of note labels registered in the current sfz file + * @version 0.4.0-dev + * + */ +SFIZZ_EXPORTED_API unsigned int sfizz_get_num_cc_labels(sfizz_synth_t* synth); + +/** + * @brief Get the CC number for the label registered at index label_index. + * @version 0.4.0-dev + * + * @returns SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX if the index is out of bounds. + * @returns the number + */ + +SFIZZ_EXPORTED_API int sfizz_get_cc_label_number(sfizz_synth_t* synth, int label_index); +/** + * @brief Get the CC text for the label registered at index label_index. + * @version 0.4.0-dev + * + * @returns NULL if the index is out of bounds. + * @returns the label + */ +SFIZZ_EXPORTED_API const char * sfizz_get_cc_label_text(sfizz_synth_t* synth, int label_index); + #ifdef __cplusplus } #endif diff --git a/src/sfizz.hpp b/src/sfizz.hpp index 49e9cd7a2..d726d6c57 100644 --- a/src/sfizz.hpp +++ b/src/sfizz.hpp @@ -10,6 +10,7 @@ */ #include +#include #include #include @@ -287,7 +288,8 @@ class SFIZZ_EXPORTED_API Sfizz void setLoggingPrefix(const std::string& prefix) noexcept; /** - * @brief Disable logging. + * @brief + * */ void disableLogging() noexcept; @@ -312,6 +314,18 @@ class SFIZZ_EXPORTED_API Sfizz */ void clearExternalDefinitions(); + /** + * @brief Get the note labels, if any + * @version 0.4.0-dev + * + */ + const std::vector>& getNoteLabels() const noexcept; + /** + * @brief Get the CC labels, if any + * @version 0.4.0-dev + * + */ + const std::vector>& getCCLabels() const noexcept; private: std::unique_ptr synth; }; diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index 57bf109f8..5d581a743 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -20,7 +20,7 @@ namespace sfz { -using CCNamePair = std::pair; +using CCNamePair = std::pair; using NoteNamePair = std::pair; template diff --git a/src/sfizz/sfizz.cpp b/src/sfizz/sfizz.cpp index 3e17af01f..97e52cd47 100644 --- a/src/sfizz/sfizz.cpp +++ b/src/sfizz/sfizz.cpp @@ -220,3 +220,13 @@ void sfz::Sfizz::clearExternalDefinitions() { synth->getParser().clearExternalDefinitions(); } + +const std::vector>& sfz::Sfizz::getNoteLabels() const noexcept +{ + return synth->getNoteLabels(); +} + +const std::vector>& sfz::Sfizz::getCCLabels() const noexcept +{ + return synth->getCCLabels(); +} diff --git a/src/sfizz/sfizz_wrapper.cpp b/src/sfizz/sfizz_wrapper.cpp index 3c9d88f09..c614bf448 100644 --- a/src/sfizz/sfizz_wrapper.cpp +++ b/src/sfizz/sfizz_wrapper.cpp @@ -8,6 +8,7 @@ #include "Macros.h" #include "Synth.h" #include "sfizz.h" +#include #ifdef __cplusplus extern "C" { @@ -268,6 +269,80 @@ void sfizz_clear_external_definitions(sfizz_synth_t* synth) self->getParser().clearExternalDefinitions(); } +unsigned int sfizz_get_num_note_labels(sfizz_synth_t* synth) +{ + auto self = reinterpret_cast(synth); + return self->getNoteLabels().size(); +} + +int sfizz_get_note_label_number(sfizz_synth_t* synth, int label_index) +{ + auto self = reinterpret_cast(synth); + const auto noteLabels = self->getNoteLabels(); + if (label_index < 0) + return SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX; + + if (static_cast(label_index) >= noteLabels.size()) + return SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX; + + // Sanity checks for the future or platforms + static_assert( + std::numeric_limits::max() < std::numeric_limits::max(), + "The C API sends back an int but the note index in NoteNamePair can overflow it on this platform" + ); + return static_cast(noteLabels[label_index].first); +} + +const char * sfizz_get_note_label_text(sfizz_synth_t* synth, int label_index) +{ + auto self = reinterpret_cast(synth); + const auto noteLabels = self->getNoteLabels(); + if (label_index < 0) + return NULL; + + if (static_cast(label_index) >= noteLabels.size()) + return NULL; + + return noteLabels[label_index].second.c_str(); +} + +unsigned int sfizz_get_num_cc_labels(sfizz_synth_t* synth) +{ + auto self = reinterpret_cast(synth); + return self->getCCLabels().size(); +} + +int sfizz_get_cc_label_number(sfizz_synth_t* synth, int label_index) +{ + auto self = reinterpret_cast(synth); + const auto ccLabels = self->getCCLabels(); + if (label_index < 0) + return SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX; + + if (static_cast(label_index) >= ccLabels.size()) + return SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX; + + // Sanity checks for the future or platforms + static_assert( + std::numeric_limits::max() < std::numeric_limits::max(), + "The C API sends back an int but the cc index in CCNamePair can overflow it on this platform" + ); + return static_cast(ccLabels[label_index].first); +} + +const char * sfizz_get_cc_label_text(sfizz_synth_t* synth, int label_index) +{ + auto self = reinterpret_cast(synth); + const auto ccLabels = self->getCCLabels(); + if (label_index < 0) + return NULL; + + if (static_cast(label_index) >= ccLabels.size()) + return NULL; + + return ccLabels[label_index].second.c_str(); +} + #ifdef __cplusplus } #endif From 7cbd920f7b1295bff01553750865d5dc51c69aeb Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 8 Apr 2020 13:17:14 +0200 Subject: [PATCH 066/444] Added note names to the MIDNAM file --- src/sfizz/Synth.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index fd3b55873..b3a43e84d 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -885,6 +885,16 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const } } + { + pugi::xml_node nnl = device.append_child("NoteNameList"); + nnl.append_attribute("Name").set_value("Notes"); + for (const CCNamePair& pair : noteLabels) { + pugi::xml_node nn = nnl.append_child("Note"); + nn.append_attribute("Number").set_value(std::to_string(pair.first).c_str()); + nn.append_attribute("Name").set_value(pair.second.c_str()); + } + } + /// struct string_writer : pugi::xml_writer { std::string result; From 25353e78ff8b6927e4fdf9fd479e713cbfb563b5 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 8 Apr 2020 13:37:26 +0200 Subject: [PATCH 067/444] Use auto in the foreach loops --- src/sfizz/Synth.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index b3a43e84d..00d5804f8 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -877,7 +877,7 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const { pugi::xml_node cns = device.append_child("ControlNameList"); cns.append_attribute("Name").set_value("Controls"); - for (const CCNamePair& pair : ccLabels) { + for (const auto& pair : ccLabels) { pugi::xml_node cn = cns.append_child("Control"); cn.append_attribute("Type").set_value("7bit"); cn.append_attribute("Number").set_value(std::to_string(pair.first).c_str()); @@ -888,7 +888,7 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const { pugi::xml_node nnl = device.append_child("NoteNameList"); nnl.append_attribute("Name").set_value("Notes"); - for (const CCNamePair& pair : noteLabels) { + for (const auto& pair : noteLabels) { pugi::xml_node nn = nnl.append_child("Note"); nn.append_attribute("Number").set_value(std::to_string(pair.first).c_str()); nn.append_attribute("Name").set_value(pair.second.c_str()); From b7f336e2435694884f2e442fb44935e7c94f36cb Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 8 Apr 2020 14:00:40 +0200 Subject: [PATCH 068/444] Correct documentation --- src/sfizz.h | 24 ++++++++++-------------- src/sfizz.hpp | 4 ++-- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/sfizz.h b/src/sfizz.h index 63482393f..ab55f5d24 100644 --- a/src/sfizz.h +++ b/src/sfizz.h @@ -398,50 +398,46 @@ SFIZZ_EXPORTED_API void sfizz_clear_external_definitions(sfizz_synth_t* synth); /** * @brief Get the number of note labels registered in the current sfz file - * @version 0.4.0-dev + * @since 0.4.0-dev */ SFIZZ_EXPORTED_API unsigned int sfizz_get_num_note_labels(sfizz_synth_t* synth); /** * @brief Get the note number for the label registered at index label_index. - * @version 0.4.0-dev + * @since 0.4.0-dev * - * @returns SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX if the index is out of bounds. - * @returns the number + * @returns the number or SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX if the index is out of bounds. */ SFIZZ_EXPORTED_API int sfizz_get_note_label_number(sfizz_synth_t* synth, int label_index); /** * @brief Get the note text for the label registered at index label_index. - * @version 0.4.0-dev + * @since 0.4.0-dev * - * @returns NULL if the index is out of bounds. - * @returns the label + * @returns the label or NULL if the index is out of bounds. */ SFIZZ_EXPORTED_API const char * sfizz_get_note_label_text(sfizz_synth_t* synth, int label_index); /** * @brief Get the number of note labels registered in the current sfz file - * @version 0.4.0-dev + * @since 0.4.0-dev * */ SFIZZ_EXPORTED_API unsigned int sfizz_get_num_cc_labels(sfizz_synth_t* synth); /** * @brief Get the CC number for the label registered at index label_index. - * @version 0.4.0-dev + * @since 0.4.0-dev * - * @returns SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX if the index is out of bounds. - * @returns the number + * @returns the number or SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX if the index is out of bounds. */ SFIZZ_EXPORTED_API int sfizz_get_cc_label_number(sfizz_synth_t* synth, int label_index); /** * @brief Get the CC text for the label registered at index label_index. - * @version 0.4.0-dev + * @since 0.4.0-dev * - * @returns NULL if the index is out of bounds. - * @returns the label + * @returns the label or NULL if the index is out of bounds. */ SFIZZ_EXPORTED_API const char * sfizz_get_cc_label_text(sfizz_synth_t* synth, int label_index); diff --git a/src/sfizz.hpp b/src/sfizz.hpp index d726d6c57..f8885259d 100644 --- a/src/sfizz.hpp +++ b/src/sfizz.hpp @@ -316,13 +316,13 @@ class SFIZZ_EXPORTED_API Sfizz /** * @brief Get the note labels, if any - * @version 0.4.0-dev + * @since 0.4.0-dev * */ const std::vector>& getNoteLabels() const noexcept; /** * @brief Get the CC labels, if any - * @version 0.4.0-dev + * @since 0.4.0-dev * */ const std::vector>& getCCLabels() const noexcept; From ff60b35b7378d086f07da940603fedb9f3d1202f Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 8 Apr 2020 14:01:18 +0200 Subject: [PATCH 069/444] Added since to the external definition things --- src/sfizz.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sfizz.h b/src/sfizz.h index ab55f5d24..a48cce55f 100644 --- a/src/sfizz.h +++ b/src/sfizz.h @@ -380,6 +380,7 @@ 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. + * @since 0.4.0-dev * * @param synth * @param id @@ -389,6 +390,7 @@ SFIZZ_EXPORTED_API void sfizz_add_external_definitions(sfizz_synth_t* synth, con /** * @brief Clears external definitions for the next file loading. + * @since 0.4.0-dev * * @param synth */ From fc34ccb20574dee90dfd7c24bda1e1bfa3843167 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 8 Apr 2020 14:43:51 +0200 Subject: [PATCH 070/444] Added @since in the C++ bindings --- src/sfizz.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sfizz.hpp b/src/sfizz.hpp index f8885259d..d3838eea1 100644 --- a/src/sfizz.hpp +++ b/src/sfizz.hpp @@ -302,6 +302,7 @@ class SFIZZ_EXPORTED_API Sfizz * @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. + * @since 0.4.0-dev * * @param id * @param value @@ -310,6 +311,7 @@ class SFIZZ_EXPORTED_API Sfizz /** * @brief Clears external definitions for the next file loading. + * @since 0.4.0-dev * */ void clearExternalDefinitions(); From a810dc57753dee3dddfda06e3d385cdc04951a5c Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 14 Apr 2020 16:58:19 +0200 Subject: [PATCH 071/444] Add the MIDNAM property: UsesNoteNameList --- src/sfizz/Synth.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 00d5804f8..2a3bdebd2 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -872,6 +872,9 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const chns.append_child("UsesControlNameList") .append_attribute("Name") .set_value("Controls"); + chns.append_child("UsesNoteNameList") + .append_attribute("Name") + .set_value("Notes"); } { From 5f94e34230d0c187f35bbbac5304c8fd73093c68 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 15 Apr 2020 18:04:02 +0200 Subject: [PATCH 072/444] Rename all: note label -> key label --- src/sfizz.h | 14 +++++++------- src/sfizz.hpp | 4 ++-- src/sfizz/Synth.cpp | 8 ++++---- src/sfizz/Synth.h | 8 ++++---- src/sfizz/sfizz.cpp | 4 ++-- src/sfizz/sfizz_wrapper.cpp | 20 ++++++++++---------- tests/FilesT.cpp | 12 ++++++------ tests/TestFiles/labels.sfz | 6 +++--- 8 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/sfizz.h b/src/sfizz.h index a48cce55f..a941d59a1 100644 --- a/src/sfizz.h +++ b/src/sfizz.h @@ -399,29 +399,29 @@ SFIZZ_EXPORTED_API void sfizz_clear_external_definitions(sfizz_synth_t* synth); #define SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX -1 /** - * @brief Get the number of note labels registered in the current sfz file + * @brief Get the number of key labels registered in the current sfz file * @since 0.4.0-dev */ -SFIZZ_EXPORTED_API unsigned int sfizz_get_num_note_labels(sfizz_synth_t* synth); +SFIZZ_EXPORTED_API unsigned int sfizz_get_num_key_labels(sfizz_synth_t* synth); /** - * @brief Get the note number for the label registered at index label_index. + * @brief Get the key number for the label registered at index label_index. * @since 0.4.0-dev * * @returns the number or SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX if the index is out of bounds. */ -SFIZZ_EXPORTED_API int sfizz_get_note_label_number(sfizz_synth_t* synth, int label_index); +SFIZZ_EXPORTED_API int sfizz_get_key_label_number(sfizz_synth_t* synth, int label_index); /** - * @brief Get the note text for the label registered at index label_index. + * @brief Get the key text for the label registered at index label_index. * @since 0.4.0-dev * * @returns the label or NULL if the index is out of bounds. */ -SFIZZ_EXPORTED_API const char * sfizz_get_note_label_text(sfizz_synth_t* synth, int label_index); +SFIZZ_EXPORTED_API const char * sfizz_get_key_label_text(sfizz_synth_t* synth, int label_index); /** - * @brief Get the number of note labels registered in the current sfz file + * @brief Get the number of CC labels registered in the current sfz file * @since 0.4.0-dev * */ diff --git a/src/sfizz.hpp b/src/sfizz.hpp index d3838eea1..9c9d92081 100644 --- a/src/sfizz.hpp +++ b/src/sfizz.hpp @@ -317,11 +317,11 @@ class SFIZZ_EXPORTED_API Sfizz void clearExternalDefinitions(); /** - * @brief Get the note labels, if any + * @brief Get the key labels, if any * @since 0.4.0-dev * */ - const std::vector>& getNoteLabels() const noexcept; + const std::vector>& getKeyLabels() const noexcept; /** * @brief Get the CC labels, if any * @since 0.4.0-dev diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 2a3bdebd2..03f3cc34a 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -144,7 +144,7 @@ void sfz::Synth::clear() defaultPath = ""; resources.midiState.reset(); ccLabels.clear(); - noteLabels.clear(); + keyLabels.clear(); globalOpcodes.clear(); masterOpcodes.clear(); groupOpcodes.clear(); @@ -212,9 +212,9 @@ void sfz::Synth::handleControlOpcodes(const std::vector& members) if (Default::ccNumberRange.containsWithEnd(member.parameters.back())) ccLabels.emplace_back(member.parameters.back(), std::string(member.value)); break; - case hash("label_note&"): + case hash("label_key&"): if (Default::keyRange.containsWithEnd(member.parameters.back())) - noteLabels.emplace_back(member.parameters.back(), std::string(member.value)); + keyLabels.emplace_back(member.parameters.back(), std::string(member.value)); break; case hash("Default_path"): // fallthrough @@ -891,7 +891,7 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const { pugi::xml_node nnl = device.append_child("NoteNameList"); nnl.append_attribute("Name").set_value("Notes"); - for (const auto& pair : noteLabels) { + for (const auto& pair : keyLabels) { pugi::xml_node nn = nnl.append_child("Note"); nn.append_attribute("Number").set_value(std::to_string(pair.first).c_str()); nn.append_attribute("Name").set_value(pair.second.c_str()); diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index 0f34fdb73..738e54d23 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -389,11 +389,11 @@ class Synth final : public Parser::Listener { const Parser& getParser() const noexcept { return parser; } /** - * @brief Get the note labels, if any + * @brief Get the key labels, if any * * @return const std::vector& */ - const std::vector& getNoteLabels() const noexcept { return noteLabels; } + const std::vector& getKeyLabels() const noexcept { return keyLabels; } /** * @brief Get the CC labels, if any * @@ -508,9 +508,9 @@ class Synth final : public Parser::Listener { */ Voice* findFreeVoice() noexcept; - // Names for the CC and notes as set by label_cc and label_note + // Names for the CC and notes as set by label_cc and label_key std::vector ccLabels; - std::vector noteLabels; + std::vector keyLabels; // Default active switch if multiple keyswitchable regions are present absl::optional defaultSwitch; diff --git a/src/sfizz/sfizz.cpp b/src/sfizz/sfizz.cpp index 97e52cd47..5ce7c1330 100644 --- a/src/sfizz/sfizz.cpp +++ b/src/sfizz/sfizz.cpp @@ -221,9 +221,9 @@ void sfz::Sfizz::clearExternalDefinitions() synth->getParser().clearExternalDefinitions(); } -const std::vector>& sfz::Sfizz::getNoteLabels() const noexcept +const std::vector>& sfz::Sfizz::getKeyLabels() const noexcept { - return synth->getNoteLabels(); + return synth->getKeyLabels(); } const std::vector>& sfz::Sfizz::getCCLabels() const noexcept diff --git a/src/sfizz/sfizz_wrapper.cpp b/src/sfizz/sfizz_wrapper.cpp index c614bf448..e27d08726 100644 --- a/src/sfizz/sfizz_wrapper.cpp +++ b/src/sfizz/sfizz_wrapper.cpp @@ -269,20 +269,20 @@ void sfizz_clear_external_definitions(sfizz_synth_t* synth) self->getParser().clearExternalDefinitions(); } -unsigned int sfizz_get_num_note_labels(sfizz_synth_t* synth) +unsigned int sfizz_get_num_key_labels(sfizz_synth_t* synth) { auto self = reinterpret_cast(synth); - return self->getNoteLabels().size(); + return self->getKeyLabels().size(); } -int sfizz_get_note_label_number(sfizz_synth_t* synth, int label_index) +int sfizz_get_key_label_number(sfizz_synth_t* synth, int label_index) { auto self = reinterpret_cast(synth); - const auto noteLabels = self->getNoteLabels(); + const auto keyLabels = self->getKeyLabels(); if (label_index < 0) return SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX; - if (static_cast(label_index) >= noteLabels.size()) + if (static_cast(label_index) >= keyLabels.size()) return SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX; // Sanity checks for the future or platforms @@ -290,20 +290,20 @@ int sfizz_get_note_label_number(sfizz_synth_t* synth, int label_index) std::numeric_limits::max() < std::numeric_limits::max(), "The C API sends back an int but the note index in NoteNamePair can overflow it on this platform" ); - return static_cast(noteLabels[label_index].first); + return static_cast(keyLabels[label_index].first); } -const char * sfizz_get_note_label_text(sfizz_synth_t* synth, int label_index) +const char * sfizz_get_key_label_text(sfizz_synth_t* synth, int label_index) { auto self = reinterpret_cast(synth); - const auto noteLabels = self->getNoteLabels(); + const auto keyLabels = self->getKeyLabels(); if (label_index < 0) return NULL; - if (static_cast(label_index) >= noteLabels.size()) + if (static_cast(label_index) >= keyLabels.size()) return NULL; - return noteLabels[label_index].second.c_str(); + return keyLabels[label_index].second.c_str(); } unsigned int sfizz_get_num_cc_labels(sfizz_synth_t* synth) diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index d7bedade1..d4ce55686 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -568,13 +568,13 @@ TEST_CASE("[Files] Labels") { sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/labels.sfz"); - auto noteLabels = synth.getNoteLabels(); + auto keyLabels = synth.getKeyLabels(); auto ccLabels = synth.getCCLabels(); - REQUIRE( noteLabels.size() == 2); - REQUIRE( noteLabels[0].first == 12 ); - REQUIRE( noteLabels[0].second == "Cymbals" ); - REQUIRE( noteLabels[1].first == 65 ); - REQUIRE( noteLabels[1].second == "Crash" ); + REQUIRE( keyLabels.size() == 2); + REQUIRE( keyLabels[0].first == 12 ); + REQUIRE( keyLabels[0].second == "Cymbals" ); + REQUIRE( keyLabels[1].first == 65 ); + REQUIRE( keyLabels[1].second == "Crash" ); REQUIRE( ccLabels.size() == 2); REQUIRE( ccLabels[0].first == 54 ); REQUIRE( ccLabels[0].second == "Gain" ); diff --git a/tests/TestFiles/labels.sfz b/tests/TestFiles/labels.sfz index 49f51ed57..ceb9f17b9 100644 --- a/tests/TestFiles/labels.sfz +++ b/tests/TestFiles/labels.sfz @@ -1,7 +1,7 @@ label_cc54=Gain label_cc2=Other -label_note12=Cymbals -label_note65=Crash -label_note128=Ignored +label_key12=Cymbals +label_key65=Crash +label_key128=Ignored sample=*sine From 64a591fb7a4ff9f12c322cf724c4c48b7c9c10d5 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 17:38:31 +0200 Subject: [PATCH 073/444] Parse offset_cc --- src/sfizz/Defaults.h | 1 + src/sfizz/Region.cpp | 6 ++++++ src/sfizz/Region.h | 1 + tests/RegionT.cpp | 13 +++++++++++++ 4 files changed, 21 insertions(+) diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 8506d90f2..34a43ba55 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -49,6 +49,7 @@ namespace Default constexpr uint32_t offset { 0 }; constexpr uint32_t offsetRandom { 0 }; constexpr Range offsetRange { 0, std::numeric_limits::max() }; + constexpr Range offsetCCRange = offsetRange; constexpr Range sampleEndRange { 0, std::numeric_limits::max() }; constexpr Range sampleCountRange { 0, std::numeric_limits::max() }; constexpr SfzLoopMode loopMode { SfzLoopMode::no_loop }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 3411e9159..a8ffa43ff 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -59,6 +59,12 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) case hash("offset_random"): setValueFromOpcode(opcode, offsetRandom, Default::offsetRange); break; + case hash("offset_cc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::offsetCCRange)) + offsetCC[opcode.parameters.back()] = *value; + break; case hash("end"): setValueFromOpcode(opcode, sampleEnd, Default::sampleEndRange); break; diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index fa9526755..6e93b96fd 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -232,6 +232,7 @@ struct Region { float delayRandom { Default::delayRandom }; // delay_random uint32_t offset { Default::offset }; // offset uint32_t offsetRandom { Default::offsetRandom }; // offset_random + CCMap offsetCC { Default::offset }; uint32_t sampleEnd { Default::sampleEndRange.getEnd() }; // end absl::optional sampleCount {}; // count absl::optional loopMode {}; // loopmode diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index f982c424f..c90ebc784 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -53,6 +53,19 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "offset", "-1" }); REQUIRE(region.offset == 0); } + SECTION("offset_cc") + { + REQUIRE(region.offsetCC.empty()); + region.parseOpcode({ "offset_cc1", "1" }); + REQUIRE(region.offsetCC.contains(1)); + REQUIRE(region.offsetCC[1] == 1); + region.parseOpcode({ "offset_cc2", "15420" }); + REQUIRE(region.offsetCC.contains(2)); + REQUIRE(region.offsetCC[2] == 15420); + region.parseOpcode({ "offset_cc2", "-1" }); + REQUIRE(region.offsetCC[2] == 0); + } + SECTION("offset_random") { From 02ad421fa3208e795a11f83c940114f569a520a0 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 17:38:38 +0200 Subject: [PATCH 074/444] Adapt preload size --- src/sfizz/Synth.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index fd12b218b..46c0109d4 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -374,7 +374,16 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) region->hasStereoSample = true; // TODO: adjust with LFO targets - const auto maxOffset = region->offset + region->offsetRandom; + const auto maxOffset = [region]() { + uint64_t sumOffsetCC = region->offset + region->offsetRandom; + for (const auto& offsets: region->offsetCC) + sumOffsetCC += offsets.value; + if (static_cast(Default::offsetCCRange.getEnd()) < sumOffsetCC) + return Default::offsetCCRange.getEnd(); + + return static_cast(sumOffsetCC); + }(); + if (!resources.filePool.preloadFile(region->sample, maxOffset)) removeCurrentRegion(); } From 7a59b35b803d1973ca587f8d65229356bcd0cd60 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 17:55:22 +0200 Subject: [PATCH 075/444] Used clamp int64 and get the modified offsets in getOffset() --- src/sfizz/Defaults.h | 8 ++++---- src/sfizz/Region.cpp | 9 ++++++--- src/sfizz/Region.h | 8 ++++---- src/sfizz/Synth.cpp | 5 +---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 34a43ba55..4fe7e973e 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -46,10 +46,10 @@ namespace Default constexpr float delay { 0.0 }; constexpr float delayRandom { 0.0 }; constexpr Range delayRange { 0.0, 100.0 }; - constexpr uint32_t offset { 0 }; - constexpr uint32_t offsetRandom { 0 }; - constexpr Range offsetRange { 0, std::numeric_limits::max() }; - constexpr Range offsetCCRange = offsetRange; + constexpr int64_t offset { 0 }; + constexpr int64_t offsetRandom { 0 }; + constexpr Range offsetRange { 0, std::numeric_limits::max() }; + constexpr Range offsetCCRange = offsetRange; constexpr Range sampleEndRange { 0, std::numeric_limits::max() }; constexpr Range sampleCountRange { 0, std::numeric_limits::max() }; constexpr SfzLoopMode loopMode { SfzLoopMode::no_loop }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index a8ffa43ff..8fbed19c2 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -1140,10 +1140,13 @@ float sfz::Region::getPhase() const noexcept return phase; } -uint32_t sfz::Region::getOffset(Oversampling factor) const noexcept +uint64_t sfz::Region::getOffset(Oversampling factor) const noexcept { - std::uniform_int_distribution offsetDistribution { 0, offsetRandom }; - return (offset + offsetDistribution(Random::randomGenerator)) * static_cast(factor); + std::uniform_int_distribution offsetDistribution { 0, offsetRandom }; + uint64_t finalOffset = offset + offsetDistribution(Random::randomGenerator); + for (const auto& mod: offsetCC) + finalOffset += static_cast(mod.value * midiState.getCCValue(mod.cc)); + return Default::offsetCCRange.clamp(offset + offsetDistribution(Random::randomGenerator)) * static_cast(factor); } float sfz::Region::getDelay() const noexcept diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 6e93b96fd..9540b1cce 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -189,7 +189,7 @@ struct Region { * * @return uint32_t */ - uint32_t getOffset(Oversampling factor = Oversampling::x1) const noexcept; + uint64_t getOffset(Oversampling factor = Oversampling::x1) const noexcept; /** * @brief Get the region delay in seconds * @@ -230,9 +230,9 @@ struct Region { std::string sample {}; // Sample float delay { Default::delay }; // delay float delayRandom { Default::delayRandom }; // delay_random - uint32_t offset { Default::offset }; // offset - uint32_t offsetRandom { Default::offsetRandom }; // offset_random - CCMap offsetCC { Default::offset }; + int64_t offset { Default::offset }; // offset + int64_t offsetRandom { Default::offsetRandom }; // offset_random + CCMap offsetCC { Default::offset }; uint32_t sampleEnd { Default::sampleEndRange.getEnd() }; // end absl::optional sampleCount {}; // count absl::optional loopMode {}; // loopmode diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 46c0109d4..0913f9a61 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -378,10 +378,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) uint64_t sumOffsetCC = region->offset + region->offsetRandom; for (const auto& offsets: region->offsetCC) sumOffsetCC += offsets.value; - if (static_cast(Default::offsetCCRange.getEnd()) < sumOffsetCC) - return Default::offsetCCRange.getEnd(); - - return static_cast(sumOffsetCC); + return Default::offsetCCRange.clamp(sumOffsetCC); }(); if (!resources.filePool.preloadFile(region->sample, maxOffset)) From 0ceb58e6ae6fbb1f90dafbc8729a7aa841044392 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 7 Apr 2020 18:26:04 +0200 Subject: [PATCH 076/444] Rebase on the new modifiers --- src/sfizz/Region.cpp | 2 +- src/sfizz/Synth.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 8fbed19c2..501550da0 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -1145,7 +1145,7 @@ uint64_t sfz::Region::getOffset(Oversampling factor) const noexcept std::uniform_int_distribution offsetDistribution { 0, offsetRandom }; uint64_t finalOffset = offset + offsetDistribution(Random::randomGenerator); for (const auto& mod: offsetCC) - finalOffset += static_cast(mod.value * midiState.getCCValue(mod.cc)); + finalOffset += static_cast(mod.data * midiState.getCCValue(mod.cc)); return Default::offsetCCRange.clamp(offset + offsetDistribution(Random::randomGenerator)) * static_cast(factor); } diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 0913f9a61..d52aa86c2 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -377,7 +377,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) const auto maxOffset = [region]() { uint64_t sumOffsetCC = region->offset + region->offsetRandom; for (const auto& offsets: region->offsetCC) - sumOffsetCC += offsets.value; + sumOffsetCC += offsets.data; return Default::offsetCCRange.clamp(sumOffsetCC); }(); From ad16540778bafb1a2efe24e0111d138f78dcc5ec Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 15 Apr 2020 18:41:06 +0200 Subject: [PATCH 077/444] Remove an excess space character [ci skip] --- src/sfizz/Defaults.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 4fe7e973e..2b12a5048 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -49,7 +49,7 @@ namespace Default constexpr int64_t offset { 0 }; constexpr int64_t offsetRandom { 0 }; constexpr Range offsetRange { 0, std::numeric_limits::max() }; - constexpr Range offsetCCRange = offsetRange; + constexpr Range offsetCCRange = offsetRange; constexpr Range sampleEndRange { 0, std::numeric_limits::max() }; constexpr Range sampleCountRange { 0, std::numeric_limits::max() }; constexpr SfzLoopMode loopMode { SfzLoopMode::no_loop }; From aa4af91131b9bc8b1152f6fd80e56187ffbeee31 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 16 Apr 2020 00:53:21 +0200 Subject: [PATCH 078/444] Parse sw_label --- src/sfizz/Region.cpp | 3 +++ src/sfizz/Region.h | 1 + tests/RegionT.cpp | 9 +++++++++ 3 files changed, 13 insertions(+) diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 3411e9159..fd87ef304 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -199,6 +199,9 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) setValueFromOpcode(opcode, keyswitch, Default::keyRange); keySwitched = false; break; + case hash("sw_label"): + keyswitchLabel = opcode.value; + break; case hash("sw_down"): setValueFromOpcode(opcode, keyswitchDown, Default::keyRange); keySwitched = false; diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index fa9526755..66a0e94eb 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -259,6 +259,7 @@ struct Region { CCMap> ccConditions { Default::ccValueRange }; Range keyswitchRange { Default::keyRange }; // sw_hikey and sw_lokey absl::optional keyswitch {}; // sw_last + absl::optional keyswitchLabel {}; absl::optional keyswitchUp {}; // sw_up absl::optional keyswitchDown {}; // sw_down absl::optional previousNote {}; // sw_previous diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index f982c424f..fe33765c0 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -273,6 +273,15 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.keyswitchRange == sfz::Range(0, 0)); } + SECTION("sw_label") + { + REQUIRE(!region.keyswitchLabel); + region.parseOpcode({ "sw_label", "note" }); + REQUIRE(region.keyswitchLabel == "note"); + region.parseOpcode({ "sw_label", "ring" }); + REQUIRE(region.keyswitchLabel == "ring"); + } + SECTION("sw_last") { REQUIRE(!region.keyswitch); From 4b55f955d639b5d71beb0b77f5f73cab6f80da4e Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 16 Apr 2020 00:53:40 +0200 Subject: [PATCH 079/444] Add switch labels to the midnam --- src/sfizz/Synth.cpp | 9 +++++++++ src/sfizz/Synth.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 03f3cc34a..6c30fe568 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -396,6 +396,10 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) } } + if (region->keyswitchLabel && region->keyswitch) + keyswitchLabels.push_back({ *region->keyswitch, *region->keyswitchLabel }); + + // Some regions had group number but no "group-level" opcodes handled the polyphony while (groupMaxPolyphony.size() <= region->group) groupMaxPolyphony.push_back(config::maxVoices); @@ -891,6 +895,11 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const { pugi::xml_node nnl = device.append_child("NoteNameList"); nnl.append_attribute("Name").set_value("Notes"); + for (const auto& pair : keyswitchLabels) { + pugi::xml_node nn = nnl.append_child("Note"); + nn.append_attribute("Number").set_value(std::to_string(pair.first).c_str()); + nn.append_attribute("Name").set_value(pair.second.c_str()); + } for (const auto& pair : keyLabels) { pugi::xml_node nn = nnl.append_child("Note"); nn.append_attribute("Number").set_value(std::to_string(pair.first).c_str()); diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index 738e54d23..0eddea0b1 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -511,6 +511,7 @@ class Synth final : public Parser::Listener { // Names for the CC and notes as set by label_cc and label_key std::vector ccLabels; std::vector keyLabels; + std::vector keyswitchLabels; // Default active switch if multiple keyswitchable regions are present absl::optional defaultSwitch; From e4103600ec4dbc10d3e883582a5f670638172022 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Thu, 16 Apr 2020 14:08:11 +0200 Subject: [PATCH 080/444] Fix some numCC comparisons which are off by 1 --- src/sfizz/MidiState.cpp | 2 +- src/sfizz/Region.cpp | 64 ++++++++++++++++++++--------------------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/sfizz/MidiState.cpp b/src/sfizz/MidiState.cpp index 0b932010b..6f4630201 100644 --- a/src/sfizz/MidiState.cpp +++ b/src/sfizz/MidiState.cpp @@ -155,7 +155,7 @@ void sfz::MidiState::resetAllControllers(int delay) noexcept const sfz::EventVector& sfz::MidiState::getCCEvents(int ccIdx) const noexcept { - if (ccIdx < 0 || ccIdx > config::numCCs) + if (ccIdx < 0 || ccIdx >= config::numCCs) return nullEvent; return cc[ccIdx]; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 501550da0..992618394 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -184,13 +184,13 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) bendRange.setEnd(normalizeBend(*value)); break; case hash("locc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::midi7Range)) ccConditions[opcode.parameters.back()].setStart(normalizeCC(*value)); break; case hash("hicc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::midi7Range)) ccConditions[opcode.parameters.back()].setEnd(normalizeCC(*value)); @@ -285,14 +285,14 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) break; case hash("on_locc&"): // fallthrough case hash("start_locc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::midi7Range)) ccTriggers[opcode.parameters.back()].setStart(normalizeCC(*value)); break; case hash("on_hicc&"): // fallthrough case hash("start_hicc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::midi7Range)) ccTriggers[opcode.parameters.back()].setEnd(normalizeCC(*value)); @@ -303,19 +303,19 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) setValueFromOpcode(opcode, volume, Default::volumeRange); break; case hash("volume_curvecc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::curveCCRange)) volumeCC[opcode.parameters.back()].curve = *value; break; case hash("volume_stepcc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::volumeStepRange)) volumeCC[opcode.parameters.back()].step = *value; break; case hash("volume_smoothcc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::smoothCCRange)) volumeCC[opcode.parameters.back()].smooth = *value; @@ -323,7 +323,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) case hash("gain_cc&"): case hash("gain_oncc&"): // fallthrough case hash("volume_oncc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::volumeCCRange)) volumeCC[opcode.parameters.back()].value = *value; @@ -333,26 +333,26 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) amplitude = normalizePercents(*value); break; case hash("amplitude_curvecc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::curveCCRange)) amplitudeCC[opcode.parameters.back()].curve = *value; break; case hash("amplitude_stepcc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) amplitudeCC[opcode.parameters.back()].step = normalizePercents(*value); break; case hash("amplitude_smoothcc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::smoothCCRange)) amplitudeCC[opcode.parameters.back()].smooth = *value; break; case hash("amplitude_cc&"): // fallthrough case hash("amplitude_oncc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) amplitudeCC[opcode.parameters.back()].value = normalizePercents(*value); @@ -362,26 +362,26 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) pan = normalizePercents(*value); break; case hash("pan_curvecc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::curveCCRange)) panCC[opcode.parameters.back()].curve = *value; break; case hash("pan_stepcc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::panStepRange)) panCC[opcode.parameters.back()].step = normalizePercents(*value); break; case hash("pan_smoothcc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::smoothCCRange)) panCC[opcode.parameters.back()].smooth = *value; break; case hash("pan_cc&"): case hash("pan_oncc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::panCCRange)) panCC[opcode.parameters.back()].value = normalizePercents(*value); @@ -391,26 +391,26 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) position = normalizePercents(*value); break; case hash("position_curvecc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::curveCCRange)) positionCC[opcode.parameters.back()].curve = *value; break; case hash("position_stepcc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::positionStepRange)) positionCC[opcode.parameters.back()].step = normalizePercents(*value); break; case hash("position_smoothcc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::smoothCCRange)) positionCC[opcode.parameters.back()].smooth = *value; break; case hash("position_cc&"): // fallthrough case hash("position_oncc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::positionCCRange)) positionCC[opcode.parameters.back()].value = normalizePercents(*value); @@ -420,25 +420,25 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) width = normalizePercents(*value); break; case hash("width_curvecc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::curveCCRange)) widthCC[opcode.parameters.back()].curve = *value; break; case hash("width_stepcc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::widthStepRange)) widthCC[opcode.parameters.back()].step = normalizePercents(*value); break; case hash("width_smoothcc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::smoothCCRange)) widthCC[opcode.parameters.back()].smooth = *value; break; case hash("width_oncc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::widthCCRange)) widthCC[opcode.parameters.back()].value = normalizePercents(*value); @@ -518,25 +518,25 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) } break; case hash("xfin_locc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::midi7Range)) crossfadeCCInRange[opcode.parameters.back()].setStart(normalizeCC(*value)); break; case hash("xfin_hicc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::midi7Range)) crossfadeCCInRange[opcode.parameters.back()].setEnd(normalizeCC(*value)); break; case hash("xfout_locc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::midi7Range)) crossfadeCCOutRange[opcode.parameters.back()].setStart(normalizeCC(*value)); break; case hash("xfout_hicc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::midi7Range)) crossfadeCCOutRange[opcode.parameters.back()].setEnd(normalizeCC(*value)); @@ -824,21 +824,21 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) break; case hash("pitch_curvecc&"): // fallthrough case hash("tune_curvecc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::curveCCRange)) tuneCC[opcode.parameters.back()].curve = *value; break; case hash("pitch_stepcc&"): // fallthrough case hash("tune_stepcc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::tuneStepRange)) tuneCC[opcode.parameters.back()].step = *value; break; case hash("pitch_smoothcc&"): // fallthrough case hash("tune_smoothcc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::smoothCCRange)) tuneCC[opcode.parameters.back()].smooth = *value; @@ -847,7 +847,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) case hash("tune_oncc&"): case hash("pitch_cc&"): case hash("pitch_oncc&"): - if (opcode.parameters.back() > config::numCCs) + if (opcode.parameters.back() >= config::numCCs) return false; if (auto value = readOpcode(opcode.value, Default::tuneCCRange)) tuneCC[opcode.parameters.back()].value = *value; From b6a437e4a4206fc2ca86a58909fd310e64cf7a01 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 15 Apr 2020 21:35:16 +0200 Subject: [PATCH 081/444] Support the direction opcode --- src/CMakeLists.txt | 1 + src/sfizz/FileId.cpp | 24 +++++++++ src/sfizz/FileId.h | 67 +++++++++++++++++++++++++ src/sfizz/FilePool.cpp | 108 ++++++++++++++++++++++++----------------- src/sfizz/FilePool.h | 28 +++++------ src/sfizz/Region.cpp | 7 ++- src/sfizz/Region.h | 5 +- src/sfizz/Synth.cpp | 12 ++--- src/sfizz/Voice.cpp | 10 ++-- 9 files changed, 189 insertions(+), 73 deletions(-) create mode 100644 src/sfizz/FileId.cpp create mode 100644 src/sfizz/FileId.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5b096fa3d..1a02ee538 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(external/cpuid) set (SFIZZ_SOURCES sfizz/Synth.cpp + sfizz/FileId.cpp sfizz/FilePool.cpp sfizz/FilterPool.cpp sfizz/EQPool.cpp diff --git a/src/sfizz/FileId.cpp b/src/sfizz/FileId.cpp new file mode 100644 index 000000000..8355ab708 --- /dev/null +++ b/src/sfizz/FileId.cpp @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "FileId.h" +#include "StringViewHelpers.h" +#include + +size_t std::hash::operator()(const sfz::FileId &id) const +{ + uint64_t h = ::hash(id.filename); + h = ::hash(id.reverse ? "!" : "", h); + return h; +} + +std::ostream &operator<<(std::ostream &os, const sfz::FileId &fileId) +{ + os << fileId.filename; + if (fileId.reverse) + os << " (reverse)"; + return os; +} diff --git a/src/sfizz/FileId.h b/src/sfizz/FileId.h new file mode 100644 index 000000000..7193bdd06 --- /dev/null +++ b/src/sfizz/FileId.h @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include +#include + +namespace sfz { + +/** + * @brief Sample file identifier within a file pool. + */ +struct FileId { + std::string filename; + bool reverse = false; + + /** + * @brief Construct a null identifier. + */ + FileId() + { + } + + /** + * @brief Construct a file identifier, optionally reversed. + * + * @param filename + * @param reverse + */ + FileId(std::string filename, bool reverse = false) + : filename(std::move(filename)), reverse(reverse) + { + } + + /** + * @brief Check equality with another identifier. + * + * @param other + */ + bool operator==(const FileId &other) const + { + return reverse == other.reverse && filename == other.filename; + } + + /** + * @brief Check inequality with another identifier. + * + * @param other + */ + bool operator!=(const FileId &other) const + { + return !operator==(other); + } +}; + +} + +namespace std { + template <> struct hash { + size_t operator()(const sfz::FileId &id) const; + }; +} + +std::ostream &operator<<(std::ostream &os, const sfz::FileId &fileId); diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index c474b6348..f9f38ae7d 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -32,32 +32,47 @@ #include "absl/types/span.h" #include "absl/strings/match.h" #include "absl/memory/memory.h" +#include #include #include #include template -void readBaseFile(SndfileHandle& sndFile, sfz::AudioBuffer& output, uint32_t numFrames) +void readBaseFile(SndfileHandle& sndFile, sfz::AudioBuffer& output, uint32_t numFrames, bool reverse) { output.reset(); output.resize(numFrames); - if (sndFile.channels() == 1) { + + if (reverse) + sndFile.seek(-static_cast(numFrames), SEEK_END); + + const unsigned channels = sndFile.channels(); + + if (channels == 1) { output.addChannel(); sndFile.readf(output.channelWriter(0), numFrames); - } else if (sndFile.channels() == 2) { + } else if (channels == 2) { output.addChannel(); output.addChannel(); sfz::Buffer tempReadBuffer { 2 * numFrames }; sndFile.readf(tempReadBuffer.data(), numFrames); sfz::readInterleaved(tempReadBuffer, output.getSpan(0), output.getSpan(1)); } + + if (reverse) { + for (unsigned c = 0; c < channels; ++c) { + // TODO: consider optimizing with SIMD + absl::Span channel = output.getSpan(c); + std::reverse(channel.begin(), channel.end()); + } + } } template -std::unique_ptr> readFromFile(SndfileHandle& sndFile, uint32_t numFrames, sfz::Oversampling factor) +std::unique_ptr> readFromFile(SndfileHandle& sndFile, uint32_t numFrames, sfz::Oversampling factor, bool reverse) { auto baseBuffer = absl::make_unique>(); - readBaseFile(sndFile, *baseBuffer, numFrames); + readBaseFile(sndFile, *baseBuffer, numFrames, reverse); if (factor == sfz::Oversampling::x1) return baseBuffer; @@ -69,16 +84,16 @@ std::unique_ptr> readFromFile(SndfileHandle& sndFile, uint32 } template -void streamFromFile(SndfileHandle& sndFile, uint32_t numFrames, sfz::Oversampling factor, sfz::AudioBuffer& output, std::atomic* filledFrames=nullptr) +void streamFromFile(SndfileHandle& sndFile, uint32_t numFrames, sfz::Oversampling factor, bool reverse, sfz::AudioBuffer& output, std::atomic* filledFrames = nullptr) { if (factor == sfz::Oversampling::x1) { - readBaseFile(sndFile, output, numFrames); + readBaseFile(sndFile, output, numFrames, reverse); if (filledFrames != nullptr) filledFrames->store(numFrames); return; } - auto baseBuffer = readFromFile(sndFile, numFrames, sfz::Oversampling::x1); + auto baseBuffer = readFromFile(sndFile, numFrames, sfz::Oversampling::x1, reverse); output.reset(); output.addChannels(baseBuffer->getNumChannels()); output.resize(numFrames * static_cast(factor)); @@ -175,16 +190,16 @@ bool sfz::FilePool::checkSample(std::string& filename) const noexcept #endif } -absl::optional sfz::FilePool::getFileInformation(const std::string& filename) noexcept +absl::optional sfz::FilePool::getFileInformation(const FileId& fileId) noexcept { - fs::path file { rootDirectory / filename }; + const fs::path file { rootDirectory / fileId.filename }; if (!fs::exists(file)) return {}; SndfileHandle sndFile(file.string().c_str()); if (sndFile.channels() != 1 && sndFile.channels() != 2) { - DBG("[sfizz] Missing logic for " << sndFile.channels() << " channels, discarding sample " << filename); + DBG("[sfizz] Missing logic for " << sndFile.channels() << " channels, discarding sample " << fileId); return {}; } @@ -193,23 +208,28 @@ absl::optional sfz::FilePool::getFileInformation(const std returnedValue.sampleRate = static_cast(sndFile.samplerate()); returnedValue.numChannels = sndFile.channels(); - SF_INSTRUMENT instrumentInfo; - sndFile.command(SFC_GET_INSTRUMENT, &instrumentInfo, sizeof(instrumentInfo)); - if (instrumentInfo.loop_count > 0) { - returnedValue.loopBegin = instrumentInfo.loops[0].start; - returnedValue.loopEnd = min(returnedValue.end, instrumentInfo.loops[0].end - 1); + if (!fileId.reverse) { + SF_INSTRUMENT instrumentInfo; + sndFile.command(SFC_GET_INSTRUMENT, &instrumentInfo, sizeof(instrumentInfo)); + if (instrumentInfo.loop_count > 0) { + returnedValue.loopBegin = instrumentInfo.loops[0].start; + returnedValue.loopEnd = min(returnedValue.end, instrumentInfo.loops[0].end - 1); + } + } else { + // TODO loops ignored when reversed + // prehaps it can make use of SF_LOOP_BACKWARD? } return returnedValue; } -bool sfz::FilePool::preloadFile(const std::string& filename, uint32_t maxOffset) noexcept +bool sfz::FilePool::preloadFile(const FileId& fileId, uint32_t maxOffset) noexcept { - fs::path file { rootDirectory / filename }; - auto fileInformation = getFileInformation(filename); + auto fileInformation = getFileInformation(fileId); if (!fileInformation) return false; + const fs::path file { rootDirectory / fileId.filename }; SndfileHandle sndFile(file.string().c_str()); // FIXME: Large offsets will require large preloading; is this OK in practice? Apparently sforzando does the same @@ -221,69 +241,69 @@ bool sfz::FilePool::preloadFile(const std::string& filename, uint32_t maxOffset) return min(frames, maxOffset + preloadSize); }(); - const auto existingFile = preloadedFiles.find(filename); + const auto existingFile = preloadedFiles.find(fileId); if (existingFile != preloadedFiles.end()) { if (framesToLoad > existingFile->second.preloadedData->getNumFrames()) { - preloadedFiles[filename].preloadedData = readFromFile(sndFile, framesToLoad, oversamplingFactor); + preloadedFiles[fileId].preloadedData = readFromFile(sndFile, framesToLoad, oversamplingFactor, fileId.reverse); } } else { fileInformation->sampleRate = static_cast(oversamplingFactor) * static_cast(sndFile.samplerate()); FileDataHandle handle { - readFromFile(sndFile, framesToLoad, oversamplingFactor), + readFromFile(sndFile, framesToLoad, oversamplingFactor, fileId.reverse), *fileInformation }; - preloadedFiles.insert_or_assign(filename, handle); + preloadedFiles.insert_or_assign(fileId, handle); } return true; } -absl::optional sfz::FilePool::loadFile(const std::string& filename) noexcept +absl::optional sfz::FilePool::loadFile(const FileId& fileId) noexcept { - fs::path file { rootDirectory / filename }; - auto fileInformation = getFileInformation(filename); + auto fileInformation = getFileInformation(fileId); if (!fileInformation) return {}; + const fs::path file { rootDirectory / fileId.filename }; SndfileHandle sndFile(file.string().c_str()); // FIXME: Large offsets will require large preloading; is this OK in practice? Apparently sforzando does the same const auto frames = static_cast(sndFile.frames()); - const auto existingFile = loadedFiles.find(filename); + const auto existingFile = loadedFiles.find(fileId); if (existingFile != loadedFiles.end()) { return existingFile->second; } else { fileInformation->sampleRate = static_cast(oversamplingFactor) * static_cast(sndFile.samplerate()); FileDataHandle handle { - readFromFile(sndFile, frames, oversamplingFactor), + readFromFile(sndFile, frames, oversamplingFactor, fileId.reverse), *fileInformation }; - loadedFiles.insert_or_assign(filename, handle); + loadedFiles.insert_or_assign(fileId, handle); return handle; } } -sfz::FilePromisePtr sfz::FilePool::getFilePromise(const std::string& filename) noexcept +sfz::FilePromisePtr sfz::FilePool::getFilePromise(const FileId& fileId) noexcept { if (emptyPromises.empty()) { - DBG("[sfizz] No empty promises left to honor the one for " << filename); + DBG("[sfizz] No empty promises left to honor the one for " << fileId); return {}; } - const auto preloaded = preloadedFiles.find(filename); + const auto preloaded = preloadedFiles.find(fileId); if (preloaded == preloadedFiles.end()) { - DBG("[sfizz] File not found in the preloaded files: " << filename); + DBG("[sfizz] File not found in the preloaded files: " << fileId); return {}; } auto promise = emptyPromises.back(); - promise->filename = preloaded->first; + promise->fileId = preloaded->first; promise->preloadedData = preloaded->second.preloadedData; promise->sampleRate = static_cast(preloaded->second.information.sampleRate); promise->oversamplingFactor = oversamplingFactor; promise->creationTime = std::chrono::high_resolution_clock::now(); if (!promiseQueue.try_push(promise)) { - DBG("[sfizz] Could not enqueue the promise for " << filename << " (queue capacity " << promiseQueue.capacity() << ")"); + DBG("[sfizz] Could not enqueue the promise for " << fileId << " (queue capacity " << promiseQueue.capacity() << ")"); return {}; } @@ -302,9 +322,9 @@ void sfz::FilePool::setPreloadSize(uint32_t preloadSize) noexcept for (auto& preloadedFile : preloadedFiles) { const auto numFrames = preloadedFile.second.preloadedData->getNumFrames() / static_cast(oversamplingFactor); const auto maxOffset = numFrames > this->preloadSize ? static_cast(numFrames) - this->preloadSize : 0; - fs::path file { rootDirectory / std::string(preloadedFile.first) }; + fs::path file { rootDirectory / preloadedFile.first.filename }; SndfileHandle sndFile(file.string().c_str()); - preloadedFile.second.preloadedData = readFromFile(sndFile, preloadSize + maxOffset, oversamplingFactor); + preloadedFile.second.preloadedData = readFromFile(sndFile, preloadSize + maxOffset, oversamplingFactor, preloadedFile.first.reverse); } this->preloadSize = preloadSize; } @@ -352,23 +372,23 @@ void sfz::FilePool::loadingThread() noexcept const auto loadStartTime = std::chrono::high_resolution_clock::now(); const auto waitDuration = loadStartTime - promise->creationTime; - fs::path file { rootDirectory / std::string(promise->filename) }; + const fs::path file { rootDirectory / promise->fileId.filename }; SndfileHandle sndFile(file.string().c_str()); if (sndFile.error() != 0) { - DBG("[sfizz] libsndfile errored for " << promise->filename << " with message " << sndFile.strError()); + DBG("[sfizz] libsndfile errored for " << promise->fileId << " with message " << sndFile.strError()); promise->dataStatus = FilePromise::DataStatus::Error; continue; } const auto frames = static_cast(sndFile.frames()); - streamFromFile(sndFile, frames, oversamplingFactor, promise->fileData, &promise->availableFrames); + streamFromFile(sndFile, frames, oversamplingFactor, promise->fileId.reverse, promise->fileData, &promise->availableFrames); promise->dataStatus = FilePromise::DataStatus::Ready; const auto loadDuration = std::chrono::high_resolution_clock::now() - loadStartTime; - logger.logFileTime(waitDuration, loadDuration, frames, promise->filename); + logger.logFileTime(waitDuration, loadDuration, frames, promise->fileId.filename); threadsLoading--; while (!filledPromiseQueue.try_push(promise)) { - DBG("[sfizz] Error enqueuing the promise for " << promise->filename << " in the filledPromiseQueue"); + DBG("[sfizz] Error enqueuing the promise for " << promise->fileId << " in the filledPromiseQueue"); std::this_thread::sleep_for(std::chrono::milliseconds(1)); } @@ -431,9 +451,9 @@ void sfz::FilePool::setOversamplingFactor(sfz::Oversampling factor) noexcept for (auto& preloadedFile : preloadedFiles) { const auto numFrames = preloadedFile.second.preloadedData->getNumFrames() / static_cast(this->oversamplingFactor); const uint32_t maxOffset = numFrames > this->preloadSize ? static_cast(numFrames) - this->preloadSize : 0; - fs::path file { rootDirectory / std::string(preloadedFile.first) }; + fs::path file { rootDirectory / preloadedFile.first.filename }; SndfileHandle sndFile(file.string().c_str()); - preloadedFile.second.preloadedData = readFromFile(sndFile, preloadSize + maxOffset, factor); + preloadedFile.second.preloadedData = readFromFile(sndFile, preloadSize + maxOffset, factor, preloadedFile.first.reverse); preloadedFile.second.information.sampleRate *= samplerateChange; } diff --git a/src/sfizz/FilePool.h b/src/sfizz/FilePool.h index c2600ecee..631dd6f6c 100644 --- a/src/sfizz/FilePool.h +++ b/src/sfizz/FilePool.h @@ -30,6 +30,7 @@ #include "RTSemaphore.h" #include "AudioBuffer.h" #include "AudioSpan.h" +#include "FileId.h" #include "SIMDHelpers.h" #include "ghc/fs_std.hpp" #include @@ -44,7 +45,6 @@ namespace sfz { using AudioBufferPtr = std::shared_ptr>; - struct FileInformation { uint32_t end { Default::sampleEndRange.getEnd() }; uint32_t loopBegin { Default::loopRange.getStart() }; @@ -76,7 +76,7 @@ struct FilePromise { fileData.reset(); preloadedData.reset(); - filename = ""; + fileId = FileId {}; availableFrames = 0; dataStatus = DataStatus::Wait; oversamplingFactor = config::defaultOversamplingFactor; @@ -95,7 +95,7 @@ struct FilePromise Error, }; - absl::string_view filename {}; + FileId fileId {}; AudioBufferPtr preloadedData {}; AudioBuffer fileData {}; float sampleRate { config::defaultSampleRate }; @@ -155,30 +155,30 @@ class FilePool { /** * @brief Get metadata information about a file. * - * @param filename + * @param fileId * @return absl::optional */ - absl::optional getFileInformation(const std::string& filename) noexcept; + absl::optional getFileInformation(const FileId& fileId) noexcept; /** * @brief Preload a file with the proper offset bounds * - * @param filename - * @param offset the maximum offset to consider for preloading. The total preloaded + * @param fileId + * @param maxOffset the maximum offset to consider for preloading. The total preloaded * size will be preloadSize + offset * @return true if the preloading went fine * @return false if something went wrong () */ - bool preloadFile(const std::string& filename, uint32_t maxOffset) noexcept; + bool preloadFile(const FileId& fileId, uint32_t maxOffset) noexcept; /** * @brief Load a file and return its information. The file pool will store this * data for future requests so use this function responsibly. * - * @param filename + * @param fileId * @return A handle on the file data */ - absl::optional loadFile(const std::string& filename) noexcept; + absl::optional loadFile(const FileId& fileId) noexcept; /** * @brief Check that the sample exists. If not, try to find it in a case insensitive way. @@ -204,10 +204,10 @@ class FilePool { /** * @brief Get a file promise * - * @param filename the file to preload + * @param fileId the file to preload * @return FilePromisePtr a file promise */ - FilePromisePtr getFilePromise(const std::string& filename) noexcept; + FilePromisePtr getFilePromise(const FileId& fileId) noexcept; /** * @brief Change the preloading size. This will trigger a full * reload of all samples, so don't call it on the audio thread. @@ -270,8 +270,8 @@ class FilePool { std::mutex promiseGuard; // Preloaded data - absl::flat_hash_map preloadedFiles; - absl::flat_hash_map loadedFiles; + absl::flat_hash_map preloadedFiles; + absl::flat_hash_map loadedFiles; std::vector threadPool { }; LEAK_DETECTOR(FilePool); }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 992618394..2d95854aa 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -42,11 +42,14 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) break; if (trimmedSample[0] == '*') - sample = std::string(trimmedSample); + sampleId.filename = std::string(trimmedSample); else - sample = absl::StrCat(defaultPath, absl::StrReplaceAll(trimmedSample, { { "\\", "/" } })); + sampleId.filename = absl::StrCat(defaultPath, absl::StrReplaceAll(trimmedSample, { { "\\", "/" } })); } break; + case hash("direction"): + sampleId.reverse = opcode.value == "reverse"; + break; case hash("delay"): setValueFromOpcode(opcode, delay, Default::delayRange); break; diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 9540b1cce..cd954671d 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -14,6 +14,7 @@ #include "Opcode.h" #include "AudioBuffer.h" #include "MidiState.h" +#include "FileId.h" #include "absl/types/optional.h" #include #include @@ -57,7 +58,7 @@ struct Region { * @return true * @return false */ - bool isGenerator() const noexcept { return sample.size() > 0 ? sample[0] == '*' : false; } + bool isGenerator() const noexcept { return sampleId.filename.size() > 0 ? sampleId.filename[0] == '*' : false; } /** * @brief Is stereo (has stereo sample or is unison oscillator)? * @@ -227,7 +228,7 @@ struct Region { float getGainToEffectBus(unsigned number) const noexcept; // Sound source: sample playback - std::string sample {}; // Sample + FileId sampleId {}; // Sample float delay { Default::delay }; // delay float delayRandom { Default::delayRandom }; // delay_random int64_t offset { Default::offset }; // offset diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index d52aa86c2..8f9f1b4f3 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -332,7 +332,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) if (currentRegion->get() == nullptr) return; - DBG("Removing the region with sample " << currentRegion->get()->sample); + DBG("Removing the region with sample " << currentRegion->get()->sampleId); std::iter_swap(currentRegion, lastRegion); ++lastRegion; }; @@ -344,12 +344,12 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) auto region = currentRegion->get(); if (!region->oscillator && !region->isGenerator()) { - if (!resources.filePool.checkSample(region->sample)) { + if (!resources.filePool.checkSample(region->sampleId.filename)) { removeCurrentRegion(); continue; } - const auto fileInformation = resources.filePool.getFileInformation(region->sample); + const auto fileInformation = resources.filePool.getFileInformation(region->sampleId); if (!fileInformation) { removeCurrentRegion(); continue; @@ -381,16 +381,16 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) return Default::offsetCCRange.clamp(sumOffsetCC); }(); - if (!resources.filePool.preloadFile(region->sample, maxOffset)) + if (!resources.filePool.preloadFile(region->sampleId, maxOffset)) removeCurrentRegion(); } else if (region->oscillator && !region->isGenerator()) { - if (!resources.filePool.checkSample(region->sample)) { + if (!resources.filePool.checkSample(region->sampleId.filename)) { removeCurrentRegion(); continue; } - if (!resources.wavePool.createFileWave(resources.filePool, region->sample)) { + if (!resources.wavePool.createFileWave(resources.filePool, region->sampleId.filename)) { removeCurrentRegion(); continue; } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index cfa8bfbef..68650ef0c 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -40,7 +40,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, if (region->isGenerator()) { const WavetableMulti* wave = nullptr; - switch (hash(region->sample)) { + switch (hash(region->sampleId.filename)) { default: case hash("*silence"): break; @@ -64,14 +64,14 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, } setupOscillatorUnison(); } else if (region->oscillator) { - const WavetableMulti* wave = resources.wavePool.getFileWave(region->sample); + const WavetableMulti* wave = resources.wavePool.getFileWave(region->sampleId.filename); for (WavetableOscillator& osc : waveOscillators) { osc.setWavetable(wave); osc.setPhase(region->getPhase()); } setupOscillatorUnison(); } else { - currentPromise = resources.filePool.getFilePromise(region->sample); + currentPromise = resources.filePool.getFilePromise(region->sampleId); if (currentPromise == nullptr) { reset(); return; @@ -483,7 +483,7 @@ void sfz::Voice::fillWithData(AudioSpan buffer) noexcept DBG("[sfizz] Underflow: source available samples " << source.getNumFrames() << "/" << region->trueSampleEnd(currentPromise->oversamplingFactor) - << " for sample " << region->sample); + << " for sample " << region->sampleId); } fill(indices->last(remainingElements), sampleEnd); fill(leftCoeffs->last(remainingElements), 0.0f); @@ -529,7 +529,7 @@ void sfz::Voice::fillWithGenerator(AudioSpan buffer) noexcept const auto leftSpan = buffer.getSpan(0); const auto rightSpan = buffer.getSpan(1); - if (region->sample == "*noise") { + if (region->sampleId.filename == "*noise") { absl::c_generate(leftSpan, [&](){ return noiseDist(Random::randomGenerator); }); absl::c_generate(rightSpan, [&](){ return noiseDist(Random::randomGenerator); }); } else { From 32d7a0aa48eac6b780e7f9eb3a2d8d32dc840825 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Thu, 16 Apr 2020 05:48:08 +0200 Subject: [PATCH 082/444] Add a test for opcode `direction` --- tests/RegionT.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index c90ebc784..12c764bae 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -23,6 +23,15 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.sample == "dummy.wav"); } + SECTION("direction") + { + REQUIRE(!region.sampleId.reverse); + region.parseOpcode({ "direction", "reverse" }); + REQUIRE(region.sampleId.reverse); + region.parseOpcode({ "direction", "forward" }); + REQUIRE(!region.sampleId.reverse); + } + SECTION("delay") { REQUIRE(region.delay == 0.0); From ff2b710937e862bbcce7a6d882dc561e95c2ef6b Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Thu, 16 Apr 2020 05:54:10 +0200 Subject: [PATCH 083/444] Update tests --- tests/FilesT.cpp | 112 +++++++++++++++++++++++----------------------- tests/RegionT.cpp | 4 +- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 4dd43c8fc..f332186c5 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -19,7 +19,7 @@ TEST_CASE("[Files] Single region (regions_one.sfz)") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_one.sfz"); REQUIRE(synth.getNumRegions() == 1); - REQUIRE(synth.getRegionView(0)->sample == "dummy.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy.wav"); } @@ -28,9 +28,9 @@ TEST_CASE("[Files] Multiple regions (regions_many.sfz)") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_many.sfz"); REQUIRE(synth.getNumRegions() == 3); - REQUIRE(synth.getRegionView(0)->sample == "dummy.wav"); - REQUIRE(synth.getRegionView(1)->sample == "dummy.1.wav"); - REQUIRE(synth.getRegionView(2)->sample == "dummy.2.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename == "dummy.1.wav"); + REQUIRE(synth.getRegionView(2)->sampleId.filename == "dummy.2.wav"); } TEST_CASE("[Files] Basic opcodes (regions_opcodes.sfz)") @@ -54,8 +54,8 @@ TEST_CASE("[Files] (regions_bad.sfz)") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_bad.sfz"); REQUIRE(synth.getNumRegions() == 2); - REQUIRE(synth.getRegionView(0)->sample == "dummy.wav"); - REQUIRE(synth.getRegionView(1)->sample == "dummy.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename == "dummy.wav"); } TEST_CASE("[Files] Local include") @@ -63,7 +63,7 @@ TEST_CASE("[Files] Local include") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_local.sfz"); REQUIRE(synth.getNumRegions() == 1); - REQUIRE(synth.getRegionView(0)->sample == "dummy.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy.wav"); } TEST_CASE("[Files] Multiple includes") @@ -71,8 +71,8 @@ TEST_CASE("[Files] Multiple includes") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/multiple_includes.sfz"); REQUIRE(synth.getNumRegions() == 2); - REQUIRE(synth.getRegionView(0)->sample == "dummy.wav"); - REQUIRE(synth.getRegionView(1)->sample == "dummy2.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename == "dummy2.wav"); } TEST_CASE("[Files] Multiple includes with comments") @@ -80,8 +80,8 @@ TEST_CASE("[Files] Multiple includes with comments") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/multiple_includes_with_comments.sfz"); REQUIRE(synth.getNumRegions() == 2); - REQUIRE(synth.getRegionView(0)->sample == "dummy.wav"); - REQUIRE(synth.getRegionView(1)->sample == "dummy2.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename == "dummy2.wav"); } TEST_CASE("[Files] Subdir include") @@ -89,7 +89,7 @@ TEST_CASE("[Files] Subdir include") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_subdir.sfz"); REQUIRE(synth.getNumRegions() == 1); - REQUIRE(synth.getRegionView(0)->sample == "dummy_subdir.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy_subdir.wav"); } TEST_CASE("[Files] Subdir include Win") @@ -97,7 +97,7 @@ TEST_CASE("[Files] Subdir include Win") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_subdir_win.sfz"); REQUIRE(synth.getNumRegions() == 1); - REQUIRE(synth.getRegionView(0)->sample == "dummy_subdir.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy_subdir.wav"); } TEST_CASE("[Files] Recursive include (with include guard)") @@ -107,8 +107,8 @@ TEST_CASE("[Files] Recursive include (with include guard)") parser.setRecursiveIncludeGuardEnabled(true); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_recursive.sfz"); REQUIRE(synth.getNumRegions() == 2); - REQUIRE(synth.getRegionView(0)->sample == "dummy_recursive2.wav"); - REQUIRE(synth.getRegionView(1)->sample == "dummy_recursive1.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy_recursive2.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename == "dummy_recursive1.wav"); } TEST_CASE("[Files] Include loops (with include guard)") @@ -118,8 +118,8 @@ TEST_CASE("[Files] Include loops (with include guard)") parser.setRecursiveIncludeGuardEnabled(true); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_loop.sfz"); REQUIRE(synth.getNumRegions() == 2); - REQUIRE(synth.getRegionView(0)->sample == "dummy_loop2.wav"); - REQUIRE(synth.getRegionView(1)->sample == "dummy_loop1.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy_loop2.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename == "dummy_loop1.wav"); } TEST_CASE("[Files] Define test") @@ -205,28 +205,28 @@ TEST_CASE("[Files] Full hierarchy with antislashes") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz"); REQUIRE(synth.getNumRegions() == 8); - REQUIRE(synth.getRegionView(0)->sample == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(1)->sample == "Regions/dummy.1.wav"); - REQUIRE(synth.getRegionView(2)->sample == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(3)->sample == "Regions/dummy.1.wav"); - REQUIRE(synth.getRegionView(4)->sample == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(5)->sample == "Regions/dummy.1.wav"); - REQUIRE(synth.getRegionView(6)->sample == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(7)->sample == "Regions/dummy.1.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename == "Regions/dummy.1.wav"); + REQUIRE(synth.getRegionView(2)->sampleId.filename == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(3)->sampleId.filename == "Regions/dummy.1.wav"); + REQUIRE(synth.getRegionView(4)->sampleId.filename == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(5)->sampleId.filename == "Regions/dummy.1.wav"); + REQUIRE(synth.getRegionView(6)->sampleId.filename == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(7)->sampleId.filename == "Regions/dummy.1.wav"); } { sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy_antislash.sfz"); REQUIRE(synth.getNumRegions() == 8); - REQUIRE(synth.getRegionView(0)->sample == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(1)->sample == "Regions/dummy.1.wav"); - REQUIRE(synth.getRegionView(2)->sample == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(3)->sample == "Regions/dummy.1.wav"); - REQUIRE(synth.getRegionView(4)->sample == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(5)->sample == "Regions/dummy.1.wav"); - REQUIRE(synth.getRegionView(6)->sample == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(7)->sample == "Regions/dummy.1.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename == "Regions/dummy.1.wav"); + REQUIRE(synth.getRegionView(2)->sampleId.filename == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(3)->sampleId.filename == "Regions/dummy.1.wav"); + REQUIRE(synth.getRegionView(4)->sampleId.filename == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(5)->sampleId.filename == "Regions/dummy.1.wav"); + REQUIRE(synth.getRegionView(6)->sampleId.filename == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(7)->sampleId.filename == "Regions/dummy.1.wav"); } } @@ -245,10 +245,10 @@ TEST_CASE("[Files] Pizz basic") REQUIRE(synth.getRegionView(1)->randRange == sfz::Range(0.25, 0.5)); REQUIRE(synth.getRegionView(2)->randRange == sfz::Range(0.5, 0.75)); REQUIRE(synth.getRegionView(3)->randRange == sfz::Range(0.75, 1.0)); - REQUIRE(synth.getRegionView(0)->sample == R"(../Samples/pizz/a0_vl4_rr1.wav)"); - REQUIRE(synth.getRegionView(1)->sample == R"(../Samples/pizz/a0_vl4_rr2.wav)"); - REQUIRE(synth.getRegionView(2)->sample == R"(../Samples/pizz/a0_vl4_rr3.wav)"); - REQUIRE(synth.getRegionView(3)->sample == R"(../Samples/pizz/a0_vl4_rr4.wav)"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == R"(../Samples/pizz/a0_vl4_rr1.wav)"); + REQUIRE(synth.getRegionView(1)->sampleId.filename == R"(../Samples/pizz/a0_vl4_rr2.wav)"); + REQUIRE(synth.getRegionView(2)->sampleId.filename == R"(../Samples/pizz/a0_vl4_rr3.wav)"); + REQUIRE(synth.getRegionView(3)->sampleId.filename == R"(../Samples/pizz/a0_vl4_rr4.wav)"); } TEST_CASE("[Files] Channels (channels.sfz)") @@ -256,9 +256,9 @@ TEST_CASE("[Files] Channels (channels.sfz)") sfz::Synth synth; 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)->sampleId.filename == "mono_sample.wav"); REQUIRE(!synth.getRegionView(0)->isStereo()); - REQUIRE(synth.getRegionView(1)->sample == "stereo_sample.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename == "stereo_sample.wav"); REQUIRE(synth.getRegionView(1)->isStereo()); } @@ -268,32 +268,32 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)") synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels_multi.sfz"); REQUIRE(synth.getNumRegions() == 6); - REQUIRE(synth.getRegionView(0)->sample == "*sine"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == "*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)->sampleId.filename == "*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)->sampleId.filename == "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)->sampleId.filename == "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)->sampleId.filename == "*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)->sampleId.filename == "*sine"); REQUIRE(!synth.getRegionView(5)->isStereo()); REQUIRE(synth.getRegionView(5)->isGenerator()); REQUIRE(!synth.getRegionView(5)->oscillator); @@ -359,7 +359,7 @@ TEST_CASE("[Files] Specific bug: relative path with backslashes") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/win_backslashes.sfz"); REQUIRE(synth.getNumRegions() == 1); - REQUIRE(synth.getRegionView(0)->sample == R"(Xylo/Subfolder/closedhat.wav)"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == R"(Xylo/Subfolder/closedhat.wav)"); } TEST_CASE("[Files] Default path") @@ -367,10 +367,10 @@ TEST_CASE("[Files] Default path") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path.sfz"); REQUIRE(synth.getNumRegions() == 4); - REQUIRE(synth.getRegionView(0)->sample == R"(DefaultPath/SubPath1/sample1.wav)"); - REQUIRE(synth.getRegionView(1)->sample == R"(DefaultPath/SubPath2/sample2.wav)"); - REQUIRE(synth.getRegionView(2)->sample == R"(DefaultPath/SubPath1/sample1.wav)"); - REQUIRE(synth.getRegionView(3)->sample == R"(DefaultPath/SubPath2/sample2.wav)"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == R"(DefaultPath/SubPath1/sample1.wav)"); + REQUIRE(synth.getRegionView(1)->sampleId.filename == R"(DefaultPath/SubPath2/sample2.wav)"); + REQUIRE(synth.getRegionView(2)->sampleId.filename == R"(DefaultPath/SubPath1/sample1.wav)"); + REQUIRE(synth.getRegionView(3)->sampleId.filename == R"(DefaultPath/SubPath2/sample2.wav)"); } TEST_CASE("[Files] Default path reset when calling loadSfzFile again") @@ -380,7 +380,7 @@ TEST_CASE("[Files] Default path reset when calling loadSfzFile again") REQUIRE(synth.getNumRegions() == 4); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path_reset.sfz"); REQUIRE(synth.getNumRegions() == 1); - REQUIRE(synth.getRegionView(0)->sample == R"(DefaultPath/SubPath2/sample2.wav)"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == R"(DefaultPath/SubPath2/sample2.wav)"); } TEST_CASE("[Files] Default path is ignored for generators") @@ -388,7 +388,7 @@ TEST_CASE("[Files] Default path is ignored for generators") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path_generator.sfz"); REQUIRE(synth.getNumRegions() == 1); - REQUIRE(synth.getRegionView(0)->sample == R"(*sine)"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == R"(*sine)"); } TEST_CASE("[Files] Set CC applies properly") @@ -547,10 +547,10 @@ TEST_CASE("[Files] Case sentitiveness") sfz::Synth synth; synth.loadSfzFile(sfzFilePath); REQUIRE(synth.getNumRegions() == 4); - REQUIRE(synth.getRegionView(0)->sample == "dummy1.wav"); - REQUIRE(synth.getRegionView(1)->sample == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(2)->sample == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(3)->sample == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy1.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(2)->sampleId.filename == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(3)->sampleId.filename == "Regions/dummy.wav"); } } diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index 12c764bae..7cc2864d6 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -18,9 +18,9 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("sample") { - REQUIRE(region.sample == ""); + REQUIRE(region.sampleId.filename == ""); region.parseOpcode({ "sample", "dummy.wav" }); - REQUIRE(region.sample == "dummy.wav"); + REQUIRE(region.sampleId.filename == "dummy.wav"); } SECTION("direction") From 0474b5b7cee240424814b0845315b1db71ab531c Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 16 Apr 2020 23:25:32 +0200 Subject: [PATCH 084/444] Add a benchmark for chunked reading of a flac file --- benchmarks/BM_readChunkFlac.cpp | 120 ++++++++++++++++++++++++++++++++ benchmarks/CMakeLists.txt | 2 + 2 files changed, 122 insertions(+) create mode 100644 benchmarks/BM_readChunkFlac.cpp diff --git a/benchmarks/BM_readChunkFlac.cpp b/benchmarks/BM_readChunkFlac.cpp new file mode 100644 index 000000000..048ae23df --- /dev/null +++ b/benchmarks/BM_readChunkFlac.cpp @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SIMDHelpers.h" +#include "Buffer.h" +#include +#include +#include "ghc/filesystem.hpp" +#define DR_FLAC_IMPLEMENTATION +#include "dr_flac.h" +#include "AudioBuffer.h" +#include "absl/memory/memory.h" +#ifndef NDEBUG +#include +#endif + +class FileFixture : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State& /* state */) { + rootPath = getPath() / "sample1.flac"; + if (!ghc::filesystem::exists(rootPath)) { + #ifndef NDEBUG + std::cerr << "Can't find path" << '\n'; + #endif + std::terminate(); + } + + sndfile = SndfileHandle(rootPath.c_str()); + numFrames = static_cast(sndfile.frames()); + output = absl::make_unique>(sndfile.channels(), sndfile.frames()); + } + + void TearDown(const ::benchmark::State& /* state */) { + } + + ghc::filesystem::path getPath() + { + #ifdef __linux__ + char buf[PATH_MAX + 1]; + if (readlink("/proc/self/exe", buf, sizeof(buf) - 1) == -1) + return {}; + std::string str { buf }; + return str.substr(0, str.rfind('/')); + #elif _WIN32 + return ghc::filesystem::current_path(); + #endif + } + + std::unique_ptr> output; + SndfileHandle sndfile; + ghc::filesystem::path rootPath; + size_t numFrames; +}; + + +BENCHMARK_DEFINE_F(FileFixture, SndFileOnce)(benchmark::State& state) { + for (auto _ : state) + { + sfz::Buffer buffer { numFrames * sndfile.channels() }; + sndfile.readf(buffer.data(), numFrames); + sfz::readInterleaved(buffer, output->getSpan(0), output->getSpan(1)); + } +} + +BENCHMARK_DEFINE_F(FileFixture, SndFileChunked)(benchmark::State& state) { + for (auto _ : state) + { + auto chunkSize = static_cast(state.range(0)); + size_t framesRead = 0; + sndfile.seek(0, SEEK_SET); + while(framesRead < numFrames) + { + sfz::Buffer buffer { chunkSize * sndfile.channels() }; + auto read = sndfile.readf(buffer.data(), chunkSize); + sfz::readInterleaved( + absl::MakeSpan(buffer).first(read), + output->getSpan(0).subspan(framesRead), + output->getSpan(1).subspan(framesRead) + ); + framesRead += read; + } + } +} + + +BENCHMARK_DEFINE_F(FileFixture, DrWavChunked)(benchmark::State& state) { + auto chunkSize = static_cast(state.range(0)); + auto* flac = drflac_open_file(rootPath.c_str(), nullptr); + if (!flac) { + #ifndef NDEBUG + std::cerr << "Can't find path" << '\n'; + #endif + std::terminate(); + } + sfz::Buffer buffer { chunkSize * flac->channels }; + + for (auto _ : state) + { + size_t framesRead = 0; + drflac_seek_to_pcm_frame(flac, 0); + while(framesRead < numFrames) + { + auto read = drflac_read_pcm_frames_f32(flac, chunkSize, buffer.data()); + sfz::readInterleaved( + absl::MakeSpan(buffer).first(read), + output->getSpan(0).subspan(framesRead), + output->getSpan(1).subspan(framesRead) + ); + framesRead += read; + } + } +} + +BENCHMARK_REGISTER_F(FileFixture, SndFileOnce); +BENCHMARK_REGISTER_F(FileFixture, SndFileChunked)->RangeMultiplier(4)->Range((1 << 10), (1 << 16)); +BENCHMARK_REGISTER_F(FileFixture, DrWavChunked)->RangeMultiplier(4)->Range((1 << 10), (1 << 16)); +BENCHMARK_MAIN(); diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 493f450e3..c56504608 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -83,6 +83,8 @@ target_link_libraries(bm_flacfile PRIVATE sfizz-sndfile) sfizz_add_benchmark(bm_readChunk BM_readChunk.cpp) target_link_libraries(bm_readChunk PRIVATE sfizz-sndfile) +sfizz_add_benchmark(bm_readChunkFlac BM_readChunkFlac.cpp) +target_link_libraries(bm_readChunkFlac PRIVATE sfizz-sndfile) sfizz_add_benchmark(bm_resampleChunk BM_resampleChunk.cpp) target_link_libraries(bm_resampleChunk PRIVATE sfizz-sndfile) From fb45f27bd44fb4eb559890b2353bda9934aacb71 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 18 Apr 2020 04:57:06 +0200 Subject: [PATCH 085/444] Split the installation in multiple components --- clients/CMakeLists.txt | 3 ++- lv2/CMakeLists.txt | 3 ++- src/CMakeLists.txt | 9 ++++++--- vst/CMakeLists.txt | 3 ++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/clients/CMakeLists.txt b/clients/CMakeLists.txt index 6eb78e2a4..eb45daf39 100644 --- a/clients/CMakeLists.txt +++ b/clients/CMakeLists.txt @@ -7,4 +7,5 @@ add_executable (sfizz_jack jack_client.cpp) target_include_directories (sfizz_jack PRIVATE ${JACK_INCLUDE_DIRS}) target_link_libraries (sfizz_jack PRIVATE sfizz::sfizz jack absl::flags_parse ${JACK_LIBRARIES}) sfizz_enable_lto_if_needed (sfizz_jack) -install (TARGETS sfizz_jack DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL) +install (TARGETS sfizz_jack DESTINATION ${CMAKE_INSTALL_BINDIR} + COMPONENT "jack" OPTIONAL) diff --git a/lv2/CMakeLists.txt b/lv2/CMakeLists.txt index aa732cb4b..17e1dcfb9 100644 --- a/lv2/CMakeLists.txt +++ b/lv2/CMakeLists.txt @@ -45,5 +45,6 @@ endif() # Installation if (NOT MSVC) - install (DIRECTORY ${PROJECT_BINARY_DIR} DESTINATION ${LV2PLUGIN_INSTALL_DIR}) + install (DIRECTORY ${PROJECT_BINARY_DIR} DESTINATION ${LV2PLUGIN_INSTALL_DIR} + COMPONENT "lv2") endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1a02ee538..e786dae33 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -67,11 +67,13 @@ if (NOT MSVC) install (TARGETS sfizz_static ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + COMPONENT "development") configure_file (${PROJECT_SOURCE_DIR}/scripts/sfizz.pc.in sfizz.pc @ONLY) install (FILES ${CMAKE_BINARY_DIR}/src/sfizz.pc - DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig + COMPONENT "development") endif() if(WIN32) include(VSTConfig) @@ -101,6 +103,7 @@ if (SFIZZ_SHARED) install (TARGETS sfizz_shared RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT "runtime") endif() endif() diff --git a/vst/CMakeLists.txt b/vst/CMakeLists.txt index 90747045b..7a38c7a25 100644 --- a/vst/CMakeLists.txt +++ b/vst/CMakeLists.txt @@ -106,5 +106,6 @@ endif() # Installation if (NOT MSVC) install (DIRECTORY "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}" - DESTINATION "${VSTPLUGIN_INSTALL_DIR}") + DESTINATION "${VSTPLUGIN_INSTALL_DIR}" + COMPONENT "vst") endif() From 5e6fdb58fa90f280657413055765b5534fa37b26 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 18 Apr 2020 06:30:44 +0200 Subject: [PATCH 086/444] Bundle the dylibs --- .travis.yml | 2 +- .travis/install_osx.sh | 1 + .travis/prepare_osx.sh | 34 ++++++++++++++++++++++++++++++++++ .travis/prepare_tarball.sh | 12 ++++++------ .travis/script_osx.sh | 2 ++ 5 files changed, 44 insertions(+), 7 deletions(-) create mode 100755 .travis/prepare_osx.sh diff --git a/.travis.yml b/.travis.yml index 83722930f..039e194a1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -129,7 +129,7 @@ jobs: - INSTALL_DIR="sfizz-${TRAVIS_BRANCH}-${TRAVIS_OS_NAME}-${TRAVIS_CPU_ARCH}" install: .travis/install_osx.sh script: .travis/script_osx.sh - after_success: .travis/prepare_tarball.sh + after_success: .travis/prepare_osx.sh - name: "MOD devices arm" stage: "Build" diff --git a/.travis/install_osx.sh b/.travis/install_osx.sh index c04bc6954..63e882a80 100755 --- a/.travis/install_osx.sh +++ b/.travis/install_osx.sh @@ -6,3 +6,4 @@ sudo ln -s /usr/local /opt/local brew update brew upgrade cmake brew install jack +brew install dylibbundler diff --git a/.travis/prepare_osx.sh b/.travis/prepare_osx.sh new file mode 100755 index 000000000..955d40e3c --- /dev/null +++ b/.travis/prepare_osx.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +set -ex + +test -z "$CONTAINER" + +if ! [ -z "$CONTAINER" ]; then + . .travis/docker_container.sh +else + . .travis/no_container.sh +fi + +cd build +buildenv make DESTDIR=${PWD}/${INSTALL_DIR} install + +# Bundle LV2 dependencies +cd "${INSTALL_DIR}"/Library/Audio/Plug-Ins/LV2 +dylibbundler -od -b -x sfizz.lv2/sfizz.so -d sfizz.lv2/libs/ -p @loader_path/libs/ +cd "${TRAVIS_BUILD_DIR}/build" + +# Bundle VST3 dependencies +cd "${INSTALL_DIR}"/Library/Audio/Plug-Ins/VST3 +dylibbundler -od -b -x sfizz.vst3/Contents/MacOS/sfizz -d sfizz.vst3/Contents/libs/ -p @loader_path/../libs/ +cd "${TRAVIS_BUILD_DIR}/build" + +# +tar -zcvf "${INSTALL_DIR}.tar.gz" ${INSTALL_DIR} + +# Only release a tarball if there is a tag +if [[ ${TRAVIS_TAG} != "" ]]; then + mv "${INSTALL_DIR}.tar.gz" ${TRAVIS_BUILD_DIR} +fi + +cd "${TRAVIS_BUILD_DIR}" diff --git a/.travis/prepare_tarball.sh b/.travis/prepare_tarball.sh index 001e235f8..6e1114060 100755 --- a/.travis/prepare_tarball.sh +++ b/.travis/prepare_tarball.sh @@ -8,13 +8,13 @@ else . .travis/no_container.sh fi -# Do not prepare a tarball without a tag -if [[ ${TRAVIS_TAG} == "" ]]; then - exit 0 -fi - cd build buildenv make DESTDIR=${PWD}/${INSTALL_DIR} install tar -zcvf "${INSTALL_DIR}.tar.gz" ${INSTALL_DIR} -mv "${INSTALL_DIR}.tar.gz" ${TRAVIS_BUILD_DIR} + +# Only release a tarball if there is a tag +if [[ ${TRAVIS_TAG} != "" ]]; then + mv "${INSTALL_DIR}.tar.gz" ${TRAVIS_BUILD_DIR} +fi + cd .. diff --git a/.travis/script_osx.sh b/.travis/script_osx.sh index 557259a65..98bb0ca92 100755 --- a/.travis/script_osx.sh +++ b/.travis/script_osx.sh @@ -6,6 +6,8 @@ cmake -DCMAKE_BUILD_TYPE=Release \ -DSFIZZ_VST=ON \ -DSFIZZ_TESTS=OFF \ -DCMAKE_CXX_STANDARD=17 \ + -DLV2PLUGIN_INSTALL_DIR=/Library/Audio/Plug-Ins/LV2 \ + -DVSTPLUGIN_INSTALL_DIR=/Library/Audio/Plug-Ins/VST3 \ .. make -j$(sysctl -n hw.ncpu) # Xcode not currently supported, see https://gitlab.kitware.com/cmake/cmake/issues/18088 From 8fcafa7b679a2ac450c2a2ce9cd48079bfcfe380 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 18 Apr 2020 21:26:56 +0200 Subject: [PATCH 087/444] Add some checks to test the midnam file --- tests/FilesT.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index d4ce55686..6a8dac59e 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -580,4 +580,9 @@ TEST_CASE("[Files] Labels") REQUIRE( ccLabels[0].second == "Gain" ); REQUIRE( ccLabels[1].first == 2 ); REQUIRE( ccLabels[1].second == "Other" ); + const std::string xmlMidnam = synth.exportMidnam(); + REQUIRE(xmlMidnam.find("") != xmlMidnam.npos); + REQUIRE(xmlMidnam.find("") != xmlMidnam.npos); + REQUIRE(xmlMidnam.find("") != xmlMidnam.npos); + REQUIRE(xmlMidnam.find("") != xmlMidnam.npos); } From 2de8fe237f46c5e2add69e92bcebcc7f61c76638 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 18 Apr 2020 21:35:38 +0200 Subject: [PATCH 088/444] Add a test for key switch MIDNAM --- tests/FilesT.cpp | 10 ++++++++++ tests/TestFiles/labels_sw.sfz | 4 ++++ 2 files changed, 14 insertions(+) create mode 100644 tests/TestFiles/labels_sw.sfz diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 6a8dac59e..99f0e64bd 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -586,3 +586,13 @@ TEST_CASE("[Files] Labels") REQUIRE(xmlMidnam.find("") != xmlMidnam.npos); REQUIRE(xmlMidnam.find("") != xmlMidnam.npos); } + +TEST_CASE("[Files] Switch labels") +{ + sfz::Synth synth; + synth.loadSfzFile(fs::current_path() / "tests/TestFiles/labels_sw.sfz"); + const std::string xmlMidnam = synth.exportMidnam(); + REQUIRE(xmlMidnam.find("") != xmlMidnam.npos); + REQUIRE(xmlMidnam.find("") != xmlMidnam.npos); + REQUIRE(xmlMidnam.find("") != xmlMidnam.npos); +} diff --git a/tests/TestFiles/labels_sw.sfz b/tests/TestFiles/labels_sw.sfz new file mode 100644 index 000000000..5c1721f95 --- /dev/null +++ b/tests/TestFiles/labels_sw.sfz @@ -0,0 +1,4 @@ + sw_lokey=36 sw_hikey=40 sw_default=36 + sw_last=36 sw_label=Sine lokey=41 sample=*sine + sw_last=38 sw_label=Triangle lokey=41 sample=*triangle + sw_last=40 sw_label=Saw lokey=41 sample=*saw From e8562cc00334520e8e89c6c06dd6c50501379cde Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 19 Apr 2020 01:04:02 +0200 Subject: [PATCH 089/444] Prevent FileId allocating memory from Voice init --- src/sfizz/FileId.cpp | 10 ++-- src/sfizz/FileId.h | 50 ++++++++++++++--- src/sfizz/FilePool.cpp | 37 ++++++++----- src/sfizz/FilePool.h | 9 ++++ src/sfizz/Region.cpp | 9 ++-- src/sfizz/Region.h | 2 +- src/sfizz/Synth.cpp | 6 +-- src/sfizz/Voice.cpp | 6 +-- src/sfizz/Wavetables.cpp | 2 +- tests/FilesT.cpp | 112 +++++++++++++++++++-------------------- tests/RegionT.cpp | 10 ++-- 11 files changed, 157 insertions(+), 96 deletions(-) diff --git a/src/sfizz/FileId.cpp b/src/sfizz/FileId.cpp index 8355ab708..13dca994f 100644 --- a/src/sfizz/FileId.cpp +++ b/src/sfizz/FileId.cpp @@ -10,15 +10,17 @@ size_t std::hash::operator()(const sfz::FileId &id) const { - uint64_t h = ::hash(id.filename); - h = ::hash(id.reverse ? "!" : "", h); + uint64_t h = ::hash(id.filename()); + h = ::hash(id.isReverse() ? "!" : "", h); return h; } std::ostream &operator<<(std::ostream &os, const sfz::FileId &fileId) { - os << fileId.filename; - if (fileId.reverse) + os << fileId.filename(); + if (fileId.isReverse()) os << " (reverse)"; return os; } + +const std::string sfz::FileId::emptyFilename; diff --git a/src/sfizz/FileId.h b/src/sfizz/FileId.h index 7193bdd06..df97f1fd7 100644 --- a/src/sfizz/FileId.h +++ b/src/sfizz/FileId.h @@ -6,6 +6,7 @@ #pragma once #include +#include #include namespace sfz { @@ -14,9 +15,6 @@ namespace sfz { * @brief Sample file identifier within a file pool. */ struct FileId { - std::string filename; - bool reverse = false; - /** * @brief Construct a null identifier. */ @@ -30,9 +28,44 @@ struct FileId { * @param filename * @param reverse */ - FileId(std::string filename, bool reverse = false) - : filename(std::move(filename)), reverse(reverse) + explicit FileId(std::string filename, bool reverse = false) + : filenameBuffer(new std::string(std::move(filename))), + reverse(reverse) + { + } + + /** + * @brief Make an identifier which is a clone of the callee, except with the + * reverse flag passed as parameter. + * + * @param reverse + */ + FileId reversed(bool reverse = true) { + FileId id; + id.filenameBuffer = filenameBuffer; + id.reverse = reverse; + return id; + } + + /** + * @brief Get the file name of this identifier. + * + * @return file name + */ + const std::string &filename() const noexcept + { + return filenameBuffer ? *filenameBuffer : emptyFilename; + } + + /** + * @brief Get whether the identified file is reversed. + * + * @return bool + */ + bool isReverse() const noexcept + { + return reverse; } /** @@ -42,7 +75,7 @@ struct FileId { */ bool operator==(const FileId &other) const { - return reverse == other.reverse && filename == other.filename; + return reverse == other.reverse && filename() == other.filename(); } /** @@ -54,6 +87,11 @@ struct FileId { { return !operator==(other); } + +private: + std::shared_ptr filenameBuffer; + bool reverse = false; + static const std::string emptyFilename; }; } diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index f9f38ae7d..b8e9ade49 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -190,9 +190,18 @@ bool sfz::FilePool::checkSample(std::string& filename) const noexcept #endif } +bool sfz::FilePool::checkSampleId(FileId& fileId) const noexcept +{ + std::string filename = fileId.filename(); + bool result = checkSample(filename); + if (result) + fileId = FileId(std::move(filename), fileId.isReverse()); + return result; +} + absl::optional sfz::FilePool::getFileInformation(const FileId& fileId) noexcept { - const fs::path file { rootDirectory / fileId.filename }; + const fs::path file { rootDirectory / fileId.filename() }; if (!fs::exists(file)) return {}; @@ -208,7 +217,7 @@ absl::optional sfz::FilePool::getFileInformation(const Fil returnedValue.sampleRate = static_cast(sndFile.samplerate()); returnedValue.numChannels = sndFile.channels(); - if (!fileId.reverse) { + if (!fileId.isReverse()) { SF_INSTRUMENT instrumentInfo; sndFile.command(SFC_GET_INSTRUMENT, &instrumentInfo, sizeof(instrumentInfo)); if (instrumentInfo.loop_count > 0) { @@ -229,7 +238,7 @@ bool sfz::FilePool::preloadFile(const FileId& fileId, uint32_t maxOffset) noexce if (!fileInformation) return false; - const fs::path file { rootDirectory / fileId.filename }; + const fs::path file { rootDirectory / fileId.filename() }; SndfileHandle sndFile(file.string().c_str()); // FIXME: Large offsets will require large preloading; is this OK in practice? Apparently sforzando does the same @@ -244,12 +253,12 @@ bool sfz::FilePool::preloadFile(const FileId& fileId, uint32_t maxOffset) noexce const auto existingFile = preloadedFiles.find(fileId); if (existingFile != preloadedFiles.end()) { if (framesToLoad > existingFile->second.preloadedData->getNumFrames()) { - preloadedFiles[fileId].preloadedData = readFromFile(sndFile, framesToLoad, oversamplingFactor, fileId.reverse); + preloadedFiles[fileId].preloadedData = readFromFile(sndFile, framesToLoad, oversamplingFactor, fileId.isReverse()); } } else { fileInformation->sampleRate = static_cast(oversamplingFactor) * static_cast(sndFile.samplerate()); FileDataHandle handle { - readFromFile(sndFile, framesToLoad, oversamplingFactor, fileId.reverse), + readFromFile(sndFile, framesToLoad, oversamplingFactor, fileId.isReverse()), *fileInformation }; preloadedFiles.insert_or_assign(fileId, handle); @@ -263,7 +272,7 @@ absl::optional sfz::FilePool::loadFile(const FileId& fileId if (!fileInformation) return {}; - const fs::path file { rootDirectory / fileId.filename }; + const fs::path file { rootDirectory / fileId.filename() }; SndfileHandle sndFile(file.string().c_str()); // FIXME: Large offsets will require large preloading; is this OK in practice? Apparently sforzando does the same @@ -274,7 +283,7 @@ absl::optional sfz::FilePool::loadFile(const FileId& fileId } else { fileInformation->sampleRate = static_cast(oversamplingFactor) * static_cast(sndFile.samplerate()); FileDataHandle handle { - readFromFile(sndFile, frames, oversamplingFactor, fileId.reverse), + readFromFile(sndFile, frames, oversamplingFactor, fileId.isReverse()), *fileInformation }; loadedFiles.insert_or_assign(fileId, handle); @@ -322,9 +331,9 @@ void sfz::FilePool::setPreloadSize(uint32_t preloadSize) noexcept for (auto& preloadedFile : preloadedFiles) { const auto numFrames = preloadedFile.second.preloadedData->getNumFrames() / static_cast(oversamplingFactor); const auto maxOffset = numFrames > this->preloadSize ? static_cast(numFrames) - this->preloadSize : 0; - fs::path file { rootDirectory / preloadedFile.first.filename }; + fs::path file { rootDirectory / preloadedFile.first.filename() }; SndfileHandle sndFile(file.string().c_str()); - preloadedFile.second.preloadedData = readFromFile(sndFile, preloadSize + maxOffset, oversamplingFactor, preloadedFile.first.reverse); + preloadedFile.second.preloadedData = readFromFile(sndFile, preloadSize + maxOffset, oversamplingFactor, preloadedFile.first.isReverse()); } this->preloadSize = preloadSize; } @@ -372,7 +381,7 @@ void sfz::FilePool::loadingThread() noexcept const auto loadStartTime = std::chrono::high_resolution_clock::now(); const auto waitDuration = loadStartTime - promise->creationTime; - const fs::path file { rootDirectory / promise->fileId.filename }; + const fs::path file { rootDirectory / promise->fileId.filename() }; SndfileHandle sndFile(file.string().c_str()); if (sndFile.error() != 0) { DBG("[sfizz] libsndfile errored for " << promise->fileId << " with message " << sndFile.strError()); @@ -380,10 +389,10 @@ void sfz::FilePool::loadingThread() noexcept continue; } const auto frames = static_cast(sndFile.frames()); - streamFromFile(sndFile, frames, oversamplingFactor, promise->fileId.reverse, promise->fileData, &promise->availableFrames); + streamFromFile(sndFile, frames, oversamplingFactor, promise->fileId.isReverse(), promise->fileData, &promise->availableFrames); promise->dataStatus = FilePromise::DataStatus::Ready; const auto loadDuration = std::chrono::high_resolution_clock::now() - loadStartTime; - logger.logFileTime(waitDuration, loadDuration, frames, promise->fileId.filename); + logger.logFileTime(waitDuration, loadDuration, frames, promise->fileId.filename()); threadsLoading--; @@ -451,9 +460,9 @@ void sfz::FilePool::setOversamplingFactor(sfz::Oversampling factor) noexcept for (auto& preloadedFile : preloadedFiles) { const auto numFrames = preloadedFile.second.preloadedData->getNumFrames() / static_cast(this->oversamplingFactor); const uint32_t maxOffset = numFrames > this->preloadSize ? static_cast(numFrames) - this->preloadSize : 0; - fs::path file { rootDirectory / preloadedFile.first.filename }; + fs::path file { rootDirectory / preloadedFile.first.filename() }; SndfileHandle sndFile(file.string().c_str()); - preloadedFile.second.preloadedData = readFromFile(sndFile, preloadSize + maxOffset, factor, preloadedFile.first.reverse); + preloadedFile.second.preloadedData = readFromFile(sndFile, preloadSize + maxOffset, factor, preloadedFile.first.isReverse()); preloadedFile.second.information.sampleRate *= samplerateChange; } diff --git a/src/sfizz/FilePool.h b/src/sfizz/FilePool.h index 631dd6f6c..7f5e15d93 100644 --- a/src/sfizz/FilePool.h +++ b/src/sfizz/FilePool.h @@ -189,6 +189,15 @@ class FilePool { */ bool checkSample(std::string& filename) const noexcept; + /** + * @brief Check that the sample exists. If not, try to find it in a case insensitive way. + * + * @param fileId the sample file identifier; may be updated by the method + * @return true if the sample exists or was updated properly + * @return false if no sample was found even with a case insensitive search + */ + bool checkSampleId(FileId& fileId) const noexcept; + /** * @brief Clear all preloaded files. * diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 0c6d954cf..117a6d263 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -41,14 +41,17 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) if (trimmedSample.empty()) break; + std::string filename; if (trimmedSample[0] == '*') - sampleId.filename = std::string(trimmedSample); + filename = std::string(trimmedSample); else - sampleId.filename = absl::StrCat(defaultPath, absl::StrReplaceAll(trimmedSample, { { "\\", "/" } })); + filename = absl::StrCat(defaultPath, absl::StrReplaceAll(trimmedSample, { { "\\", "/" } })); + + sampleId = FileId(std::move(filename), sampleId.isReverse()); } break; case hash("direction"): - sampleId.reverse = opcode.value == "reverse"; + sampleId = sampleId.reversed(opcode.value == "reverse"); break; case hash("delay"): setValueFromOpcode(opcode, delay, Default::delayRange); diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 0ab2dd825..17180bcac 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -58,7 +58,7 @@ struct Region { * @return true * @return false */ - bool isGenerator() const noexcept { return sampleId.filename.size() > 0 ? sampleId.filename[0] == '*' : false; } + bool isGenerator() const noexcept { return sampleId.filename().size() > 0 ? sampleId.filename()[0] == '*' : false; } /** * @brief Is stereo (has stereo sample or is unison oscillator)? * diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 2783718c8..d16fc87e2 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -350,7 +350,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) auto region = currentRegion->get(); if (!region->oscillator && !region->isGenerator()) { - if (!resources.filePool.checkSample(region->sampleId.filename)) { + if (!resources.filePool.checkSampleId(region->sampleId)) { removeCurrentRegion(); continue; } @@ -391,12 +391,12 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) removeCurrentRegion(); } else if (region->oscillator && !region->isGenerator()) { - if (!resources.filePool.checkSample(region->sampleId.filename)) { + if (!resources.filePool.checkSampleId(region->sampleId)) { removeCurrentRegion(); continue; } - if (!resources.wavePool.createFileWave(resources.filePool, region->sampleId.filename)) { + if (!resources.wavePool.createFileWave(resources.filePool, std::string(region->sampleId.filename()))) { removeCurrentRegion(); continue; } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 68650ef0c..7eea837af 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -40,7 +40,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, if (region->isGenerator()) { const WavetableMulti* wave = nullptr; - switch (hash(region->sampleId.filename)) { + switch (hash(region->sampleId.filename())) { default: case hash("*silence"): break; @@ -64,7 +64,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, } setupOscillatorUnison(); } else if (region->oscillator) { - const WavetableMulti* wave = resources.wavePool.getFileWave(region->sampleId.filename); + const WavetableMulti* wave = resources.wavePool.getFileWave(region->sampleId.filename()); for (WavetableOscillator& osc : waveOscillators) { osc.setWavetable(wave); osc.setPhase(region->getPhase()); @@ -529,7 +529,7 @@ void sfz::Voice::fillWithGenerator(AudioSpan buffer) noexcept const auto leftSpan = buffer.getSpan(0); const auto rightSpan = buffer.getSpan(1); - if (region->sampleId.filename == "*noise") { + if (region->sampleId.filename() == "*noise") { absl::c_generate(leftSpan, [&](){ return noiseDist(Random::randomGenerator); }); absl::c_generate(rightSpan, [&](){ return noiseDist(Random::randomGenerator); }); } else { diff --git a/src/sfizz/Wavetables.cpp b/src/sfizz/Wavetables.cpp index 617d39018..c1a4448b8 100644 --- a/src/sfizz/Wavetables.cpp +++ b/src/sfizz/Wavetables.cpp @@ -365,7 +365,7 @@ bool WavetablePool::createFileWave(FilePool& filePool, const std::string& filena if (_fileWaves.contains(filename)) return true; - auto fileHandle = filePool.loadFile(filename); + auto fileHandle = filePool.loadFile(FileId(filename)); if (!fileHandle) return false; diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index e6c2394e5..2f22dea5e 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -19,7 +19,7 @@ TEST_CASE("[Files] Single region (regions_one.sfz)") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_one.sfz"); REQUIRE(synth.getNumRegions() == 1); - REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); } @@ -28,9 +28,9 @@ TEST_CASE("[Files] Multiple regions (regions_many.sfz)") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_many.sfz"); REQUIRE(synth.getNumRegions() == 3); - REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy.wav"); - REQUIRE(synth.getRegionView(1)->sampleId.filename == "dummy.1.wav"); - REQUIRE(synth.getRegionView(2)->sampleId.filename == "dummy.2.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename() == "dummy.1.wav"); + REQUIRE(synth.getRegionView(2)->sampleId.filename() == "dummy.2.wav"); } TEST_CASE("[Files] Basic opcodes (regions_opcodes.sfz)") @@ -54,8 +54,8 @@ TEST_CASE("[Files] (regions_bad.sfz)") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_bad.sfz"); REQUIRE(synth.getNumRegions() == 2); - REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy.wav"); - REQUIRE(synth.getRegionView(1)->sampleId.filename == "dummy.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename() == "dummy.wav"); } TEST_CASE("[Files] Local include") @@ -63,7 +63,7 @@ TEST_CASE("[Files] Local include") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_local.sfz"); REQUIRE(synth.getNumRegions() == 1); - REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); } TEST_CASE("[Files] Multiple includes") @@ -71,8 +71,8 @@ TEST_CASE("[Files] Multiple includes") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/multiple_includes.sfz"); REQUIRE(synth.getNumRegions() == 2); - REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy.wav"); - REQUIRE(synth.getRegionView(1)->sampleId.filename == "dummy2.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename() == "dummy2.wav"); } TEST_CASE("[Files] Multiple includes with comments") @@ -80,8 +80,8 @@ TEST_CASE("[Files] Multiple includes with comments") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/multiple_includes_with_comments.sfz"); REQUIRE(synth.getNumRegions() == 2); - REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy.wav"); - REQUIRE(synth.getRegionView(1)->sampleId.filename == "dummy2.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename() == "dummy2.wav"); } TEST_CASE("[Files] Subdir include") @@ -89,7 +89,7 @@ TEST_CASE("[Files] Subdir include") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_subdir.sfz"); REQUIRE(synth.getNumRegions() == 1); - REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy_subdir.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy_subdir.wav"); } TEST_CASE("[Files] Subdir include Win") @@ -97,7 +97,7 @@ TEST_CASE("[Files] Subdir include Win") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_subdir_win.sfz"); REQUIRE(synth.getNumRegions() == 1); - REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy_subdir.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy_subdir.wav"); } TEST_CASE("[Files] Recursive include (with include guard)") @@ -107,8 +107,8 @@ TEST_CASE("[Files] Recursive include (with include guard)") parser.setRecursiveIncludeGuardEnabled(true); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_recursive.sfz"); REQUIRE(synth.getNumRegions() == 2); - REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy_recursive2.wav"); - REQUIRE(synth.getRegionView(1)->sampleId.filename == "dummy_recursive1.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy_recursive2.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename() == "dummy_recursive1.wav"); } TEST_CASE("[Files] Include loops (with include guard)") @@ -118,8 +118,8 @@ TEST_CASE("[Files] Include loops (with include guard)") parser.setRecursiveIncludeGuardEnabled(true); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_loop.sfz"); REQUIRE(synth.getNumRegions() == 2); - REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy_loop2.wav"); - REQUIRE(synth.getRegionView(1)->sampleId.filename == "dummy_loop1.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy_loop2.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename() == "dummy_loop1.wav"); } TEST_CASE("[Files] Define test") @@ -205,28 +205,28 @@ TEST_CASE("[Files] Full hierarchy with antislashes") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz"); REQUIRE(synth.getNumRegions() == 8); - REQUIRE(synth.getRegionView(0)->sampleId.filename == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(1)->sampleId.filename == "Regions/dummy.1.wav"); - REQUIRE(synth.getRegionView(2)->sampleId.filename == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(3)->sampleId.filename == "Regions/dummy.1.wav"); - REQUIRE(synth.getRegionView(4)->sampleId.filename == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(5)->sampleId.filename == "Regions/dummy.1.wav"); - REQUIRE(synth.getRegionView(6)->sampleId.filename == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(7)->sampleId.filename == "Regions/dummy.1.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename() == "Regions/dummy.1.wav"); + REQUIRE(synth.getRegionView(2)->sampleId.filename() == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(3)->sampleId.filename() == "Regions/dummy.1.wav"); + REQUIRE(synth.getRegionView(4)->sampleId.filename() == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(5)->sampleId.filename() == "Regions/dummy.1.wav"); + REQUIRE(synth.getRegionView(6)->sampleId.filename() == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(7)->sampleId.filename() == "Regions/dummy.1.wav"); } { sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy_antislash.sfz"); REQUIRE(synth.getNumRegions() == 8); - REQUIRE(synth.getRegionView(0)->sampleId.filename == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(1)->sampleId.filename == "Regions/dummy.1.wav"); - REQUIRE(synth.getRegionView(2)->sampleId.filename == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(3)->sampleId.filename == "Regions/dummy.1.wav"); - REQUIRE(synth.getRegionView(4)->sampleId.filename == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(5)->sampleId.filename == "Regions/dummy.1.wav"); - REQUIRE(synth.getRegionView(6)->sampleId.filename == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(7)->sampleId.filename == "Regions/dummy.1.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename() == "Regions/dummy.1.wav"); + REQUIRE(synth.getRegionView(2)->sampleId.filename() == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(3)->sampleId.filename() == "Regions/dummy.1.wav"); + REQUIRE(synth.getRegionView(4)->sampleId.filename() == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(5)->sampleId.filename() == "Regions/dummy.1.wav"); + REQUIRE(synth.getRegionView(6)->sampleId.filename() == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(7)->sampleId.filename() == "Regions/dummy.1.wav"); } } @@ -245,10 +245,10 @@ TEST_CASE("[Files] Pizz basic") REQUIRE(synth.getRegionView(1)->randRange == sfz::Range(0.25, 0.5)); REQUIRE(synth.getRegionView(2)->randRange == sfz::Range(0.5, 0.75)); REQUIRE(synth.getRegionView(3)->randRange == sfz::Range(0.75, 1.0)); - REQUIRE(synth.getRegionView(0)->sampleId.filename == R"(../Samples/pizz/a0_vl4_rr1.wav)"); - REQUIRE(synth.getRegionView(1)->sampleId.filename == R"(../Samples/pizz/a0_vl4_rr2.wav)"); - REQUIRE(synth.getRegionView(2)->sampleId.filename == R"(../Samples/pizz/a0_vl4_rr3.wav)"); - REQUIRE(synth.getRegionView(3)->sampleId.filename == R"(../Samples/pizz/a0_vl4_rr4.wav)"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(../Samples/pizz/a0_vl4_rr1.wav)"); + REQUIRE(synth.getRegionView(1)->sampleId.filename() == R"(../Samples/pizz/a0_vl4_rr2.wav)"); + REQUIRE(synth.getRegionView(2)->sampleId.filename() == R"(../Samples/pizz/a0_vl4_rr3.wav)"); + REQUIRE(synth.getRegionView(3)->sampleId.filename() == R"(../Samples/pizz/a0_vl4_rr4.wav)"); } TEST_CASE("[Files] Channels (channels.sfz)") @@ -256,9 +256,9 @@ TEST_CASE("[Files] Channels (channels.sfz)") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels.sfz"); REQUIRE(synth.getNumRegions() == 2); - REQUIRE(synth.getRegionView(0)->sampleId.filename == "mono_sample.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == "mono_sample.wav"); REQUIRE(!synth.getRegionView(0)->isStereo()); - REQUIRE(synth.getRegionView(1)->sampleId.filename == "stereo_sample.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename() == "stereo_sample.wav"); REQUIRE(synth.getRegionView(1)->isStereo()); } @@ -268,32 +268,32 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)") synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels_multi.sfz"); REQUIRE(synth.getNumRegions() == 6); - REQUIRE(synth.getRegionView(0)->sampleId.filename == "*sine"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == "*sine"); REQUIRE(!synth.getRegionView(0)->isStereo()); REQUIRE(synth.getRegionView(0)->isGenerator()); REQUIRE(!synth.getRegionView(0)->oscillator); - REQUIRE(synth.getRegionView(1)->sampleId.filename == "*sine"); + REQUIRE(synth.getRegionView(1)->sampleId.filename() == "*sine"); REQUIRE(synth.getRegionView(1)->isStereo()); REQUIRE(synth.getRegionView(1)->isGenerator()); REQUIRE(!synth.getRegionView(1)->oscillator); - REQUIRE(synth.getRegionView(2)->sampleId.filename == "ramp_wave.wav"); + REQUIRE(synth.getRegionView(2)->sampleId.filename() == "ramp_wave.wav"); REQUIRE(!synth.getRegionView(2)->isStereo()); REQUIRE(!synth.getRegionView(2)->isGenerator()); REQUIRE(synth.getRegionView(2)->oscillator); - REQUIRE(synth.getRegionView(3)->sampleId.filename == "ramp_wave.wav"); + REQUIRE(synth.getRegionView(3)->sampleId.filename() == "ramp_wave.wav"); REQUIRE(synth.getRegionView(3)->isStereo()); REQUIRE(!synth.getRegionView(3)->isGenerator()); REQUIRE(synth.getRegionView(3)->oscillator); - REQUIRE(synth.getRegionView(4)->sampleId.filename == "*sine"); + REQUIRE(synth.getRegionView(4)->sampleId.filename() == "*sine"); REQUIRE(!synth.getRegionView(4)->isStereo()); REQUIRE(synth.getRegionView(4)->isGenerator()); REQUIRE(!synth.getRegionView(4)->oscillator); - REQUIRE(synth.getRegionView(5)->sampleId.filename == "*sine"); + REQUIRE(synth.getRegionView(5)->sampleId.filename() == "*sine"); REQUIRE(!synth.getRegionView(5)->isStereo()); REQUIRE(synth.getRegionView(5)->isGenerator()); REQUIRE(!synth.getRegionView(5)->oscillator); @@ -359,7 +359,7 @@ TEST_CASE("[Files] Specific bug: relative path with backslashes") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/win_backslashes.sfz"); REQUIRE(synth.getNumRegions() == 1); - REQUIRE(synth.getRegionView(0)->sampleId.filename == R"(Xylo/Subfolder/closedhat.wav)"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(Xylo/Subfolder/closedhat.wav)"); } TEST_CASE("[Files] Default path") @@ -367,10 +367,10 @@ TEST_CASE("[Files] Default path") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path.sfz"); REQUIRE(synth.getNumRegions() == 4); - REQUIRE(synth.getRegionView(0)->sampleId.filename == R"(DefaultPath/SubPath1/sample1.wav)"); - REQUIRE(synth.getRegionView(1)->sampleId.filename == R"(DefaultPath/SubPath2/sample2.wav)"); - REQUIRE(synth.getRegionView(2)->sampleId.filename == R"(DefaultPath/SubPath1/sample1.wav)"); - REQUIRE(synth.getRegionView(3)->sampleId.filename == R"(DefaultPath/SubPath2/sample2.wav)"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(DefaultPath/SubPath1/sample1.wav)"); + REQUIRE(synth.getRegionView(1)->sampleId.filename() == R"(DefaultPath/SubPath2/sample2.wav)"); + REQUIRE(synth.getRegionView(2)->sampleId.filename() == R"(DefaultPath/SubPath1/sample1.wav)"); + REQUIRE(synth.getRegionView(3)->sampleId.filename() == R"(DefaultPath/SubPath2/sample2.wav)"); } TEST_CASE("[Files] Default path reset when calling loadSfzFile again") @@ -380,7 +380,7 @@ TEST_CASE("[Files] Default path reset when calling loadSfzFile again") REQUIRE(synth.getNumRegions() == 4); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path_reset.sfz"); REQUIRE(synth.getNumRegions() == 1); - REQUIRE(synth.getRegionView(0)->sampleId.filename == R"(DefaultPath/SubPath2/sample2.wav)"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(DefaultPath/SubPath2/sample2.wav)"); } TEST_CASE("[Files] Default path is ignored for generators") @@ -388,7 +388,7 @@ TEST_CASE("[Files] Default path is ignored for generators") sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path_generator.sfz"); REQUIRE(synth.getNumRegions() == 1); - REQUIRE(synth.getRegionView(0)->sampleId.filename == R"(*sine)"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(*sine)"); } TEST_CASE("[Files] Set CC applies properly") @@ -547,10 +547,10 @@ TEST_CASE("[Files] Case sentitiveness") sfz::Synth synth; synth.loadSfzFile(sfzFilePath); REQUIRE(synth.getNumRegions() == 4); - REQUIRE(synth.getRegionView(0)->sampleId.filename == "dummy1.wav"); - REQUIRE(synth.getRegionView(1)->sampleId.filename == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(2)->sampleId.filename == "Regions/dummy.wav"); - REQUIRE(synth.getRegionView(3)->sampleId.filename == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy1.wav"); + REQUIRE(synth.getRegionView(1)->sampleId.filename() == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(2)->sampleId.filename() == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(3)->sampleId.filename() == "Regions/dummy.wav"); } } diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index 141763370..fa73bd51d 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -18,18 +18,18 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("sample") { - REQUIRE(region.sampleId.filename == ""); + REQUIRE(region.sampleId.filename() == ""); region.parseOpcode({ "sample", "dummy.wav" }); - REQUIRE(region.sampleId.filename == "dummy.wav"); + REQUIRE(region.sampleId.filename() == "dummy.wav"); } SECTION("direction") { - REQUIRE(!region.sampleId.reverse); + REQUIRE(!region.sampleId.isReverse()); region.parseOpcode({ "direction", "reverse" }); - REQUIRE(region.sampleId.reverse); + REQUIRE(region.sampleId.isReverse()); region.parseOpcode({ "direction", "forward" }); - REQUIRE(!region.sampleId.reverse); + REQUIRE(!region.sampleId.isReverse()); } SECTION("delay") From 364a2afb3ecd122ca0273eec13e0195e8179602f Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 19 Apr 2020 11:07:07 +0200 Subject: [PATCH 090/444] Clear the keyswitch labels --- src/sfizz/Synth.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index d16fc87e2..0eb79ae38 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -145,6 +145,7 @@ void sfz::Synth::clear() resources.midiState.reset(); ccLabels.clear(); keyLabels.clear(); + keyswitchLabels.clear(); globalOpcodes.clear(); masterOpcodes.clear(); groupOpcodes.clear(); From 43a7df2de34de043f6fee77e4077c3f4f0286575 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 19 Apr 2020 18:05:43 +0200 Subject: [PATCH 091/444] Do some cleanup in the Buffer class --- src/sfizz/AudioBuffer.h | 17 +++- src/sfizz/Buffer.h | 168 +++++++++++++++++++++++----------------- tests/BufferT.cpp | 20 ++--- 3 files changed, 120 insertions(+), 85 deletions(-) diff --git a/src/sfizz/AudioBuffer.h b/src/sfizz/AudioBuffer.h index dcd805887..931402dee 100644 --- a/src/sfizz/AudioBuffer.h +++ b/src/sfizz/AudioBuffer.h @@ -64,12 +64,12 @@ class AudioBuffer { * @return true if the resize worked * @return false otherwise */ - bool resize(size_t newSize) + bool resize(size_t newSize, std::nothrow_t) noexcept { bool returnedOK = true; for (size_t i = 0; i < numChannels; ++i) - returnedOK &= buffers[i]->resize(newSize); + returnedOK &= buffers[i]->resize(newSize, std::nothrow); if (returnedOK) numFrames = newSize; @@ -77,6 +77,19 @@ class AudioBuffer { return returnedOK; } + /** + * @brief Resizes all the underlying buffers to a new size. + * + * @param newSize + */ + void resize(size_t newSize) + { + for (size_t i = 0; i < numChannels; ++i) + buffers[i]->resize(newSize); + + numFrames = newSize; + } + /** * @brief Return an iterator to a specific channel with a non-const type. * diff --git a/src/sfizz/Buffer.h b/src/sfizz/Buffer.h index c48372bcc..a93fb9905 100644 --- a/src/sfizz/Buffer.h +++ b/src/sfizz/Buffer.h @@ -45,8 +45,8 @@ namespace sfz class BufferCounter { public: - BufferCounter() = default; - ~BufferCounter() + BufferCounter() noexcept = default; + ~BufferCounter() noexcept { #ifndef NDEBUG std::cout << "Remaining buffers on destruction: " << numBuffers << '\n'; @@ -54,41 +54,41 @@ class BufferCounter #endif } - void newBuffer(int size) + void newBuffer(int size) noexcept { numBuffers++; bytes.fetch_add(size); } - void bufferResized(int oldSize, int newSize) + void bufferResized(int oldSize, int newSize) noexcept { bytes.fetch_add(newSize); bytes.fetch_sub(oldSize); } - void bufferDeleted(int size) + void bufferDeleted(int size) noexcept { numBuffers--; bytes.fetch_sub(size); } - void bufferDeleted(size_t size) + void bufferDeleted(size_t size) noexcept { bufferDeleted(static_cast(size)); } - void bufferResized(size_t oldSize, size_t newSize) + void bufferResized(size_t oldSize, size_t newSize) noexcept { bufferResized(static_cast(oldSize), static_cast(newSize)); } - void newBuffer(size_t size) + void newBuffer(size_t size) noexcept { newBuffer(static_cast(size)); } - int getNumBuffers() { return numBuffers; } - int getTotalBytes() { return bytes; } + int getNumBuffers() const noexcept { return numBuffers; } + int getTotalBytes() const noexcept { return bytes; } private: std::atomic numBuffers { 0 }; std::atomic bytes { 0 }; @@ -128,9 +128,8 @@ class Buffer { * @brief Construct a new Buffer object that is empty * */ - Buffer() + Buffer() noexcept { - counter().newBuffer(0); } /** @@ -138,9 +137,8 @@ class Buffer { * * @param size */ - Buffer(size_t size) + explicit Buffer(size_t size) { - counter().newBuffer(0); resize(size); } @@ -152,7 +150,7 @@ class Buffer { * @return true if allocation succeeded * @return false otherwise */ - bool resize(size_t newSize) + bool resize(size_t newSize, std::nothrow_t) noexcept { if (newSize == 0) { clear(); @@ -160,19 +158,24 @@ class Buffer { } auto tempSize = newSize + 2 * AlignmentMask; // To ensure that we have leeway at the beginning and at the end - auto* newData = paddedData != nullptr ? std::realloc(paddedData, tempSize * sizeof(value_type)) : std::malloc(tempSize * sizeof(value_type)); + auto* newData = std::realloc(paddedData.get(), tempSize * sizeof(value_type)); if (newData == nullptr) { return false; } - counter().bufferResized(largerSize * sizeof(value_type), tempSize * sizeof(value_type)); + if (largerSize > 0) + _counter.bufferResized(largerSize * sizeof(value_type), tempSize * sizeof(value_type)); + else + _counter.newBuffer(tempSize * sizeof(value_type)); + largerSize = tempSize; alignedSize = newSize; - paddedData = static_cast(newData); + paddedData.release(); // realloc has invalidated the old pointer + paddedData.reset(static_cast(newData)); normalData = static_cast(align(Alignment, alignedSize, newData, tempSize)); normalEnd = normalData + alignedSize; - auto endMisalignment = (alignedSize & TypeAlignmentMask); - if (endMisalignment != 0) + auto endMisalignment = (alignedSize & TypeAlignmentMask); + if (endMisalignment != 0) _alignedEnd = normalEnd + Alignment - endMisalignment; else _alignedEnd = normalEnd; @@ -180,24 +183,32 @@ class Buffer { return true; } + /** + * @brief Resizes the buffer. Given that std::realloc may return either the same pointer + * or a new one, you need to account for both cases in the code if you are using deep pointers. + * + * @param newSize the new buffer size in bytes + */ + void resize(size_t newSize) + { + if (!resize(newSize, std::nothrow)) + throw std::bad_alloc(); + } + /** * @brief Clear the buffers and frees the underlying memory * */ - void clear() + void clear() noexcept { - counter().bufferResized(largerSize * sizeof(value_type), static_cast(0)); - largerSize = 0; - alignedSize = 0; - std::free(paddedData); - normalData = nullptr; - normalEnd = nullptr; - _alignedEnd = nullptr; + if (largerSize > 0) + _counter.bufferDeleted(largerSize * sizeof(value_type)); + _clear(); } - ~Buffer() + ~Buffer() noexcept { - counter().bufferDeleted(largerSize * sizeof(value_type)); - std::free(paddedData); + if (largerSize > 0) + _counter.bufferDeleted(largerSize * sizeof(value_type)); } /** @@ -207,10 +218,8 @@ class Buffer { */ Buffer(const Buffer& other) { - counter().newBuffer(0); - if (resize(other.size())) { - std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type)); - } + resize(other.size()); + std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type)); } /** @@ -218,45 +227,41 @@ class Buffer { * * @param other */ - Buffer(Buffer&& other) = delete; - // { - // if (this != &other) { - // counter().bufferDeleted(largerSize * sizeof(value_type)); - // std::free(paddedData); - // largerSize = std::exchange(other.largerSize, 0); - // alignedSize = std::exchange(other.alignedSize, 0); - // paddedData = std::exchange(other.paddedData, nullptr); - // normalData = std::exchange(other.normalData, nullptr); - // normalEnd = std::exchange(other.normalEnd, nullptr); - // _alignedEnd = std::exchange(other._alignedEnd, nullptr); - // } - // } + Buffer(Buffer&& other) noexcept + : largerSize(other.largerSize), + alignedSize(other.alignedSize), + normalData(other.normalData), + paddedData(std::move(other.paddedData)), + normalEnd(other.normalEnd), + _alignedEnd(other._alignedEnd) + { + other._clear(); + } Buffer& operator=(const Buffer& other) { if (this != &other) { - if (resize(other.size())) - std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type)); + resize(other.size()); + std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type)); + } + return *this; + } + + Buffer& operator=(Buffer&& other) noexcept + { + if (this != &other) { + largerSize = other.largerSize; + alignedSize = other.alignedSize; + normalData = other.normalData; + paddedData = std::move(other.paddedData); + normalEnd = other.normalEnd; + _alignedEnd = other._alignedEnd; + other._clear(); } return *this; } - Buffer& operator=(Buffer&& other) = delete; - // { - // if (this != &other) { - // counter().bufferDeleted(largerSize * sizeof(value_type)); - // std::free(paddedData); - // largerSize = std::exchange(other.largerSize, 0); - // alignedSize = std::exchange(other.alignedSize, 0); - // paddedData = std::exchange(other.paddedData, nullptr); - // normalData = std::exchange(other.normalData, nullptr); - // normalEnd = std::exchange(other.normalEnd, nullptr); - // _alignedEnd = std::exchange(other._alignedEnd, nullptr); - // } - // return *this; - // } - - Type& operator[](size_t idx) { return *(normalData + idx); } + Type& operator[](size_t idx) noexcept { return *(normalData + idx); } constexpr pointer data() const noexcept { return normalData; } constexpr size_type size() const noexcept { return alignedSize; } constexpr bool empty() const noexcept { return alignedSize == 0; } @@ -264,19 +269,27 @@ class Buffer { constexpr iterator end() const noexcept { return normalEnd; } constexpr pointer alignedEnd() const noexcept { return _alignedEnd; } - /** * @brief Return the buffer counter object. * - * TODO: In C++ 17 all of this can be static inline. - * * @return The buffer counter on which you can call various methods. */ - static BufferCounter& counter() + static BufferCounter& counter() noexcept { - static BufferCounter counter; - return counter; + return _counter; } + +private: + void _clear() + { + largerSize = 0; + alignedSize = 0; + paddedData.reset(); + normalData = nullptr; + normalEnd = nullptr; + _alignedEnd = nullptr; + } + private: static constexpr int AlignmentMask { Alignment - 1 }; static constexpr int TypeAlignment { Alignment / sizeof(value_type) }; @@ -295,13 +308,22 @@ class Buffer { return ptr = reinterpret_cast< void * >( aligned ); } + struct deleter { + void operator()(void *p) const noexcept { std::free(p); } + }; + size_type largerSize { 0 }; size_type alignedSize { 0 }; pointer normalData { nullptr }; - pointer paddedData { nullptr }; + std::unique_ptr paddedData; pointer normalEnd { nullptr }; pointer _alignedEnd { nullptr }; + static BufferCounter _counter; + LEAK_DETECTOR(Buffer); }; +template +BufferCounter Buffer::_counter; + } diff --git a/tests/BufferT.cpp b/tests/BufferT.cpp index 04e38724b..f886f4d94 100644 --- a/tests/BufferT.cpp +++ b/tests/BufferT.cpp @@ -70,12 +70,12 @@ TEST_CASE("[Buffer] Resize 10 floats ") std::fill(buffer.begin(), buffer.end(), 1.0f); - REQUIRE(buffer.resize(smallSize)); + REQUIRE(buffer.resize(smallSize, std::nothrow)); checkBoundaries(buffer, smallSize); REQUIRE(std::all_of(buffer.begin(), buffer.end(), [](float value) { return value == 1.0f; })); - REQUIRE(buffer.resize(bigSize)); + REQUIRE(buffer.resize(bigSize, std::nothrow)); checkBoundaries(buffer, bigSize); for (auto i = 0; i < smallSize; ++i) REQUIRE(buffer[i] == 1.0f); @@ -92,12 +92,12 @@ TEST_CASE("[Buffer] Resize 4096 floats ") std::fill(buffer.begin(), buffer.end(), 1.0f); - REQUIRE(buffer.resize(smallSize)); + REQUIRE(buffer.resize(smallSize, std::nothrow)); checkBoundaries(buffer, smallSize); REQUIRE(std::all_of(buffer.begin(), buffer.end(), [](float value) { return value == 1.0f; })); - REQUIRE(buffer.resize(bigSize)); + REQUIRE(buffer.resize(bigSize, std::nothrow)); checkBoundaries(buffer, bigSize); for (auto i = 0; i < smallSize; ++i) REQUIRE(buffer[i] == 1.0f); @@ -114,12 +114,12 @@ TEST_CASE("[Buffer] Resize 65536 floats ") std::fill(buffer.begin(), buffer.end(), 1.0f); - REQUIRE(buffer.resize(smallSize)); + REQUIRE(buffer.resize(smallSize, std::nothrow)); checkBoundaries(buffer, smallSize); REQUIRE(std::all_of(buffer.begin(), buffer.end(), [](float value) { return value == 1.0f; })); - REQUIRE(buffer.resize(bigSize)); + REQUIRE(buffer.resize(bigSize, std::nothrow)); checkBoundaries(buffer, bigSize); for (auto i = 0; i < smallSize; ++i) REQUIRE(buffer[i] == 1.0f); @@ -140,8 +140,8 @@ TEST_CASE("[Buffer] Copy and move") checkBoundaries(copyConstructed, baseSize); REQUIRE(std::all_of(copyConstructed.begin(), copyConstructed.end(), [](float value) { return value == 1.0f; })); - // sfz::Buffer moveConstructed { std::move(buffer) }; - // REQUIRE(buffer.empty()); - // checkBoundaries(moveConstructed, baseSize); - // REQUIRE(std::all_of(moveConstructed.begin(), moveConstructed.end(), [](auto value) { return value == 1.0f; })); + sfz::Buffer moveConstructed { std::move(buffer) }; + REQUIRE(buffer.empty()); + checkBoundaries(moveConstructed, baseSize); + REQUIRE(std::all_of(moveConstructed.begin(), moveConstructed.end(), [](float value) { return value == 1.0f; })); } From f7e80452c4d2a217c2a3758296d3c1b06ac20c68 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 19 Apr 2020 18:31:27 +0200 Subject: [PATCH 092/444] Forgot to count the buffer deletion in move operator= --- src/sfizz/Buffer.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sfizz/Buffer.h b/src/sfizz/Buffer.h index a93fb9905..ea47bb2ba 100644 --- a/src/sfizz/Buffer.h +++ b/src/sfizz/Buffer.h @@ -250,6 +250,8 @@ class Buffer { Buffer& operator=(Buffer&& other) noexcept { if (this != &other) { + if (largerSize > 0) + _counter.bufferDeleted(largerSize * sizeof(value_type)); largerSize = other.largerSize; alignedSize = other.alignedSize; normalData = other.normalData; From 4562201455e33e69bb7f90c290f4f9e623e80471 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 19 Apr 2020 18:48:11 +0200 Subject: [PATCH 093/444] Add some tests for the buffer counter --- src/sfizz/Buffer.h | 8 ++++++++ tests/BufferT.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/sfizz/Buffer.h b/src/sfizz/Buffer.h index ea47bb2ba..5f75a8886 100644 --- a/src/sfizz/Buffer.h +++ b/src/sfizz/Buffer.h @@ -211,6 +211,14 @@ class Buffer { _counter.bufferDeleted(largerSize * sizeof(value_type)); } + /** + * @brief Get the size of the larger block which is actually allocated + */ + size_t allocationSize() const noexcept + { + return largerSize; + } + /** * @brief Construct a new Buffer object from an existing one * diff --git a/tests/BufferT.cpp b/tests/BufferT.cpp index f886f4d94..dfad14830 100644 --- a/tests/BufferT.cpp +++ b/tests/BufferT.cpp @@ -145,3 +145,52 @@ TEST_CASE("[Buffer] Copy and move") checkBoundaries(moveConstructed, baseSize); REQUIRE(std::all_of(moveConstructed.begin(), moveConstructed.end(), [](float value) { return value == 1.0f; })); } + +TEST_CASE("[Buffer] Buffer counter") +{ + sfz::BufferCounter& counter = sfz::Buffer::counter(); + + REQUIRE(counter.getNumBuffers() == 0); + REQUIRE(counter.getTotalBytes() == 0); + + // create an empty buffer + sfz::Buffer b1; + REQUIRE(counter.getNumBuffers() == 0); + REQUIRE(counter.getTotalBytes() == 0); + + // clear an empty buffer + b1.clear(); + REQUIRE(counter.getNumBuffers() == 0); + REQUIRE(counter.getTotalBytes() == 0); + + // create a sized buffer + sfz::Buffer b2(5); + REQUIRE(counter.getNumBuffers() == 1); + REQUIRE(counter.getTotalBytes() == (b2.allocationSize()) * sizeof(float)); + + // resize an empty buffer + b1.resize(3); + REQUIRE(counter.getNumBuffers() == 2); + REQUIRE(counter.getTotalBytes() == (b1.allocationSize() + b2.allocationSize()) * sizeof(float)); + + // resize a non-empty buffer + b1.resize(7); + REQUIRE(counter.getNumBuffers() == 2); + REQUIRE(counter.getTotalBytes() == (b1.allocationSize() + b2.allocationSize()) * sizeof(float)); + + // clear a non-empty buffer + b2.clear(); + REQUIRE(counter.getNumBuffers() == 1); + REQUIRE(counter.getTotalBytes() == (b1.allocationSize()) * sizeof(float)); + + // move an empty buffer into a non-empty one + b1 = std::move(b2); + REQUIRE(counter.getNumBuffers() == 0); + REQUIRE(counter.getTotalBytes() == 0); + + // move a non-empty buffer into an empty one + b1.resize(3); + b2 = std::move(b1); + REQUIRE(counter.getNumBuffers() == 1); + REQUIRE(counter.getTotalBytes() == (b2.allocationSize()) * sizeof(float)); +} From 6ac903ac8fac3c89f92504205201f29993090c3b Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 19 Apr 2020 19:15:06 +0200 Subject: [PATCH 094/444] Make the counter really singleton, remove non-working static init --- src/sfizz/Buffer.h | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/sfizz/Buffer.h b/src/sfizz/Buffer.h index 5f75a8886..89acfa362 100644 --- a/src/sfizz/Buffer.h +++ b/src/sfizz/Buffer.h @@ -45,6 +45,16 @@ namespace sfz class BufferCounter { public: + /** + * @brief Return the buffer counter object. + */ + static BufferCounter& counter() noexcept + { + static BufferCounter counter; + return counter; + } + +protected: BufferCounter() noexcept = default; ~BufferCounter() noexcept { @@ -54,6 +64,7 @@ class BufferCounter #endif } +public: void newBuffer(int size) noexcept { numBuffers++; @@ -164,9 +175,9 @@ class Buffer { } if (largerSize > 0) - _counter.bufferResized(largerSize * sizeof(value_type), tempSize * sizeof(value_type)); + counter().bufferResized(largerSize * sizeof(value_type), tempSize * sizeof(value_type)); else - _counter.newBuffer(tempSize * sizeof(value_type)); + counter().newBuffer(tempSize * sizeof(value_type)); largerSize = tempSize; alignedSize = newSize; @@ -202,13 +213,13 @@ class Buffer { void clear() noexcept { if (largerSize > 0) - _counter.bufferDeleted(largerSize * sizeof(value_type)); + counter().bufferDeleted(largerSize * sizeof(value_type)); _clear(); } ~Buffer() noexcept { if (largerSize > 0) - _counter.bufferDeleted(largerSize * sizeof(value_type)); + counter().bufferDeleted(largerSize * sizeof(value_type)); } /** @@ -259,7 +270,7 @@ class Buffer { { if (this != &other) { if (largerSize > 0) - _counter.bufferDeleted(largerSize * sizeof(value_type)); + counter().bufferDeleted(largerSize * sizeof(value_type)); largerSize = other.largerSize; alignedSize = other.alignedSize; normalData = other.normalData; @@ -286,7 +297,7 @@ class Buffer { */ static BufferCounter& counter() noexcept { - return _counter; + return BufferCounter::counter(); } private: @@ -328,12 +339,7 @@ class Buffer { std::unique_ptr paddedData; pointer normalEnd { nullptr }; pointer _alignedEnd { nullptr }; - static BufferCounter _counter; - LEAK_DETECTOR(Buffer); }; -template -BufferCounter Buffer::_counter; - } From 99bce82ba4950d61467957f7ec8a2cc2e19fded8 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 19 Apr 2020 19:31:52 +0200 Subject: [PATCH 095/444] Fix the test --- tests/BufferT.cpp | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/tests/BufferT.cpp b/tests/BufferT.cpp index dfad14830..8d43291d4 100644 --- a/tests/BufferT.cpp +++ b/tests/BufferT.cpp @@ -150,47 +150,54 @@ TEST_CASE("[Buffer] Buffer counter") { sfz::BufferCounter& counter = sfz::Buffer::counter(); - REQUIRE(counter.getNumBuffers() == 0); - REQUIRE(counter.getTotalBytes() == 0); + // handle the eventuality that the buffer counter does not start at zero + const int initialNumBuffers = counter.getNumBuffers(); + const int initialTotalBytes = counter.getTotalBytes(); + auto haveNumBuffers = [&](int n) -> bool { + return n == counter.getNumBuffers() - initialNumBuffers; + }; + auto haveTotalAllocation = [&](int n) -> bool { + return n * static_cast(sizeof(float)) == counter.getTotalBytes() - initialTotalBytes; + }; // create an empty buffer sfz::Buffer b1; - REQUIRE(counter.getNumBuffers() == 0); - REQUIRE(counter.getTotalBytes() == 0); + REQUIRE(haveNumBuffers(0)); + REQUIRE(haveTotalAllocation(0)); // clear an empty buffer b1.clear(); - REQUIRE(counter.getNumBuffers() == 0); - REQUIRE(counter.getTotalBytes() == 0); + REQUIRE(haveNumBuffers(0)); + REQUIRE(haveTotalAllocation(0)); // create a sized buffer sfz::Buffer b2(5); - REQUIRE(counter.getNumBuffers() == 1); - REQUIRE(counter.getTotalBytes() == (b2.allocationSize()) * sizeof(float)); + REQUIRE(haveNumBuffers(1)); + REQUIRE(haveTotalAllocation(b2.allocationSize())); // resize an empty buffer b1.resize(3); - REQUIRE(counter.getNumBuffers() == 2); - REQUIRE(counter.getTotalBytes() == (b1.allocationSize() + b2.allocationSize()) * sizeof(float)); + REQUIRE(haveNumBuffers(2)); + REQUIRE(haveTotalAllocation(b1.allocationSize() + b2.allocationSize())); // resize a non-empty buffer b1.resize(7); - REQUIRE(counter.getNumBuffers() == 2); - REQUIRE(counter.getTotalBytes() == (b1.allocationSize() + b2.allocationSize()) * sizeof(float)); + REQUIRE(haveNumBuffers(2)); + REQUIRE(haveTotalAllocation(b1.allocationSize() + b2.allocationSize())); // clear a non-empty buffer b2.clear(); - REQUIRE(counter.getNumBuffers() == 1); - REQUIRE(counter.getTotalBytes() == (b1.allocationSize()) * sizeof(float)); + REQUIRE(haveNumBuffers(1)); + REQUIRE(haveTotalAllocation(b1.allocationSize())); // move an empty buffer into a non-empty one b1 = std::move(b2); - REQUIRE(counter.getNumBuffers() == 0); - REQUIRE(counter.getTotalBytes() == 0); + REQUIRE(haveNumBuffers(0)); + REQUIRE(haveTotalAllocation(0)); // move a non-empty buffer into an empty one b1.resize(3); b2 = std::move(b1); - REQUIRE(counter.getNumBuffers() == 1); - REQUIRE(counter.getTotalBytes() == (b2.allocationSize()) * sizeof(float)); + REQUIRE(haveNumBuffers(1)); + REQUIRE(haveTotalAllocation(b2.allocationSize())); } From 748e3668a8baee4c1b20c8e65b719f8bb886cf4f Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 20 Apr 2020 19:47:28 +0200 Subject: [PATCH 096/444] Support block comments --- src/sfizz/parser/Parser.cpp | 69 ++++++++++++++++++++++++++++++------- src/sfizz/parser/Parser.h | 4 +-- tests/DemoParser.cpp | 11 ++++++ 3 files changed, 70 insertions(+), 14 deletions(-) diff --git a/src/sfizz/parser/Parser.cpp b/src/sfizz/parser/Parser.cpp index eb3c496d7..800d0379f 100644 --- a/src/sfizz/parser/Parser.cpp +++ b/src/sfizz/parser/Parser.cpp @@ -113,7 +113,7 @@ void Parser::processTopLevel() while (!_included.empty()) { Reader& reader = *_included.back(); - while (reader.skipChars(" \t\r\n") || skipComment(reader)); + while (reader.skipChars(" \t\r\n") || skipComment()); switch (reader.peekChar()) { case Reader::kEof: @@ -348,32 +348,70 @@ void Parser::flushCurrentHeader() _currentOpcodes.clear(); } -bool Parser::hasComment(Reader& reader) +int Parser::hasComment(Reader& reader) { if (reader.peekChar() != '/') return false; reader.getChar(); - if (reader.peekChar() != '/') { - reader.putBackChar('/'); - return false; + + int ret = 0; + + switch (reader.peekChar()) { + case '/': + ret = 1; + break; + case '*': + ret = 2; + break; } - return true; + reader.putBackChar('/'); + return ret; } -size_t Parser::skipComment(Reader& reader) +size_t Parser::skipComment() { - if (!hasComment(reader)) + Reader& reader = *_included.back(); + + int commentType = hasComment(reader); + if (!commentType) return 0; + SourceLocation start = reader.location(); + size_t count = 2; reader.getChar(); reader.getChar(); - int c; - while ((c = reader.getChar()) != Reader::kEof && c != '\r' && c != '\n') - ++count; + bool terminated = false; + + switch (commentType) { + case 1: // line comment + { + int c; + while ((c = reader.getChar()) != Reader::kEof && c != '\r' && c != '\n') + ++count; + terminated = true; + } + break; + case 2: // block comment + { + int c1 = 0; + int c2 = reader.getChar(); + while (!terminated && c2 != Reader::kEof) { + c1 = c2; + c2 = reader.getChar(); + terminated = c1 == '*' && c2 == '/'; + } + } + break; + } + + if (!terminated) { + SourceLocation end = reader.location(); + emitError({ start, end }, "Unterminated block comment."); + } return count; } @@ -387,7 +425,14 @@ void Parser::trimRight(std::string& text) size_t Parser::extractToEol(Reader& reader, std::string* dst) { return reader.extractWhile(dst, [&reader](char c) { - return c != '\r' && c != '\n' && !(c == '/' && reader.peekChar() == '/'); + if (c == '\r' || c == '\n') + return false; + if (c == '/') { + int c2 = reader.peekChar(); + if (c2 == '/' || c2 == '*') // stop at comment + return false; + } + return true; }); } diff --git a/src/sfizz/parser/Parser.h b/src/sfizz/parser/Parser.h index 4f6a071d6..88424d1d9 100644 --- a/src/sfizz/parser/Parser.h +++ b/src/sfizz/parser/Parser.h @@ -84,8 +84,8 @@ class Parser { void flushCurrentHeader(); // helpers - static bool hasComment(Reader& reader); - static size_t skipComment(Reader& reader); + static int hasComment(Reader& reader); // 0=None 1=Line 2=Block + size_t skipComment(); static void trimRight(std::string& text); static size_t extractToEol(Reader& reader, std::string* dst); // ignores comment std::string expandDollarVars(const SourceRange& range, absl::string_view src); diff --git a/tests/DemoParser.cpp b/tests/DemoParser.cpp index f7931694e..424dcceea 100644 --- a/tests/DemoParser.cpp +++ b/tests/DemoParser.cpp @@ -38,6 +38,11 @@ static const char defaultSfzText[] = R"SFZ( // This is a SFZ test file with many problems. // //----------------------------------------------------------------------------// +/* + * This is a block comment. Not all the SFZ players accept it. + * It can span over multiple lines. +*/ + // opcode without header not_in_header=on // warning @@ -75,6 +80,12 @@ abcdef=$tata // opcode name which expands to invalid identifier $titi=1 + +volume=10 /* +block comments at the end of line +*/ + +/* unterminated block comment )SFZ"; void Application::init() From ea425f380c5d1f45fd6d6df1bbe7cc6276542e9b Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 20 Apr 2020 20:12:38 +0200 Subject: [PATCH 097/444] Add some parsing tests --- tests/ParsingT.cpp | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tests/ParsingT.cpp b/tests/ParsingT.cpp index 7a51263c5..840315863 100644 --- a/tests/ParsingT.cpp +++ b/tests/ParsingT.cpp @@ -448,3 +448,78 @@ param2=$bar)"); REQUIRE(mock.fullBlockHeaders == expectedHeaders); REQUIRE(mock.fullBlockMembers == expectedMembers); } + +TEST_CASE("[Parsing] Block comments") +{ + sfz::Parser parser; + ParsingMocker mock; + parser.setListener(&mock); + parser.parseString("/blockComments.sfz", +R"(/* A block comment (1) */ +/* +A block comment (2) */ +/* A block comment (3) +*/ +/* A block comment + (4) */ +/* A block comment /* // ** (5) */ +)"); + REQUIRE(mock.beginnings == 1); + REQUIRE(mock.endings == 1); + REQUIRE(mock.errors.empty()); + REQUIRE(mock.warnings.empty()); + REQUIRE(mock.opcodes.empty()); + REQUIRE(mock.headers.empty()); + REQUIRE(mock.fullBlockHeaders.empty()); + REQUIRE(mock.fullBlockMembers.empty()); +} + +TEST_CASE("[Parsing] Unterminated block comments") +{ + sfz::Parser parser; + ParsingMocker mock; + parser.setListener(&mock); + parser.parseString("/unterminatedBlockComment.sfz", +R"(/* Unterminated block comment +)"); + REQUIRE(mock.beginnings == 1); + REQUIRE(mock.endings == 1); + REQUIRE(mock.errors.size() == 1); + REQUIRE(mock.warnings.empty()); + REQUIRE(mock.opcodes.empty()); + REQUIRE(mock.headers.empty()); + REQUIRE(mock.fullBlockHeaders.empty()); + REQUIRE(mock.fullBlockMembers.empty()); +} + +TEST_CASE("[Parsing] Comments after values") +{ + sfz::Parser parser; + ParsingMocker mock; + parser.setListener(&mock); + parser.parseString("/commentsAfterValues.sfz", +R"(
+param1=foo param2=bar // line comment +param3=baz param4=quux /* block comment */)"); + std::vector> expectedMembers = { + {{"param1", "foo"}, {"param2", "bar"}, + {"param3", "baz"}, {"param4", "quux"}} + }; + std::vector expectedHeaders = { + "header" + }; + std::vector expectedOpcodes; + + for (auto& members: expectedMembers) + for (auto& opcode: members) + expectedOpcodes.push_back(opcode); + + REQUIRE(mock.beginnings == 1); + REQUIRE(mock.endings == 1); + REQUIRE(mock.errors.empty()); + REQUIRE(mock.warnings.empty()); + REQUIRE(mock.opcodes == expectedOpcodes); + REQUIRE(mock.headers == expectedHeaders); + REQUIRE(mock.fullBlockHeaders == expectedHeaders); + REQUIRE(mock.fullBlockMembers == expectedMembers); +} From 36f65d3afb0f77dfc2364a98f0e1d5bdcedf4939 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Mon, 20 Apr 2020 20:17:12 +0200 Subject: [PATCH 098/444] Add a contribution guide --- CONTRIBUTING.md | 223 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..7dff4adc7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,223 @@ +# Contributing to sfizz + +Thanks for considering contributing! +There is actually many things anyone can do, programming-related or music-related. + +## Using the software for your projects + +One of the best and probably simplest thing to do is simply to use sfizz in a number of situations. +There is only so much we can test as far as sfz libraries go, or differents hosts for the plugin versions. +If during the course of your usage you find out that sfizz does not behave as it should with respect to what the SFZ spec dictates, or what other players do, please file in a bug reports. +If there is some feature that would be fantastic for you, feel free to post a feature requests in the issues. +This will help us track and remember everything. +Refer to (#filing-bugs-and-feature-requests) for more precision. + +### Filing bugs and feature requests + +When filing a bug, try to be as precise as possible. +Obviously bugs that are easily reproducible are easier to solve. + +- If sfizz fails to play a library correctly, please give us a link to the library and ideally an example of what you except. It helps **tremendously** if you can provide a *minimal* example in the form of a simple custom sfz library that pinpoints the problem exactly, along with instruction or midi files to render. +- If the plugins crashes, please try to reproduce and detail everything you did up to the crash, as well as the host and sfz library. It can be really helpful to provide session files, again with a minimal example. +- If the behavior of sfizz is clumsy and annoying, and the user experience feels subpar, please tell us and try to propose some solutions on how it could be improved. As developers, we are viewing and using sfizz in specific ways which may actually be quite far from what users would like, but we're all ears :) + +For feature requests, provide details about the expected behavior and possibly example sfz files that will benefit from the new feature. +Note that we aim at implementing the sfz spec as a whole, and possibly some extensions by e.g. ARIA so normally all sfz features should come at some point. + +### Using the C or C++ library + +Sfizz is liberally licensed and you can freely use it in any project under the term of its license and its dependencies. +We provide a somewhat simple C and C++ API that covers our use cases within plugins mostly, and should be useful. +The API is documented over at the [api] page. +You can find example of using the C API in the `lv2` directory which contains sfizz's LV2 plugin. +You can find example of using the C++ API in the `vst3` directory which contains sfizz's VST3 plugin. +In both cases sfizz is built in the plugins as a static library. +The project [sfizz-render] illustrates how one might use sfizz as a shared library through a `make`- and `pkg-config`-based process. +Other projects that bundle or use sfizz are: + +- (https://github.com/sfztools/beatbox): a live drum machine +- + +If you use the library, maybe you will have specific needs and requests that could be useful to you. +Be sure to post an issue for this so we can improve the API, especially because we have to queue API changes so it stays a bit stable over time. + +## Developing for sfizz + +We welcome all code contributions to sfizz, whatever your coding level. +If there is a specific feature you would like, and you are willing to put in the time (and have some form of initial background in modern C++ code), we can mentor you and provide breadcrumbs through the codebase. +Sfizz is still very much alpha, and we're refactoring large parts of the code on a regular basis. +We try to keep the public API documented, and most of the internal APIs are also documented, but this can be in flux from time to time. +If you want to add things we encourage you to ask around through an issue, on Discord, IRC, or by email, so that we may be able to guide you and discuss about how and what to implement. + +### Handling the repository and its submodules + +Sfizz uses a number of submodules, namely abseil and the VST sdk. +These submodules are purely consumed by sfizz, and the maintainers will manage updating them when needed. +As such, they should *not* pose too much issues. +They are all pinned to specific branches or commits in their original repository. + +To clone the sfizz repository, the recommended way is to do +```bash +git clone https://github.com/sfztools/sfizz.git --recursive +# Other possibility +git clone https://github.com/sfztools/sfizz.git +cd sfizz +git submodule update --init +``` + +If after some time you happen to see something like this: +``` +❯ git status +On branch develop +Your branch is up to date with 'origin/develop'. + +Changes not staged for commit: + (use "git add ..." to update what will be committed) + (use "git checkout -- ..." to discard changes in working directory) + + modified: external/abseil-cpp (new commits) + +no changes added to commit (use "git add" and/or "git commit -a") +``` +Then it means that the abseil submodule *on your disk* differs from the one expected by sfizz. +This usually happens if i) you modified one of abseil's files, in which case the repository will be dirty, or ii) sfizz updated abseil's and pinned it to another commit. +Assuming you were not adding relevant changes to abseil in order to push them upstream, you can usually resolve this issue through +```bash +git submodule update +``` +**All changes in the submodule will be lost**. +If you were in fact modifying abseil for some reason, then consider either pushing your changes upstream or find an alternative. +Same goes for the VST SDK. + +### Building sfizz from source + +For details on the dependencies and platform-specific information for building sfizz, please refer to the [build] page. +Note that for development you will probably want all dependencies, which means using debian's lingo: + +- `cmake` +- `g++` or `clang++` +- `clang-format` +- `libjack-jackd2-dev` +- `libsndfile-dev` + +We also recommend building [benchmark] from source and installing it as a static library. +You will also want to configure using the `-DSFIZZ_TESTS=ON` flag and `-DSFIZZ_BENCHMARKS=ON` flag to be able to run the tests and possibly the micro-benchmarks. + +### Forking and creating pull requests + +We do development on the git `develop` branch, and distribute using the `master` branch. +We mostly avoid directly committing to the `develop` branch and go through pull requests and issues for development-related discussion and choices. +Try to have public and private discussion about development, and if such discussion happen on IRC or Discord, try to sum them up in a pull request or an issue that we can then refer to. +Obviously, light technical questions can be asked freely in private, but the goal is to open the discussion for everyone to see. + +To start contributing, we encourage you to fork the main sfizz repository on your personal account and clone it locally on your machine +In the clone of your fork, and add the `sfztools` repository as a remote as follows +```bash +# Clone your own fork in a specific directory +git clone https://github.com/MY_GITHUB_NAME/sfizz.git sfizz-fork +cd sfizz-fork +# Add the upstream sfztools remote +git remote add upstream https://github.com/sfztools/sfizz.git +``` +Once done, the contribution process is as follows: + +1. Fetch the latest changes from the `upstream` remote, in particular the `develop` branch. +2. Checkout the `upstream/develop` branch, and immediately checkout to a new local branch to start adding changes +3. Add commits to your local branch +4. Push the local branch to your own fork +5. Go back to Github web interface and create a pull request + +In command language, this translates to +```bash +# Fetch the latest status from upstrea +git fetch upstream +# Checkout the upstream develop branch +git checkout upstream/develop +# Create a new local branch +git checkout -b my-new-contribution +# Do things as usual for git: git add, git commit +# ... +# Once ready, push the branch to your fork. Github will tell you where to go for a pull request from there. +git push --set-upstream origin my-new-contribution +``` + +### Adding files to the build system + +We use CMake for the build system. +For the main sfizz library, the main file is `src/CMakeLists.txt`. +In particular if you add a new `.cpp` file to sfizz, you should add it to the `SFIZZ_SOURCES` list in this file so it gets compiled and linked with the rest of the library. + +### Coding style and guidelines + +We have continuous integration targets down to gcc 4.9, so the code has to be fully C++11 compliant. +Sadly this means some bonuses from C++14 are not available, although `absl` compensates many things from a standard library point of view. +All modern C++ guidelines apply, as well as rules related to audio programming: + +- Raw pointers are non-owning observers; use `unique_ptr` and `shared_ptr` for owning references, and `absl::optional` for nullable/optional/maybe values returned from functions. +- Use move semantics when appropriate. +- Try to use [RAII] wrappers and write them if necessary. +- Avoid allocating memory, calling functions that throw, using system calls, or any launching any blocking operation in the audio thread; for sfizz, that means anything that is called at some point from `Synth::renderBlock` and `Synth::noteOn`, `noteOff`, ... and all midi events. +- We have a helper class that helps us to track allocation in the form of the `sfz::Buffer` class and the `sfz::AudioBuffer` class, so use it for heap memory in lieu of naked `new` or `unique_ptr` when relevant. +- Most things in the [CppCoreGuidelines] are good to take, although be careful as many proposed things may break the *no allocation* rule. + +We also have a `clang-tidy` CI step that may annoy you from time to time. + +We have a `clang-format` coding style in that is loosely based on the WebKit style. +We encourage you to use it, as well as `git-clang-format` to format your PR. +If possible, find a plugin for your text editor than handles `.editorconfig` files. Most editors have one. +Some notes : + +- Class and type names start by a capital letter and follow the `PascalCase`, function names and variables in the C++ part follow the `camelCase`, and function names and variables in the C part follows a `snake_case` to blend with the surroundings. +- Try to keep names meaningful, and code clear if possible. +- Comment your classes APIs, in a terse and clear way. Comments inside the code are usually not needed *if the code is written clearly with meaningful names*, but if you feel that it's needed please do. +- You can use `_` in front of private variables, but we do not have a consistent naming for these yet. + +All these rules are subject to change upon discussion and integration. + +### Testing your changes + +To build and run the tests, you need to have configured sfizz with the `-DSFIZZ_TESTS=ON` flag. +You can then use the `make sfizz_tests` command from your build directory. +The tests are located in `tests/sfizz_tests` from the same build directory. + +Note that these tests mostly cover the parsing and observable invariants of sfizz, as well as all helper methods as much as possible. +The processing and rendering is usually harder to test as it produces alot of data. +We are in the process of making a testing platform using rendered wave files, but it is not yet here. +If you write anything testable however, we encourage you to write the tests. +We use the [Catch] library for testing, and you can check out the files in `tests/` for examples on how to add some. + +To actually try your changes, you can use the `clients/sfizz_jack` client to instantiate a JACK application, or use `jalv` for a similar purpose by launching from your build directory +```bash +env LV2_PATH=$PWD jalv.gtk3 http://sfztools.github.io/sfizz +``` +Similarly, you can launch e.g. Ardour with suitable reference to your version of the sfizz LV2 plugin with +```bash +env LV2_PATH=$PWD Ardour6 +``` + +### Running micro-benchmarks + +For micro-optimization purposes we have a number of benchmarks written in `benchmarks/`, build against the [benchmark] library. +If you wonder about the performance of something, feel free to write some too and add them to the build system. +We tend to name the benchmarks with the `bm_` prefix. +It can be as simple as adding a line +```cmake +sfizz_add_benchmark(bm_wavfile BM_wavfile.cpp) +``` +to the `benchmarks/CMakeLists.txt` file but you might need to link to other libraries depending on what you are benchmarking. +The `CMakeLists.txt` file contains example that you can copy and try. + +### Running in-use benchmarks + +We have logging facilities for the processing inside of sfizz, that we use to track how the additions impact the rendering speed. +For now the usage is a bit hand-made, as it requires the [sfizz-render] program and manually pre-loading the development `.so` library. +The logger outputs CSV files for the rendering and file-loading processes. +The file `scripts/performance_report.py` is made to process and produce a nice interactive report from this data. + +[build]: https://sfz.tools/sfizz/development/build/ +[benchmark]: https://github.com/google/benchmark +[sfizz-render]: https://github.com/sfztools/sfizz-render +[api]: https://sfz.tools/sfizz/api/ +[RAII]: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#S-resource +[CppCoreGuidelines]: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines +[Catch]: https://github.com/catchorg/Catch2 From 605e552a251a5a05224195608b75e668d6bbd190 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 20 Apr 2020 22:54:04 +0200 Subject: [PATCH 099/444] Change the comment type into an enum --- src/sfizz/parser/Parser.cpp | 22 +++++++++++++--------- src/sfizz/parser/Parser.h | 8 +++++++- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/sfizz/parser/Parser.cpp b/src/sfizz/parser/Parser.cpp index 800d0379f..ffa660aa7 100644 --- a/src/sfizz/parser/Parser.cpp +++ b/src/sfizz/parser/Parser.cpp @@ -7,6 +7,7 @@ #include "Parser.h" #include "ParserPrivate.h" #include "absl/memory/memory.h" +#include namespace sfz { @@ -348,21 +349,21 @@ void Parser::flushCurrentHeader() _currentOpcodes.clear(); } -int Parser::hasComment(Reader& reader) +Parser::CommentType Parser::getCommentType(Reader& reader) { if (reader.peekChar() != '/') - return false; + return CommentType::None; reader.getChar(); - int ret = 0; + CommentType ret = CommentType::None; switch (reader.peekChar()) { case '/': - ret = 1; + ret = CommentType::Line; break; case '*': - ret = 2; + ret = CommentType::Block; break; } @@ -374,8 +375,8 @@ size_t Parser::skipComment() { Reader& reader = *_included.back(); - int commentType = hasComment(reader); - if (!commentType) + const CommentType commentType = getCommentType(reader); + if (commentType == CommentType::None) return 0; SourceLocation start = reader.location(); @@ -387,7 +388,7 @@ size_t Parser::skipComment() bool terminated = false; switch (commentType) { - case 1: // line comment + case CommentType::Line: { int c; while ((c = reader.getChar()) != Reader::kEof && c != '\r' && c != '\n') @@ -395,7 +396,7 @@ size_t Parser::skipComment() terminated = true; } break; - case 2: // block comment + case CommentType::Block: { int c1 = 0; int c2 = reader.getChar(); @@ -406,6 +407,9 @@ size_t Parser::skipComment() } } break; + default: + assert(false); + break; } if (!terminated) { diff --git a/src/sfizz/parser/Parser.h b/src/sfizz/parser/Parser.h index 88424d1d9..057143ad6 100644 --- a/src/sfizz/parser/Parser.h +++ b/src/sfizz/parser/Parser.h @@ -84,7 +84,13 @@ class Parser { void flushCurrentHeader(); // helpers - static int hasComment(Reader& reader); // 0=None 1=Line 2=Block + enum class CommentType { + None, + Line, + Block, + }; + + static CommentType getCommentType(Reader& reader); size_t skipComment(); static void trimRight(std::string& text); static size_t extractToEol(Reader& reader, std::string* dst); // ignores comment From 353cea949e8490c358f03d981949a8e074020e1a Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 20 Apr 2020 22:59:00 +0200 Subject: [PATCH 100/444] Make the skip count correct (even if not used currently) --- src/sfizz/parser/Parser.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sfizz/parser/Parser.cpp b/src/sfizz/parser/Parser.cpp index ffa660aa7..455c0aec5 100644 --- a/src/sfizz/parser/Parser.cpp +++ b/src/sfizz/parser/Parser.cpp @@ -389,20 +389,21 @@ size_t Parser::skipComment() switch (commentType) { case CommentType::Line: - { - int c; - while ((c = reader.getChar()) != Reader::kEof && c != '\r' && c != '\n') - ++count; - terminated = true; + while (!terminated) { + int c = reader.getChar(); + count += (c != Reader::kEof); + terminated = c == Reader::kEof || c == '\r' || c == '\n'; } break; case CommentType::Block: { int c1 = 0; int c2 = reader.getChar(); + count += (c2 != Reader::kEof); while (!terminated && c2 != Reader::kEof) { c1 = c2; c2 = reader.getChar(); + count += (c2 != Reader::kEof); terminated = c1 == '*' && c2 == '/'; } } From ffcdca81f694852df43597bf36dadb9545e790f4 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 21 Apr 2020 19:49:29 +0200 Subject: [PATCH 101/444] Transposed-form 2 biquads --- src/sfizz/dsp/filters/filters_modulable.dsp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/sfizz/dsp/filters/filters_modulable.dsp b/src/sfizz/dsp/filters/filters_modulable.dsp index fa46f7e04..2d4b99072 100644 --- a/src/sfizz/dsp/filters/filters_modulable.dsp +++ b/src/sfizz/dsp/filters/filters_modulable.dsp @@ -11,6 +11,7 @@ rbj = library("rbj_filters.dsp"); // b0,b1,b2,a1,a2 : normalized coefficients //------------------------------------------------------------------------- biquad(b0,b1,b2,a1,a2) = fi.iir((b0,b1,b2),(a1,a2)); +biquadTf2(b0,b1,b2,a1,a2) = fi.tf22t(b0,b1,b2,a1,a2); //------------------------------------------------------------------------- // Biquad filter using smoothed coefficients @@ -18,6 +19,7 @@ biquad(b0,b1,b2,a1,a2) = fi.iir((b0,b1,b2),(a1,a2)); // b0,b1,b2,a1,a2 : normalized coefficients //------------------------------------------------------------------------- smoothBiquad(s,b0,b1,b2,a1,a2) = biquad(b0:s,b1:s,b2:s,a1:s,a2:s); +smoothBiquadTf2(s,b0,b1,b2,a1,a2) = biquadTf2(b0:s,b1:s,b2:s,a1:s,a2:s); //------------------------------------------------------------------------- // RBJ filter of a specific type using smoothed coefficients @@ -27,14 +29,14 @@ smoothBiquad(s,b0,b1,b2,a1,a2) = biquad(b0:s,b1:s,b2:s,a1:s,a2:s); // q : height of the resonant peak in linear units //------------------------------------------------------------------------- rbjLpfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).LPF,x) : smoothBiquad(s); -rbjHpfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).HPF,x) : smoothBiquad(s); -rbjBpfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).BPF,x) : smoothBiquad(s); -rbjNotchSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).notch,x) : smoothBiquad(s); -rbjApfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).APF,x) : smoothBiquad(s); -rbjPeakingEqSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).peakingEQ,x) : smoothBiquad(s); -rbjPeakingNotchSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).peakNotch,x) : smoothBiquad(s); -rbjLowShelfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).lowShelf,x) : smoothBiquad(s); -rbjHighShelfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).highShelf,x) : smoothBiquad(s); +rbjHpfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).HPF,x) : smoothBiquadTf2(s); +rbjBpfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).BPF,x) : smoothBiquadTf2(s); +rbjNotchSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).notch,x) : smoothBiquadTf2(s); +rbjApfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).APF,x) : smoothBiquadTf2(s); +rbjPeakingEqSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).peakingEQ,x) : smoothBiquadTf2(s); +rbjPeakingNotchSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).peakNotch,x) : smoothBiquadTf2(s); +rbjLowShelfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).lowShelf,x) : smoothBiquadTf2(s); +rbjHighShelfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).highShelf,x) : smoothBiquadTf2(s); //------------------------------------------------------------------------- // 1-pole low-pass filter From 99702019b6e974b2b70b9c6865b1b994103ae4d7 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 21 Apr 2020 20:05:19 +0200 Subject: [PATCH 102/444] Allow the filters demo to disable modulation --- tests/DemoFilters.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/DemoFilters.cpp b/tests/DemoFilters.cpp index 3a59ea7ec..3a5eb12e6 100644 --- a/tests/DemoFilters.cpp +++ b/tests/DemoFilters.cpp @@ -62,7 +62,7 @@ class DemoApp : public QApplication { static constexpr float lfoRateMin = 0.1; static constexpr float lfoRateMax = 10.0; - static constexpr int cutoffModMin = 1; + static constexpr int cutoffModMin = 0; static constexpr int cutoffModMax = 48; int fType = sfz::kFilterNone; From 84436601bc7e6f64d23afae3f6730a31d54f74f9 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 21 Apr 2020 20:06:23 +0200 Subject: [PATCH 103/444] Delete the SVF gain compensation (match Rapture) --- src/sfizz/dsp/filters/sfz_filters.dsp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sfizz/dsp/filters/sfz_filters.dsp b/src/sfizz/dsp/filters/sfz_filters.dsp index d944d4523..1f47ce04c 100644 --- a/src/sfizz/dsp/filters/sfz_filters.dsp +++ b/src/sfizz/dsp/filters/sfz_filters.dsp @@ -78,8 +78,7 @@ sfzLpf2pSv = sk.sallenKey2ndOrderLPF(smoothCoefs,cutoff,Q); sfzHpf2pSv = sk.sallenKey2ndOrderHPF(smoothCoefs,cutoff,Q); // the SFZ 2-pole state-variable bandpass filter -// Note: attenuate in order to have the resonant peak at 0 dB (same as sfzBpf2p) -sfzBpf2pSv = sk.sallenKey2ndOrderBPF(smoothCoefs,cutoff,Q) : *((1./Q):smoothCoefs); +sfzBpf2pSv = sk.sallenKey2ndOrderBPF(smoothCoefs,cutoff,Q); // the SFZ 2-pole state-variable notch filter sfzBrf2pSv = _ <: (sfzLpf2pSv, sfzHpf2pSv) :> +; From f5f7ded7e65d54c932b6da6109c42f2be877e5b1 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 21 Apr 2020 20:07:58 +0200 Subject: [PATCH 104/444] Regenerate the filters --- src/sfizz/gen/filters/sfz2chBpf2p.cxx | 108 +++++++--- src/sfizz/gen/filters/sfz2chBpf2pSv.cxx | 29 +-- src/sfizz/gen/filters/sfz2chBpf4p.cxx | 174 ++++++++++++---- src/sfizz/gen/filters/sfz2chBpf6p.cxx | 252 ++++++++++++++++++----- src/sfizz/gen/filters/sfz2chBrf2p.cxx | 94 ++++++--- src/sfizz/gen/filters/sfz2chEqHshelf.cxx | 104 +++++++--- src/sfizz/gen/filters/sfz2chEqLshelf.cxx | 104 +++++++--- src/sfizz/gen/filters/sfz2chEqPeak.cxx | 100 ++++++--- src/sfizz/gen/filters/sfz2chHpf2p.cxx | 94 ++++++--- src/sfizz/gen/filters/sfz2chHpf4p.cxx | 174 ++++++++++++---- src/sfizz/gen/filters/sfz2chHpf6p.cxx | 248 +++++++++++++++++----- src/sfizz/gen/filters/sfz2chHsh.cxx | 106 +++++++--- src/sfizz/gen/filters/sfz2chLsh.cxx | 104 +++++++--- src/sfizz/gen/filters/sfz2chPeq.cxx | 100 ++++++--- src/sfizz/gen/filters/sfzBpf2p.cxx | 73 ++++--- src/sfizz/gen/filters/sfzBpf2pSv.cxx | 9 +- src/sfizz/gen/filters/sfzBpf4p.cxx | 104 +++++++--- src/sfizz/gen/filters/sfzBpf6p.cxx | 145 +++++++++---- src/sfizz/gen/filters/sfzBrf2p.cxx | 59 ++++-- src/sfizz/gen/filters/sfzEqHshelf.cxx | 69 ++++--- src/sfizz/gen/filters/sfzEqLshelf.cxx | 69 ++++--- src/sfizz/gen/filters/sfzEqPeak.cxx | 66 ++++-- src/sfizz/gen/filters/sfzHpf2p.cxx | 58 ++++-- src/sfizz/gen/filters/sfzHpf4p.cxx | 102 ++++++--- src/sfizz/gen/filters/sfzHpf6p.cxx | 138 ++++++++++--- src/sfizz/gen/filters/sfzHsh.cxx | 71 ++++--- src/sfizz/gen/filters/sfzLsh.cxx | 69 ++++--- src/sfizz/gen/filters/sfzPeq.cxx | 66 ++++-- 28 files changed, 2099 insertions(+), 790 deletions(-) diff --git a/src/sfizz/gen/filters/sfz2chBpf2p.cxx b/src/sfizz/gen/filters/sfz2chBpf2p.cxx index dd4a004e8..011f0d3d7 100644 --- a/src/sfizz/gen/filters/sfz2chBpf2p.cxx +++ b/src/sfizz/gen/filters/sfz2chBpf2p.cxx @@ -34,16 +34,24 @@ class faust2chBpf2p : public sfzFilterDsp { int fSampleRate; double fConst0; double fConst1; + double fRec2[2]; + double fVec0[2]; double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec1[2]; - double fRec2[2]; - double fRec0[3]; double fRec3[2]; double fRec4[2]; + double fVec1[2]; double fRec5[2]; - double fRec6[3]; + double fVec2[2]; + double fRec6[2]; + double fRec1[2]; + double fRec0[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec8[2]; + double fRec7[2]; public: @@ -110,25 +118,49 @@ class faust2chBpf2p : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec1[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } - for (int l2 = 0; (l2 < 3); l2 = (l2 + 1)) { - fRec0[l2] = 0.0; + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0; } for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + fRec4[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec4[l4] = 0.0; + fVec1[l4] = 0.0; } for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { fRec5[l5] = 0.0; } - for (int l6 = 0; (l6 < 3); l6 = (l6 + 1)) { - fRec6[l6] = 0.0; + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec6[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec1[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec0[l9] = 0.0; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec3[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fVec4[l11] = 0.0; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fVec5[l12] = 0.0; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec8[l13] = 0.0; + } + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fRec7[l14] = 0.0; } } @@ -164,33 +196,47 @@ class faust2chBpf2p : public sfzFilterDsp { double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * double(fQ)))); double fSlow4 = (0.5 * (fSlow2 / fSlow3)); double fSlow5 = (fSlow4 + 1.0); - double fSlow6 = (1.0 - fSlow0); - double fSlow7 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow5) * fSlow6); - double fSlow8 = (((1.0 - fSlow4) / fSlow5) * fSlow6); - double fSlow9 = (0.5 * (fSlow2 / (fSlow3 * fSlow5))); - double fSlow10 = (fSlow9 * fSlow6); - double fSlow11 = ((0.0 - fSlow9) * fSlow6); + double fSlow6 = (0.5 * (fSlow2 / (fSlow3 * fSlow5))); + double fSlow7 = (1.0 - fSlow0); + double fSlow8 = (fSlow6 * fSlow7); + double fSlow9 = ((0.0 - fSlow6) * fSlow7); + double fSlow10 = (((1.0 - fSlow4) / fSlow5) * fSlow7); + double fSlow11 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow5) * fSlow7); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); - fRec1[0] = ((fSlow0 * fRec1[1]) + fSlow7); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow8); - fRec0[0] = (fTemp0 - ((fRec1[0] * fRec0[1]) + (fRec2[0] * fRec0[2]))); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow10); - fRec4[0] = (fSlow0 * fRec4[1]); - fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow11); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec3[0]) + (fRec4[0] * fRec0[1])) + (fRec5[0] * fRec0[2]))); - fRec6[0] = (fTemp1 - ((fRec1[0] * fRec6[1]) + (fRec2[0] * fRec6[2]))); - output1[i] = FAUSTFLOAT((((fRec3[0] * fRec6[0]) + (fRec4[0] * fRec6[1])) + (fRec5[0] * fRec6[2]))); - fRec1[1] = fRec1[0]; + fRec2[0] = (fSlow0 * fRec2[1]); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow8); + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow9); + fVec1[0] = (fTemp0 * fRec4[0]); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow10); + fVec2[0] = (fVec1[1] - (fRec5[0] * fRec0[1])); + fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow11); + fRec1[0] = ((fVec0[1] + ((fTemp0 * fRec3[0]) + fVec2[1])) - (fRec6[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); + fVec3[0] = (fTemp1 * fRec2[0]); + fVec4[0] = (fTemp1 * fRec4[0]); + fVec5[0] = (fVec4[1] - (fRec5[0] * fRec7[1])); + fRec8[0] = ((fVec3[1] + ((fTemp1 * fRec3[0]) + fVec5[1])) - (fRec6[0] * fRec8[1])); + fRec7[0] = fRec8[0]; + output1[i] = FAUSTFLOAT(fRec7[0]); fRec2[1] = fRec2[0]; - fRec0[2] = fRec0[1]; - fRec0[1] = fRec0[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; fRec4[1] = fRec4[0]; + fVec1[1] = fVec1[0]; fRec5[1] = fRec5[0]; - fRec6[2] = fRec6[1]; + fVec2[1] = fVec2[0]; fRec6[1] = fRec6[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; + fRec8[1] = fRec8[0]; + fRec7[1] = fRec7[0]; } } diff --git a/src/sfizz/gen/filters/sfz2chBpf2pSv.cxx b/src/sfizz/gen/filters/sfz2chBpf2pSv.cxx index 7191bb6cd..f85094dd1 100644 --- a/src/sfizz/gen/filters/sfz2chBpf2pSv.cxx +++ b/src/sfizz/gen/filters/sfz2chBpf2pSv.cxx @@ -42,9 +42,8 @@ class faust2chBpf2pSv : public sfzFilterDsp { double fRec5[2]; double fRec1[2]; double fRec2[2]; - double fRec6[2]; + double fRec7[2]; double fRec8[2]; - double fRec9[2]; public: @@ -126,14 +125,11 @@ class faust2chBpf2pSv : public sfzFilterDsp { fRec2[l4] = 0.0; } for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { - fRec6[l5] = 0.0; + fRec7[l5] = 0.0; } for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { fRec8[l6] = 0.0; } - for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { - fRec9[l7] = 0.0; - } } virtual void init(int sample_rate) { @@ -166,7 +162,6 @@ class faust2chBpf2pSv : public sfzFilterDsp { double fSlow1 = (1.0 - fSlow0); double fSlow2 = (std::tan((fConst2 * double(fCutoff))) * fSlow1); double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * double(fQ)))); - double fSlow4 = (fSlow3 * fSlow1); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); @@ -182,24 +177,22 @@ class faust2chBpf2pSv : public sfzFilterDsp { fRec1[0] = (fRec1[1] + (2.0 * (fRec3[0] * fTemp6))); double fTemp7 = (fRec2[1] + (2.0 * fTemp5)); fRec2[0] = fTemp7; - fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow4); - output0[i] = FAUSTFLOAT((fRec0 * fRec6[0])); - double fTemp8 = (fTemp1 - (fRec8[1] + (fRec5[0] * fRec9[1]))); + output0[i] = FAUSTFLOAT(fRec0); + double fTemp8 = (fTemp1 - (fRec7[1] + (fRec5[0] * fRec8[1]))); double fTemp9 = (fTemp3 * fTemp8); - double fTemp10 = (fRec9[1] + fTemp9); - double fRec7 = fTemp10; - fRec8[0] = (fRec8[1] + (2.0 * (fRec3[0] * fTemp10))); - double fTemp11 = (fRec9[1] + (2.0 * fTemp9)); - fRec9[0] = fTemp11; - output1[i] = FAUSTFLOAT((fRec6[0] * fRec7)); + double fTemp10 = (fRec8[1] + fTemp9); + double fRec6 = fTemp10; + fRec7[0] = (fRec7[1] + (2.0 * (fRec3[0] * fTemp10))); + double fTemp11 = (fRec8[1] + (2.0 * fTemp9)); + fRec8[0] = fTemp11; + output1[i] = FAUSTFLOAT(fRec6); fRec3[1] = fRec3[0]; fRec4[1] = fRec4[0]; fRec5[1] = fRec5[0]; fRec1[1] = fRec1[0]; fRec2[1] = fRec2[0]; - fRec6[1] = fRec6[0]; + fRec7[1] = fRec7[0]; fRec8[1] = fRec8[0]; - fRec9[1] = fRec9[0]; } } diff --git a/src/sfizz/gen/filters/sfz2chBpf4p.cxx b/src/sfizz/gen/filters/sfz2chBpf4p.cxx index a2afdc8f0..cf9d5cb53 100644 --- a/src/sfizz/gen/filters/sfz2chBpf4p.cxx +++ b/src/sfizz/gen/filters/sfz2chBpf4p.cxx @@ -37,15 +37,31 @@ class faust2chBpf4p : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec0[2]; - double fRec3[2]; - double fRec4[2]; - double fRec2[3]; + double fRec2[2]; double fRec5[2]; + double fVec0[2]; double fRec6[2]; - double fRec1[3]; - double fRec8[3]; - double fRec7[3]; + double fVec1[2]; + double fRec7[2]; + double fVec2[2]; + double fRec8[2]; + double fRec4[2]; + double fRec3[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec1[2]; + double fRec0[2]; + double fVec6[2]; + double fVec7[2]; + double fVec8[2]; + double fRec12[2]; + double fRec11[2]; + double fVec9[2]; + double fVec10[2]; + double fVec11[2]; + double fRec10[2]; + double fRec9[2]; public: @@ -112,31 +128,79 @@ class faust2chBpf4p : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec0[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec3[l1] = 0.0; + fRec5[l1] = 0.0; } for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { - fRec4[l2] = 0.0; + fVec0[l2] = 0.0; } - for (int l3 = 0; (l3 < 3); l3 = (l3 + 1)) { - fRec2[l3] = 0.0; + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fRec6[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec5[l4] = 0.0; + fVec1[l4] = 0.0; } for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { - fRec6[l5] = 0.0; + fRec7[l5] = 0.0; } - for (int l6 = 0; (l6 < 3); l6 = (l6 + 1)) { - fRec1[l6] = 0.0; + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; } - for (int l7 = 0; (l7 < 3); l7 = (l7 + 1)) { + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { fRec8[l7] = 0.0; } - for (int l8 = 0; (l8 < 3); l8 = (l8 + 1)) { - fRec7[l8] = 0.0; + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec4[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec3[l9] = 0.0; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec3[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fVec4[l11] = 0.0; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fVec5[l12] = 0.0; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec1[l13] = 0.0; + } + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fRec0[l14] = 0.0; + } + for (int l15 = 0; (l15 < 2); l15 = (l15 + 1)) { + fVec6[l15] = 0.0; + } + for (int l16 = 0; (l16 < 2); l16 = (l16 + 1)) { + fVec7[l16] = 0.0; + } + for (int l17 = 0; (l17 < 2); l17 = (l17 + 1)) { + fVec8[l17] = 0.0; + } + for (int l18 = 0; (l18 < 2); l18 = (l18 + 1)) { + fRec12[l18] = 0.0; + } + for (int l19 = 0; (l19 < 2); l19 = (l19 + 1)) { + fRec11[l19] = 0.0; + } + for (int l20 = 0; (l20 < 2); l20 = (l20 + 1)) { + fVec9[l20] = 0.0; + } + for (int l21 = 0; (l21 < 2); l21 = (l21 + 1)) { + fVec10[l21] = 0.0; + } + for (int l22 = 0; (l22 < 2); l22 = (l22 + 1)) { + fVec11[l22] = 0.0; + } + for (int l23 = 0; (l23 < 2); l23 = (l23 + 1)) { + fRec10[l23] = 0.0; + } + for (int l24 = 0; (l24 < 2); l24 = (l24 + 1)) { + fRec9[l24] = 0.0; } } @@ -174,37 +238,65 @@ class faust2chBpf4p : public sfzFilterDsp { double fSlow5 = (fSlow4 + 1.0); double fSlow6 = (0.5 * (fSlow2 / (fSlow3 * fSlow5))); double fSlow7 = (1.0 - fSlow0); - double fSlow8 = (fSlow6 * fSlow7); - double fSlow9 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow5) * fSlow7); + double fSlow8 = ((0.0 - fSlow6) * fSlow7); + double fSlow9 = (fSlow6 * fSlow7); double fSlow10 = (((1.0 - fSlow4) / fSlow5) * fSlow7); - double fSlow11 = ((0.0 - fSlow6) * fSlow7); + double fSlow11 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow5) * fSlow7); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); - fRec0[0] = ((fSlow0 * fRec0[1]) + fSlow8); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow9); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow10); - fRec2[0] = (fTemp0 - ((fRec3[0] * fRec2[1]) + (fRec4[0] * fRec2[2]))); + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow8); fRec5[0] = (fSlow0 * fRec5[1]); - fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow11); - fRec1[0] = ((((fRec2[0] * fRec0[0]) + (fRec5[0] * fRec2[1])) + (fRec6[0] * fRec2[2])) - ((fRec3[0] * fRec1[1]) + (fRec4[0] * fRec1[2]))); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec1[0]) + (fRec5[0] * fRec1[1])) + (fRec6[0] * fRec1[2]))); - fRec8[0] = (fTemp1 - ((fRec3[0] * fRec8[1]) + (fRec4[0] * fRec8[2]))); - fRec7[0] = ((((fRec0[0] * fRec8[0]) + (fRec5[0] * fRec8[1])) + (fRec6[0] * fRec8[2])) - ((fRec3[0] * fRec7[1]) + (fRec4[0] * fRec7[2]))); - output1[i] = FAUSTFLOAT((((fRec0[0] * fRec7[0]) + (fRec5[0] * fRec7[1])) + (fRec6[0] * fRec7[2]))); - fRec0[1] = fRec0[0]; - fRec3[1] = fRec3[0]; - fRec4[1] = fRec4[0]; - fRec2[2] = fRec2[1]; + fVec0[0] = (fTemp0 * fRec5[0]); + fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow9); + fVec1[0] = (fTemp0 * fRec2[0]); + fRec7[0] = ((fSlow0 * fRec7[1]) + fSlow10); + fVec2[0] = (fVec1[1] - (fRec7[0] * fRec3[1])); + fRec8[0] = ((fSlow0 * fRec8[1]) + fSlow11); + fRec4[0] = ((fVec0[1] + ((fTemp0 * fRec6[0]) + fVec2[1])) - (fRec8[0] * fRec4[1])); + fRec3[0] = fRec4[0]; + fVec3[0] = (fRec2[0] * fRec3[0]); + fVec4[0] = (fVec3[1] - (fRec7[0] * fRec0[1])); + fVec5[0] = (fRec5[0] * fRec3[0]); + fRec1[0] = (((fVec4[1] + fVec5[1]) + (fRec6[0] * fRec3[0])) - (fRec8[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); + fVec6[0] = (fTemp1 * fRec5[0]); + fVec7[0] = (fTemp1 * fRec2[0]); + fVec8[0] = (fVec7[1] - (fRec7[0] * fRec11[1])); + fRec12[0] = ((fVec6[1] + ((fTemp1 * fRec6[0]) + fVec8[1])) - (fRec8[0] * fRec12[1])); + fRec11[0] = fRec12[0]; + fVec9[0] = (fRec2[0] * fRec11[0]); + fVec10[0] = (fVec9[1] - (fRec7[0] * fRec9[1])); + fVec11[0] = (fRec5[0] * fRec11[0]); + fRec10[0] = (((fVec10[1] + fVec11[1]) + (fRec6[0] * fRec11[0])) - (fRec8[0] * fRec10[1])); + fRec9[0] = fRec10[0]; + output1[i] = FAUSTFLOAT(fRec9[0]); fRec2[1] = fRec2[0]; fRec5[1] = fRec5[0]; + fVec0[1] = fVec0[0]; fRec6[1] = fRec6[0]; - fRec1[2] = fRec1[1]; - fRec1[1] = fRec1[0]; - fRec8[2] = fRec8[1]; - fRec8[1] = fRec8[0]; - fRec7[2] = fRec7[1]; + fVec1[1] = fVec1[0]; fRec7[1] = fRec7[0]; + fVec2[1] = fVec2[0]; + fRec8[1] = fRec8[0]; + fRec4[1] = fRec4[0]; + fRec3[1] = fRec3[0]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; + fVec6[1] = fVec6[0]; + fVec7[1] = fVec7[0]; + fVec8[1] = fVec8[0]; + fRec12[1] = fRec12[0]; + fRec11[1] = fRec11[0]; + fVec9[1] = fVec9[0]; + fVec10[1] = fVec10[0]; + fVec11[1] = fVec11[0]; + fRec10[1] = fRec10[0]; + fRec9[1] = fRec9[0]; } } diff --git a/src/sfizz/gen/filters/sfz2chBpf6p.cxx b/src/sfizz/gen/filters/sfz2chBpf6p.cxx index 27165c1ec..2e8808c6f 100644 --- a/src/sfizz/gen/filters/sfz2chBpf6p.cxx +++ b/src/sfizz/gen/filters/sfz2chBpf6p.cxx @@ -37,17 +37,41 @@ class faust2chBpf6p : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec0[2]; - double fRec4[2]; - double fRec5[2]; - double fRec3[3]; - double fRec6[2]; + double fRec2[2]; double fRec7[2]; - double fRec2[3]; - double fRec1[3]; - double fRec10[3]; - double fRec9[3]; - double fRec8[3]; + double fVec0[2]; + double fRec8[2]; + double fVec1[2]; + double fRec9[2]; + double fVec2[2]; + double fRec10[2]; + double fRec6[2]; + double fRec5[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec4[2]; + double fRec3[2]; + double fVec6[2]; + double fVec7[2]; + double fVec8[2]; + double fRec1[2]; + double fRec0[2]; + double fVec9[2]; + double fVec10[2]; + double fVec11[2]; + double fRec16[2]; + double fRec15[2]; + double fVec12[2]; + double fVec13[2]; + double fVec14[2]; + double fRec14[2]; + double fRec13[2]; + double fVec15[2]; + double fVec16[2]; + double fVec17[2]; + double fRec12[2]; + double fRec11[2]; public: @@ -114,37 +138,109 @@ class faust2chBpf6p : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec0[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec4[l1] = 0.0; + fRec7[l1] = 0.0; } for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { - fRec5[l2] = 0.0; + fVec0[l2] = 0.0; } - for (int l3 = 0; (l3 < 3); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fRec8[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec6[l4] = 0.0; + fVec1[l4] = 0.0; } for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { - fRec7[l5] = 0.0; + fRec9[l5] = 0.0; + } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec10[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec6[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec5[l9] = 0.0; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec3[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fVec4[l11] = 0.0; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fVec5[l12] = 0.0; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec4[l13] = 0.0; + } + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fRec3[l14] = 0.0; + } + for (int l15 = 0; (l15 < 2); l15 = (l15 + 1)) { + fVec6[l15] = 0.0; + } + for (int l16 = 0; (l16 < 2); l16 = (l16 + 1)) { + fVec7[l16] = 0.0; + } + for (int l17 = 0; (l17 < 2); l17 = (l17 + 1)) { + fVec8[l17] = 0.0; + } + for (int l18 = 0; (l18 < 2); l18 = (l18 + 1)) { + fRec1[l18] = 0.0; + } + for (int l19 = 0; (l19 < 2); l19 = (l19 + 1)) { + fRec0[l19] = 0.0; + } + for (int l20 = 0; (l20 < 2); l20 = (l20 + 1)) { + fVec9[l20] = 0.0; + } + for (int l21 = 0; (l21 < 2); l21 = (l21 + 1)) { + fVec10[l21] = 0.0; } - for (int l6 = 0; (l6 < 3); l6 = (l6 + 1)) { - fRec2[l6] = 0.0; + for (int l22 = 0; (l22 < 2); l22 = (l22 + 1)) { + fVec11[l22] = 0.0; } - for (int l7 = 0; (l7 < 3); l7 = (l7 + 1)) { - fRec1[l7] = 0.0; + for (int l23 = 0; (l23 < 2); l23 = (l23 + 1)) { + fRec16[l23] = 0.0; } - for (int l8 = 0; (l8 < 3); l8 = (l8 + 1)) { - fRec10[l8] = 0.0; + for (int l24 = 0; (l24 < 2); l24 = (l24 + 1)) { + fRec15[l24] = 0.0; } - for (int l9 = 0; (l9 < 3); l9 = (l9 + 1)) { - fRec9[l9] = 0.0; + for (int l25 = 0; (l25 < 2); l25 = (l25 + 1)) { + fVec12[l25] = 0.0; } - for (int l10 = 0; (l10 < 3); l10 = (l10 + 1)) { - fRec8[l10] = 0.0; + for (int l26 = 0; (l26 < 2); l26 = (l26 + 1)) { + fVec13[l26] = 0.0; + } + for (int l27 = 0; (l27 < 2); l27 = (l27 + 1)) { + fVec14[l27] = 0.0; + } + for (int l28 = 0; (l28 < 2); l28 = (l28 + 1)) { + fRec14[l28] = 0.0; + } + for (int l29 = 0; (l29 < 2); l29 = (l29 + 1)) { + fRec13[l29] = 0.0; + } + for (int l30 = 0; (l30 < 2); l30 = (l30 + 1)) { + fVec15[l30] = 0.0; + } + for (int l31 = 0; (l31 < 2); l31 = (l31 + 1)) { + fVec16[l31] = 0.0; + } + for (int l32 = 0; (l32 < 2); l32 = (l32 + 1)) { + fVec17[l32] = 0.0; + } + for (int l33 = 0; (l33 < 2); l33 = (l33 + 1)) { + fRec12[l33] = 0.0; + } + for (int l34 = 0; (l34 < 2); l34 = (l34 + 1)) { + fRec11[l34] = 0.0; } } @@ -182,43 +278,85 @@ class faust2chBpf6p : public sfzFilterDsp { double fSlow5 = (fSlow4 + 1.0); double fSlow6 = (0.5 * (fSlow2 / (fSlow3 * fSlow5))); double fSlow7 = (1.0 - fSlow0); - double fSlow8 = (fSlow6 * fSlow7); - double fSlow9 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow5) * fSlow7); + double fSlow8 = ((0.0 - fSlow6) * fSlow7); + double fSlow9 = (fSlow6 * fSlow7); double fSlow10 = (((1.0 - fSlow4) / fSlow5) * fSlow7); - double fSlow11 = ((0.0 - fSlow6) * fSlow7); + double fSlow11 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow5) * fSlow7); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); - fRec0[0] = ((fSlow0 * fRec0[1]) + fSlow8); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow9); - fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow10); - fRec3[0] = (fTemp0 - ((fRec4[0] * fRec3[1]) + (fRec5[0] * fRec3[2]))); - fRec6[0] = (fSlow0 * fRec6[1]); - fRec7[0] = ((fSlow0 * fRec7[1]) + fSlow11); - fRec2[0] = ((((fRec3[0] * fRec0[0]) + (fRec6[0] * fRec3[1])) + (fRec7[0] * fRec3[2])) - ((fRec4[0] * fRec2[1]) + (fRec5[0] * fRec2[2]))); - fRec1[0] = (((fRec0[0] * fRec2[0]) + ((fRec6[0] * fRec2[1]) + (fRec7[0] * fRec2[2]))) - ((fRec4[0] * fRec1[1]) + (fRec5[0] * fRec1[2]))); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec1[0]) + (fRec6[0] * fRec1[1])) + (fRec7[0] * fRec1[2]))); - fRec10[0] = (fTemp1 - ((fRec4[0] * fRec10[1]) + (fRec5[0] * fRec10[2]))); - fRec9[0] = ((((fRec0[0] * fRec10[0]) + (fRec6[0] * fRec10[1])) + (fRec7[0] * fRec10[2])) - ((fRec4[0] * fRec9[1]) + (fRec5[0] * fRec9[2]))); - fRec8[0] = ((((fRec0[0] * fRec9[0]) + (fRec6[0] * fRec9[1])) + (fRec7[0] * fRec9[2])) - ((fRec4[0] * fRec8[1]) + (fRec5[0] * fRec8[2]))); - output1[i] = FAUSTFLOAT((((fRec0[0] * fRec8[0]) + (fRec6[0] * fRec8[1])) + (fRec7[0] * fRec8[2]))); - fRec0[1] = fRec0[0]; - fRec4[1] = fRec4[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow8); + fRec7[0] = (fSlow0 * fRec7[1]); + fVec0[0] = (fTemp0 * fRec7[0]); + fRec8[0] = ((fSlow0 * fRec8[1]) + fSlow9); + fVec1[0] = (fTemp0 * fRec2[0]); + fRec9[0] = ((fSlow0 * fRec9[1]) + fSlow10); + fVec2[0] = (fVec1[1] - (fRec9[0] * fRec5[1])); + fRec10[0] = ((fSlow0 * fRec10[1]) + fSlow11); + fRec6[0] = ((fVec0[1] + ((fTemp0 * fRec8[0]) + fVec2[1])) - (fRec10[0] * fRec6[1])); + fRec5[0] = fRec6[0]; + fVec3[0] = (fRec2[0] * fRec5[0]); + fVec4[0] = (fVec3[1] - (fRec9[0] * fRec3[1])); + fVec5[0] = (fRec7[0] * fRec5[0]); + fRec4[0] = (((fVec4[1] + fVec5[1]) + (fRec8[0] * fRec5[0])) - (fRec10[0] * fRec4[1])); + fRec3[0] = fRec4[0]; + fVec6[0] = (fRec2[0] * fRec3[0]); + fVec7[0] = (fVec6[1] - (fRec9[0] * fRec0[1])); + fVec8[0] = (fRec7[0] * fRec3[0]); + fRec1[0] = (((fVec7[1] + fVec8[1]) + (fRec8[0] * fRec3[0])) - (fRec10[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); + fVec9[0] = (fTemp1 * fRec7[0]); + fVec10[0] = (fTemp1 * fRec2[0]); + fVec11[0] = (fVec10[1] - (fRec9[0] * fRec15[1])); + fRec16[0] = ((fVec9[1] + ((fTemp1 * fRec8[0]) + fVec11[1])) - (fRec10[0] * fRec16[1])); + fRec15[0] = fRec16[0]; + fVec12[0] = (fRec2[0] * fRec15[0]); + fVec13[0] = (fVec12[1] - (fRec9[0] * fRec13[1])); + fVec14[0] = (fRec7[0] * fRec15[0]); + fRec14[0] = (((fVec13[1] + fVec14[1]) + (fRec8[0] * fRec15[0])) - (fRec10[0] * fRec14[1])); + fRec13[0] = fRec14[0]; + fVec15[0] = (fRec2[0] * fRec13[0]); + fVec16[0] = (fVec15[1] - (fRec9[0] * fRec11[1])); + fVec17[0] = (fRec7[0] * fRec13[0]); + fRec12[0] = (((fVec16[1] + fVec17[1]) + (fRec8[0] * fRec13[0])) - (fRec10[0] * fRec12[1])); + fRec11[0] = fRec12[0]; + output1[i] = FAUSTFLOAT(fRec11[0]); + fRec2[1] = fRec2[0]; + fRec7[1] = fRec7[0]; + fVec0[1] = fVec0[0]; + fRec8[1] = fRec8[0]; + fVec1[1] = fVec1[0]; + fRec9[1] = fRec9[0]; + fVec2[1] = fVec2[0]; + fRec10[1] = fRec10[0]; + fRec6[1] = fRec6[0]; fRec5[1] = fRec5[0]; - fRec3[2] = fRec3[1]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; + fRec4[1] = fRec4[0]; fRec3[1] = fRec3[0]; - fRec6[1] = fRec6[0]; - fRec7[1] = fRec7[0]; - fRec2[2] = fRec2[1]; - fRec2[1] = fRec2[0]; - fRec1[2] = fRec1[1]; + fVec6[1] = fVec6[0]; + fVec7[1] = fVec7[0]; + fVec8[1] = fVec8[0]; fRec1[1] = fRec1[0]; - fRec10[2] = fRec10[1]; - fRec10[1] = fRec10[0]; - fRec9[2] = fRec9[1]; - fRec9[1] = fRec9[0]; - fRec8[2] = fRec8[1]; - fRec8[1] = fRec8[0]; + fRec0[1] = fRec0[0]; + fVec9[1] = fVec9[0]; + fVec10[1] = fVec10[0]; + fVec11[1] = fVec11[0]; + fRec16[1] = fRec16[0]; + fRec15[1] = fRec15[0]; + fVec12[1] = fVec12[0]; + fVec13[1] = fVec13[0]; + fVec14[1] = fVec14[0]; + fRec14[1] = fRec14[0]; + fRec13[1] = fRec13[0]; + fVec15[1] = fVec15[0]; + fVec16[1] = fVec16[0]; + fVec17[1] = fVec17[0]; + fRec12[1] = fRec12[0]; + fRec11[1] = fRec11[0]; } } diff --git a/src/sfizz/gen/filters/sfz2chBrf2p.cxx b/src/sfizz/gen/filters/sfz2chBrf2p.cxx index 044fb4f6e..91e28d076 100644 --- a/src/sfizz/gen/filters/sfz2chBrf2p.cxx +++ b/src/sfizz/gen/filters/sfz2chBrf2p.cxx @@ -37,11 +37,19 @@ class faust2chBrf2p : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec0[2]; double fRec2[2]; - double fRec1[3]; + double fVec0[2]; double fRec3[2]; - double fRec4[3]; + double fVec1[2]; + double fRec4[2]; + double fVec2[2]; + double fRec1[2]; + double fRec0[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec6[2]; + double fRec5[2]; public: @@ -108,20 +116,44 @@ class faust2chBrf2p : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec0[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } - for (int l2 = 0; (l2 < 3); l2 = (l2 + 1)) { - fRec1[l2] = 0.0; + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0; } for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + fVec1[l3] = 0.0; } - for (int l4 = 0; (l4 < 3); l4 = (l4 + 1)) { + for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { fRec4[l4] = 0.0; } + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fVec2[l5] = 0.0; + } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fRec1[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec0[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fVec3[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fVec4[l9] = 0.0; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec5[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fRec6[l11] = 0.0; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fRec5[l12] = 0.0; + } } virtual void init(int sample_rate) { @@ -156,27 +188,41 @@ class faust2chBrf2p : public sfzFilterDsp { double fSlow3 = (fSlow2 + 1.0); double fSlow4 = (1.0 - fSlow0); double fSlow5 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow3) * fSlow4); - double fSlow6 = (((1.0 - fSlow2) / fSlow3) * fSlow4); - double fSlow7 = ((1.0 / fSlow3) * fSlow4); + double fSlow6 = ((1.0 / fSlow3) * fSlow4); + double fSlow7 = (((1.0 - fSlow2) / fSlow3) * fSlow4); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); - fRec0[0] = ((fSlow0 * fRec0[1]) + fSlow5); - double fTemp2 = (fRec0[0] * fRec1[1]); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow6); - fRec1[0] = (fTemp0 - (fTemp2 + (fRec2[0] * fRec1[2]))); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow7); - output0[i] = FAUSTFLOAT((fTemp2 + (fRec3[0] * (fRec1[0] + fRec1[2])))); - double fTemp3 = (fRec0[0] * fRec4[1]); - fRec4[0] = (fTemp1 - (fTemp3 + (fRec2[0] * fRec4[2]))); - output1[i] = FAUSTFLOAT((fTemp3 + (fRec3[0] * (fRec4[0] + fRec4[2])))); - fRec0[1] = fRec0[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow5); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow6); + double fTemp2 = (fTemp0 * fRec3[0]); + fVec1[0] = fTemp2; + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow7); + fVec2[0] = (fVec1[1] - (fRec4[0] * fRec0[1])); + fRec1[0] = ((fVec0[1] + (fTemp2 + fVec2[1])) - (fRec2[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); + fVec3[0] = (fTemp1 * fRec2[0]); + double fTemp3 = (fTemp1 * fRec3[0]); + fVec4[0] = fTemp3; + fVec5[0] = (fVec4[1] - (fRec4[0] * fRec5[1])); + fRec6[0] = ((fVec3[1] + (fTemp3 + fVec5[1])) - (fRec2[0] * fRec6[1])); + fRec5[0] = fRec6[0]; + output1[i] = FAUSTFLOAT(fRec5[0]); fRec2[1] = fRec2[0]; - fRec1[2] = fRec1[1]; - fRec1[1] = fRec1[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; - fRec4[2] = fRec4[1]; + fVec1[1] = fVec1[0]; fRec4[1] = fRec4[0]; + fVec2[1] = fVec2[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; + fRec6[1] = fRec6[0]; + fRec5[1] = fRec5[0]; } } diff --git a/src/sfizz/gen/filters/sfz2chEqHshelf.cxx b/src/sfizz/gen/filters/sfz2chEqHshelf.cxx index 19349a513..85c9ea937 100644 --- a/src/sfizz/gen/filters/sfz2chEqHshelf.cxx +++ b/src/sfizz/gen/filters/sfz2chEqHshelf.cxx @@ -41,13 +41,21 @@ class faust2chEqHshelf : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fBandwidth; - double fRec1[2]; double fRec2[2]; - double fRec0[3]; + double fVec0[2]; double fRec3[2]; double fRec4[2]; + double fVec1[2]; double fRec5[2]; - double fRec6[3]; + double fVec2[2]; + double fRec6[2]; + double fRec1[2]; + double fRec0[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec8[2]; + double fRec7[2]; public: @@ -115,25 +123,49 @@ class faust2chEqHshelf : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec1[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } - for (int l2 = 0; (l2 < 3); l2 = (l2 + 1)) { - fRec0[l2] = 0.0; + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0; } for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + fRec4[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec4[l4] = 0.0; + fVec1[l4] = 0.0; } for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { fRec5[l5] = 0.0; } - for (int l6 = 0; (l6 < 3); l6 = (l6 + 1)) { - fRec6[l6] = 0.0; + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec6[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec1[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec0[l9] = 0.0; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec3[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fVec4[l11] = 0.0; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fVec5[l12] = 0.0; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec8[l13] = 0.0; + } + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fRec7[l14] = 0.0; } } @@ -175,33 +207,47 @@ class faust2chEqHshelf : public sfzFilterDsp { double fSlow9 = (fSlow3 * fSlow6); double fSlow10 = ((fSlow1 + fSlow8) + (1.0 - fSlow9)); double fSlow11 = (1.0 - fSlow0); - double fSlow12 = ((2.0 * ((fSlow1 + (-1.0 - fSlow4)) / fSlow10)) * fSlow11); + double fSlow12 = ((((0.0 - (2.0 * fSlow1)) * ((fSlow1 + fSlow4) + -1.0)) / fSlow10) * fSlow11); double fSlow13 = (fSlow9 + fSlow8); - double fSlow14 = (((fSlow1 + (1.0 - fSlow13)) / fSlow10) * fSlow11); - double fSlow15 = (((fSlow1 * ((fSlow1 + fSlow13) + 1.0)) / fSlow10) * fSlow11); - double fSlow16 = ((((0.0 - (2.0 * fSlow1)) * ((fSlow1 + fSlow4) + -1.0)) / fSlow10) * fSlow11); - double fSlow17 = (((fSlow1 * ((fSlow1 + fSlow9) + (1.0 - fSlow8))) / fSlow10) * fSlow11); + double fSlow14 = (((fSlow1 * ((fSlow1 + fSlow13) + 1.0)) / fSlow10) * fSlow11); + double fSlow15 = (((fSlow1 * ((fSlow1 + fSlow9) + (1.0 - fSlow8))) / fSlow10) * fSlow11); + double fSlow16 = (((fSlow1 + (1.0 - fSlow13)) / fSlow10) * fSlow11); + double fSlow17 = ((2.0 * ((fSlow1 + (-1.0 - fSlow4)) / fSlow10)) * fSlow11); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); - fRec1[0] = ((fSlow0 * fRec1[1]) + fSlow12); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow14); - fRec0[0] = (fTemp0 - ((fRec1[0] * fRec0[1]) + (fRec2[0] * fRec0[2]))); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow15); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow16); - fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow17); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec3[0]) + (fRec4[0] * fRec0[1])) + (fRec5[0] * fRec0[2]))); - fRec6[0] = (fTemp1 - ((fRec1[0] * fRec6[1]) + (fRec2[0] * fRec6[2]))); - output1[i] = FAUSTFLOAT((((fRec3[0] * fRec6[0]) + (fRec4[0] * fRec6[1])) + (fRec5[0] * fRec6[2]))); - fRec1[1] = fRec1[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow12); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow14); + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow15); + fVec1[0] = (fTemp0 * fRec4[0]); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow16); + fVec2[0] = (fVec1[1] - (fRec5[0] * fRec0[1])); + fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow17); + fRec1[0] = ((fVec0[1] + ((fTemp0 * fRec3[0]) + fVec2[1])) - (fRec6[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); + fVec3[0] = (fTemp1 * fRec2[0]); + fVec4[0] = (fTemp1 * fRec4[0]); + fVec5[0] = (fVec4[1] - (fRec5[0] * fRec7[1])); + fRec8[0] = ((fVec3[1] + ((fTemp1 * fRec3[0]) + fVec5[1])) - (fRec6[0] * fRec8[1])); + fRec7[0] = fRec8[0]; + output1[i] = FAUSTFLOAT(fRec7[0]); fRec2[1] = fRec2[0]; - fRec0[2] = fRec0[1]; - fRec0[1] = fRec0[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; fRec4[1] = fRec4[0]; + fVec1[1] = fVec1[0]; fRec5[1] = fRec5[0]; - fRec6[2] = fRec6[1]; + fVec2[1] = fVec2[0]; fRec6[1] = fRec6[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; + fRec8[1] = fRec8[0]; + fRec7[1] = fRec7[0]; } } diff --git a/src/sfizz/gen/filters/sfz2chEqLshelf.cxx b/src/sfizz/gen/filters/sfz2chEqLshelf.cxx index 3b69cf2e1..425697702 100644 --- a/src/sfizz/gen/filters/sfz2chEqLshelf.cxx +++ b/src/sfizz/gen/filters/sfz2chEqLshelf.cxx @@ -41,13 +41,21 @@ class faust2chEqLshelf : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fBandwidth; - double fRec1[2]; double fRec2[2]; - double fRec0[3]; + double fVec0[2]; double fRec3[2]; double fRec4[2]; + double fVec1[2]; double fRec5[2]; - double fRec6[3]; + double fVec2[2]; + double fRec6[2]; + double fRec1[2]; + double fRec0[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec8[2]; + double fRec7[2]; public: @@ -115,25 +123,49 @@ class faust2chEqLshelf : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec1[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } - for (int l2 = 0; (l2 < 3); l2 = (l2 + 1)) { - fRec0[l2] = 0.0; + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0; } for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + fRec4[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec4[l4] = 0.0; + fVec1[l4] = 0.0; } for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { fRec5[l5] = 0.0; } - for (int l6 = 0; (l6 < 3); l6 = (l6 + 1)) { - fRec6[l6] = 0.0; + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec6[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec1[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec0[l9] = 0.0; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec3[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fVec4[l11] = 0.0; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fVec5[l12] = 0.0; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec8[l13] = 0.0; + } + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fRec7[l14] = 0.0; } } @@ -176,32 +208,46 @@ class faust2chEqLshelf : public sfzFilterDsp { double fSlow10 = (fSlow6 + fSlow9); double fSlow11 = ((fSlow1 + fSlow10) + 1.0); double fSlow12 = (1.0 - fSlow0); - double fSlow13 = (((0.0 - (2.0 * ((fSlow1 + fSlow4) + -1.0))) / fSlow11) * fSlow12); - double fSlow14 = ((((fSlow1 + fSlow6) + (1.0 - fSlow9)) / fSlow11) * fSlow12); - double fSlow15 = (((fSlow1 * ((fSlow1 + fSlow9) + (1.0 - fSlow6))) / fSlow11) * fSlow12); - double fSlow16 = ((2.0 * ((fSlow1 * (fSlow1 + (-1.0 - fSlow4))) / fSlow11)) * fSlow12); - double fSlow17 = (((fSlow1 * (fSlow1 + (1.0 - fSlow10))) / fSlow11) * fSlow12); + double fSlow13 = ((2.0 * ((fSlow1 * (fSlow1 + (-1.0 - fSlow4))) / fSlow11)) * fSlow12); + double fSlow14 = (((fSlow1 * ((fSlow1 + fSlow9) + (1.0 - fSlow6))) / fSlow11) * fSlow12); + double fSlow15 = (((fSlow1 * (fSlow1 + (1.0 - fSlow10))) / fSlow11) * fSlow12); + double fSlow16 = ((((fSlow1 + fSlow6) + (1.0 - fSlow9)) / fSlow11) * fSlow12); + double fSlow17 = (((0.0 - (2.0 * ((fSlow1 + fSlow4) + -1.0))) / fSlow11) * fSlow12); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); - fRec1[0] = ((fSlow0 * fRec1[1]) + fSlow13); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow14); - fRec0[0] = (fTemp0 - ((fRec1[0] * fRec0[1]) + (fRec2[0] * fRec0[2]))); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow15); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow16); - fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow17); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec3[0]) + (fRec4[0] * fRec0[1])) + (fRec5[0] * fRec0[2]))); - fRec6[0] = (fTemp1 - ((fRec1[0] * fRec6[1]) + (fRec2[0] * fRec6[2]))); - output1[i] = FAUSTFLOAT((((fRec3[0] * fRec6[0]) + (fRec4[0] * fRec6[1])) + (fRec5[0] * fRec6[2]))); - fRec1[1] = fRec1[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow13); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow14); + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow15); + fVec1[0] = (fTemp0 * fRec4[0]); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow16); + fVec2[0] = (fVec1[1] - (fRec5[0] * fRec0[1])); + fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow17); + fRec1[0] = ((fVec0[1] + ((fTemp0 * fRec3[0]) + fVec2[1])) - (fRec6[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); + fVec3[0] = (fTemp1 * fRec2[0]); + fVec4[0] = (fTemp1 * fRec4[0]); + fVec5[0] = (fVec4[1] - (fRec5[0] * fRec7[1])); + fRec8[0] = ((fVec3[1] + ((fTemp1 * fRec3[0]) + fVec5[1])) - (fRec6[0] * fRec8[1])); + fRec7[0] = fRec8[0]; + output1[i] = FAUSTFLOAT(fRec7[0]); fRec2[1] = fRec2[0]; - fRec0[2] = fRec0[1]; - fRec0[1] = fRec0[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; fRec4[1] = fRec4[0]; + fVec1[1] = fVec1[0]; fRec5[1] = fRec5[0]; - fRec6[2] = fRec6[1]; + fVec2[1] = fVec2[0]; fRec6[1] = fRec6[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; + fRec8[1] = fRec8[0]; + fRec7[1] = fRec7[0]; } } diff --git a/src/sfizz/gen/filters/sfz2chEqPeak.cxx b/src/sfizz/gen/filters/sfz2chEqPeak.cxx index 8b653cbe4..9a3e28a34 100644 --- a/src/sfizz/gen/filters/sfz2chEqPeak.cxx +++ b/src/sfizz/gen/filters/sfz2chEqPeak.cxx @@ -40,12 +40,20 @@ class faust2chEqPeak : public sfzFilterDsp { double fConst3; FAUSTFLOAT fBandwidth; FAUSTFLOAT fPkShGain; - double fRec1[2]; double fRec2[2]; - double fRec0[3]; + double fVec0[2]; double fRec3[2]; double fRec4[2]; - double fRec5[3]; + double fVec1[2]; + double fRec5[2]; + double fVec2[2]; + double fRec1[2]; + double fRec0[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec7[2]; + double fRec6[2]; public: @@ -114,23 +122,47 @@ class faust2chEqPeak : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec1[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } - for (int l2 = 0; (l2 < 3); l2 = (l2 + 1)) { - fRec0[l2] = 0.0; + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0; } for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + fRec4[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec4[l4] = 0.0; + fVec1[l4] = 0.0; } - for (int l5 = 0; (l5 < 3); l5 = (l5 + 1)) { + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { fRec5[l5] = 0.0; } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec1[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec0[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fVec3[l9] = 0.0; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec4[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fVec5[l11] = 0.0; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fRec7[l12] = 0.0; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec6[l13] = 0.0; + } } virtual void init(int sample_rate) { @@ -169,31 +201,43 @@ class faust2chEqPeak : public sfzFilterDsp { double fSlow7 = (fSlow6 + 1.0); double fSlow8 = (1.0 - fSlow0); double fSlow9 = (((0.0 - (2.0 * std::cos(fSlow2))) / fSlow7) * fSlow8); - double fSlow10 = (((1.0 - fSlow6) / fSlow7) * fSlow8); - double fSlow11 = (0.5 * ((fSlow3 * fSlow5) / fSlow4)); - double fSlow12 = (((fSlow11 + 1.0) / fSlow7) * fSlow8); - double fSlow13 = (((1.0 - fSlow11) / fSlow7) * fSlow8); + double fSlow10 = (0.5 * ((fSlow3 * fSlow5) / fSlow4)); + double fSlow11 = (((fSlow10 + 1.0) / fSlow7) * fSlow8); + double fSlow12 = (((1.0 - fSlow10) / fSlow7) * fSlow8); + double fSlow13 = (((1.0 - fSlow6) / fSlow7) * fSlow8); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); - fRec1[0] = ((fSlow0 * fRec1[1]) + fSlow9); - double fTemp2 = (fRec1[0] * fRec0[1]); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow10); - fRec0[0] = (fTemp0 - (fTemp2 + (fRec2[0] * fRec0[2]))); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow12); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow13); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec3[0]) + fTemp2) + (fRec4[0] * fRec0[2]))); - double fTemp3 = (fRec1[0] * fRec5[1]); - fRec5[0] = (fTemp1 - (fTemp3 + (fRec2[0] * fRec5[2]))); - output1[i] = FAUSTFLOAT(((fTemp3 + (fRec3[0] * fRec5[0])) + (fRec4[0] * fRec5[2]))); - fRec1[1] = fRec1[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow9); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow11); + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow12); + fVec1[0] = (fTemp0 * fRec4[0]); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow13); + fVec2[0] = (fVec1[1] - (fRec5[0] * fRec0[1])); + fRec1[0] = ((fVec0[1] + ((fTemp0 * fRec3[0]) + fVec2[1])) - (fRec2[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); + fVec3[0] = (fTemp1 * fRec2[0]); + fVec4[0] = (fTemp1 * fRec4[0]); + fVec5[0] = (fVec4[1] - (fRec5[0] * fRec6[1])); + fRec7[0] = ((fVec3[1] + ((fTemp1 * fRec3[0]) + fVec5[1])) - (fRec2[0] * fRec7[1])); + fRec6[0] = fRec7[0]; + output1[i] = FAUSTFLOAT(fRec6[0]); fRec2[1] = fRec2[0]; - fRec0[2] = fRec0[1]; - fRec0[1] = fRec0[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; fRec4[1] = fRec4[0]; - fRec5[2] = fRec5[1]; + fVec1[1] = fVec1[0]; fRec5[1] = fRec5[0]; + fVec2[1] = fVec2[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; + fRec7[1] = fRec7[0]; + fRec6[1] = fRec6[0]; } } diff --git a/src/sfizz/gen/filters/sfz2chHpf2p.cxx b/src/sfizz/gen/filters/sfz2chHpf2p.cxx index 8e7368864..0906a3b17 100644 --- a/src/sfizz/gen/filters/sfz2chHpf2p.cxx +++ b/src/sfizz/gen/filters/sfz2chHpf2p.cxx @@ -37,12 +37,20 @@ class faust2chHpf2p : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec0[2]; double fRec2[2]; + double fVec0[2]; double fRec3[2]; - double fRec1[3]; + double fVec1[2]; double fRec4[2]; - double fRec5[3]; + double fVec2[2]; + double fRec5[2]; + double fRec1[2]; + double fRec0[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec7[2]; + double fRec6[2]; public: @@ -109,22 +117,46 @@ class faust2chHpf2p : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec0[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { fRec3[l2] = 0.0; } - for (int l3 = 0; (l3 < 3); l3 = (l3 + 1)) { - fRec1[l3] = 0.0; + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fVec1[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { fRec4[l4] = 0.0; } - for (int l5 = 0; (l5 < 3); l5 = (l5 + 1)) { - fRec5[l5] = 0.0; + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fVec2[l5] = 0.0; + } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fRec5[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec1[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec0[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fVec3[l9] = 0.0; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec4[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fVec5[l11] = 0.0; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fRec7[l12] = 0.0; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec6[l13] = 0.0; } } @@ -161,28 +193,44 @@ class faust2chHpf2p : public sfzFilterDsp { double fSlow4 = (fSlow3 + 1.0); double fSlow5 = (1.0 - fSlow0); double fSlow6 = (((-1.0 - fSlow2) / fSlow4) * fSlow5); - double fSlow7 = (((0.0 - (2.0 * fSlow2)) / fSlow4) * fSlow5); + double fSlow7 = ((0.5 * ((fSlow2 + 1.0) / fSlow4)) * fSlow5); double fSlow8 = (((1.0 - fSlow3) / fSlow4) * fSlow5); - double fSlow9 = ((0.5 * ((fSlow2 + 1.0) / fSlow4)) * fSlow5); + double fSlow9 = (((0.0 - (2.0 * fSlow2)) / fSlow4) * fSlow5); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); - fRec0[0] = ((fSlow0 * fRec0[1]) + fSlow6); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow7); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow8); - fRec1[0] = (fTemp0 - ((fRec2[0] * fRec1[1]) + (fRec3[0] * fRec1[2]))); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow9); - output0[i] = FAUSTFLOAT(((fRec0[0] * fRec1[1]) + (fRec4[0] * (fRec1[0] + fRec1[2])))); - fRec5[0] = (fTemp1 - ((fRec2[0] * fRec5[1]) + (fRec3[0] * fRec5[2]))); - output1[i] = FAUSTFLOAT(((fRec0[0] * fRec5[1]) + (fRec4[0] * (fRec5[0] + fRec5[2])))); - fRec0[1] = fRec0[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow6); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow7); + double fTemp2 = (fTemp0 * fRec3[0]); + fVec1[0] = fTemp2; + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow8); + fVec2[0] = (fVec1[1] - (fRec4[0] * fRec0[1])); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow9); + fRec1[0] = ((fVec0[1] + (fTemp2 + fVec2[1])) - (fRec5[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); + fVec3[0] = (fTemp1 * fRec2[0]); + double fTemp3 = (fTemp1 * fRec3[0]); + fVec4[0] = fTemp3; + fVec5[0] = (fVec4[1] - (fRec4[0] * fRec6[1])); + fRec7[0] = ((fVec3[1] + (fTemp3 + fVec5[1])) - (fRec5[0] * fRec7[1])); + fRec6[0] = fRec7[0]; + output1[i] = FAUSTFLOAT(fRec6[0]); fRec2[1] = fRec2[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; - fRec1[2] = fRec1[1]; - fRec1[1] = fRec1[0]; + fVec1[1] = fVec1[0]; fRec4[1] = fRec4[0]; - fRec5[2] = fRec5[1]; + fVec2[1] = fVec2[0]; fRec5[1] = fRec5[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; + fRec7[1] = fRec7[0]; + fRec6[1] = fRec6[0]; } } diff --git a/src/sfizz/gen/filters/sfz2chHpf4p.cxx b/src/sfizz/gen/filters/sfz2chHpf4p.cxx index fe4145c45..87cf14ee1 100644 --- a/src/sfizz/gen/filters/sfz2chHpf4p.cxx +++ b/src/sfizz/gen/filters/sfz2chHpf4p.cxx @@ -37,14 +37,30 @@ class faust2chHpf4p : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec0[2]; - double fRec3[2]; - double fRec4[2]; - double fRec2[3]; + double fRec2[2]; + double fVec0[2]; double fRec5[2]; - double fRec1[3]; - double fRec7[3]; - double fRec6[3]; + double fVec1[2]; + double fRec6[2]; + double fVec2[2]; + double fRec7[2]; + double fRec4[2]; + double fRec3[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec1[2]; + double fRec0[2]; + double fVec6[2]; + double fVec7[2]; + double fVec8[2]; + double fRec11[2]; + double fRec10[2]; + double fVec9[2]; + double fVec10[2]; + double fVec11[2]; + double fRec9[2]; + double fRec8[2]; public: @@ -111,28 +127,76 @@ class faust2chHpf4p : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec0[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec3[l1] = 0.0; + fVec0[l1] = 0.0; } for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { - fRec4[l2] = 0.0; + fRec5[l2] = 0.0; } - for (int l3 = 0; (l3 < 3); l3 = (l3 + 1)) { - fRec2[l3] = 0.0; + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fVec1[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec5[l4] = 0.0; + fRec6[l4] = 0.0; } - for (int l5 = 0; (l5 < 3); l5 = (l5 + 1)) { - fRec1[l5] = 0.0; + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fVec2[l5] = 0.0; } - for (int l6 = 0; (l6 < 3); l6 = (l6 + 1)) { + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { fRec7[l6] = 0.0; } - for (int l7 = 0; (l7 < 3); l7 = (l7 + 1)) { - fRec6[l7] = 0.0; + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec4[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec3[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fVec3[l9] = 0.0; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec4[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fVec5[l11] = 0.0; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fRec1[l12] = 0.0; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec0[l13] = 0.0; + } + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fVec6[l14] = 0.0; + } + for (int l15 = 0; (l15 < 2); l15 = (l15 + 1)) { + fVec7[l15] = 0.0; + } + for (int l16 = 0; (l16 < 2); l16 = (l16 + 1)) { + fVec8[l16] = 0.0; + } + for (int l17 = 0; (l17 < 2); l17 = (l17 + 1)) { + fRec11[l17] = 0.0; + } + for (int l18 = 0; (l18 < 2); l18 = (l18 + 1)) { + fRec10[l18] = 0.0; + } + for (int l19 = 0; (l19 < 2); l19 = (l19 + 1)) { + fVec9[l19] = 0.0; + } + for (int l20 = 0; (l20 < 2); l20 = (l20 + 1)) { + fVec10[l20] = 0.0; + } + for (int l21 = 0; (l21 < 2); l21 = (l21 + 1)) { + fVec11[l21] = 0.0; + } + for (int l22 = 0; (l22 < 2); l22 = (l22 + 1)) { + fRec9[l22] = 0.0; + } + for (int l23 = 0; (l23 < 2); l23 = (l23 + 1)) { + fRec8[l23] = 0.0; } } @@ -169,34 +233,66 @@ class faust2chHpf4p : public sfzFilterDsp { double fSlow4 = (fSlow3 + 1.0); double fSlow5 = (1.0 - fSlow0); double fSlow6 = (((-1.0 - fSlow2) / fSlow4) * fSlow5); - double fSlow7 = (((0.0 - (2.0 * fSlow2)) / fSlow4) * fSlow5); + double fSlow7 = ((0.5 * ((fSlow2 + 1.0) / fSlow4)) * fSlow5); double fSlow8 = (((1.0 - fSlow3) / fSlow4) * fSlow5); - double fSlow9 = ((0.5 * ((fSlow2 + 1.0) / fSlow4)) * fSlow5); + double fSlow9 = (((0.0 - (2.0 * fSlow2)) / fSlow4) * fSlow5); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); - fRec0[0] = ((fSlow0 * fRec0[1]) + fSlow6); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow7); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow8); - fRec2[0] = (fTemp0 - ((fRec3[0] * fRec2[1]) + (fRec4[0] * fRec2[2]))); - fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow9); - fRec1[0] = (((fRec0[0] * fRec2[1]) + (fRec5[0] * (fRec2[0] + fRec2[2]))) - ((fRec3[0] * fRec1[1]) + (fRec4[0] * fRec1[2]))); - output0[i] = FAUSTFLOAT(((fRec0[0] * fRec1[1]) + (fRec5[0] * (fRec1[0] + fRec1[2])))); - fRec7[0] = (fTemp1 - ((fRec3[0] * fRec7[1]) + (fRec4[0] * fRec7[2]))); - fRec6[0] = (((fRec0[0] * fRec7[1]) + (fRec5[0] * (fRec7[0] + fRec7[2]))) - ((fRec3[0] * fRec6[1]) + (fRec4[0] * fRec6[2]))); - output1[i] = FAUSTFLOAT(((fRec0[0] * fRec6[1]) + (fRec5[0] * (fRec6[0] + fRec6[2])))); - fRec0[1] = fRec0[0]; - fRec3[1] = fRec3[0]; - fRec4[1] = fRec4[0]; - fRec2[2] = fRec2[1]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow6); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow7); + double fTemp2 = (fTemp0 * fRec5[0]); + fVec1[0] = fTemp2; + fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow8); + fVec2[0] = (fVec1[1] - (fRec6[0] * fRec3[1])); + fRec7[0] = ((fSlow0 * fRec7[1]) + fSlow9); + fRec4[0] = ((fVec0[1] + (fTemp2 + fVec2[1])) - (fRec7[0] * fRec4[1])); + fRec3[0] = fRec4[0]; + fVec3[0] = (fRec2[0] * fRec3[0]); + double fTemp3 = (fRec5[0] * fRec3[0]); + fVec4[0] = fTemp3; + fVec5[0] = (fVec4[1] - (fRec6[0] * fRec0[1])); + fRec1[0] = ((fVec3[1] + (fTemp3 + fVec5[1])) - (fRec7[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); + fVec6[0] = (fTemp1 * fRec2[0]); + double fTemp4 = (fTemp1 * fRec5[0]); + fVec7[0] = fTemp4; + fVec8[0] = (fVec7[1] - (fRec6[0] * fRec10[1])); + fRec11[0] = ((fVec6[1] + (fTemp4 + fVec8[1])) - (fRec7[0] * fRec11[1])); + fRec10[0] = fRec11[0]; + fVec9[0] = (fRec2[0] * fRec10[0]); + double fTemp5 = (fRec5[0] * fRec10[0]); + fVec10[0] = fTemp5; + fVec11[0] = (fVec10[1] - (fRec6[0] * fRec8[1])); + fRec9[0] = ((fVec9[1] + (fTemp5 + fVec11[1])) - (fRec7[0] * fRec9[1])); + fRec8[0] = fRec9[0]; + output1[i] = FAUSTFLOAT(fRec8[0]); fRec2[1] = fRec2[0]; + fVec0[1] = fVec0[0]; fRec5[1] = fRec5[0]; - fRec1[2] = fRec1[1]; - fRec1[1] = fRec1[0]; - fRec7[2] = fRec7[1]; - fRec7[1] = fRec7[0]; - fRec6[2] = fRec6[1]; + fVec1[1] = fVec1[0]; fRec6[1] = fRec6[0]; + fVec2[1] = fVec2[0]; + fRec7[1] = fRec7[0]; + fRec4[1] = fRec4[0]; + fRec3[1] = fRec3[0]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; + fVec6[1] = fVec6[0]; + fVec7[1] = fVec7[0]; + fVec8[1] = fVec8[0]; + fRec11[1] = fRec11[0]; + fRec10[1] = fRec10[0]; + fVec9[1] = fVec9[0]; + fVec10[1] = fVec10[0]; + fVec11[1] = fVec11[0]; + fRec9[1] = fRec9[0]; + fRec8[1] = fRec8[0]; } } diff --git a/src/sfizz/gen/filters/sfz2chHpf6p.cxx b/src/sfizz/gen/filters/sfz2chHpf6p.cxx index 9a309db16..dcdd4ffb3 100644 --- a/src/sfizz/gen/filters/sfz2chHpf6p.cxx +++ b/src/sfizz/gen/filters/sfz2chHpf6p.cxx @@ -37,16 +37,40 @@ class faust2chHpf6p : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec0[2]; - double fRec4[2]; - double fRec5[2]; - double fRec3[3]; + double fRec2[2]; + double fVec0[2]; + double fRec7[2]; + double fVec1[2]; + double fRec8[2]; + double fVec2[2]; + double fRec9[2]; double fRec6[2]; - double fRec2[3]; - double fRec1[3]; - double fRec9[3]; - double fRec8[3]; - double fRec7[3]; + double fRec5[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec4[2]; + double fRec3[2]; + double fVec6[2]; + double fVec7[2]; + double fVec8[2]; + double fRec1[2]; + double fRec0[2]; + double fVec9[2]; + double fVec10[2]; + double fVec11[2]; + double fRec15[2]; + double fRec14[2]; + double fVec12[2]; + double fVec13[2]; + double fVec14[2]; + double fRec13[2]; + double fRec12[2]; + double fVec15[2]; + double fVec16[2]; + double fVec17[2]; + double fRec11[2]; + double fRec10[2]; public: @@ -113,34 +137,106 @@ class faust2chHpf6p : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec0[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec4[l1] = 0.0; + fVec0[l1] = 0.0; } for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { - fRec5[l2] = 0.0; + fRec7[l2] = 0.0; } - for (int l3 = 0; (l3 < 3); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fVec1[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec6[l4] = 0.0; + fRec8[l4] = 0.0; + } + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fVec2[l5] = 0.0; + } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fRec9[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec6[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec5[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fVec3[l9] = 0.0; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec4[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fVec5[l11] = 0.0; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fRec4[l12] = 0.0; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec3[l13] = 0.0; + } + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fVec6[l14] = 0.0; + } + for (int l15 = 0; (l15 < 2); l15 = (l15 + 1)) { + fVec7[l15] = 0.0; + } + for (int l16 = 0; (l16 < 2); l16 = (l16 + 1)) { + fVec8[l16] = 0.0; + } + for (int l17 = 0; (l17 < 2); l17 = (l17 + 1)) { + fRec1[l17] = 0.0; + } + for (int l18 = 0; (l18 < 2); l18 = (l18 + 1)) { + fRec0[l18] = 0.0; + } + for (int l19 = 0; (l19 < 2); l19 = (l19 + 1)) { + fVec9[l19] = 0.0; + } + for (int l20 = 0; (l20 < 2); l20 = (l20 + 1)) { + fVec10[l20] = 0.0; } - for (int l5 = 0; (l5 < 3); l5 = (l5 + 1)) { - fRec2[l5] = 0.0; + for (int l21 = 0; (l21 < 2); l21 = (l21 + 1)) { + fVec11[l21] = 0.0; } - for (int l6 = 0; (l6 < 3); l6 = (l6 + 1)) { - fRec1[l6] = 0.0; + for (int l22 = 0; (l22 < 2); l22 = (l22 + 1)) { + fRec15[l22] = 0.0; } - for (int l7 = 0; (l7 < 3); l7 = (l7 + 1)) { - fRec9[l7] = 0.0; + for (int l23 = 0; (l23 < 2); l23 = (l23 + 1)) { + fRec14[l23] = 0.0; } - for (int l8 = 0; (l8 < 3); l8 = (l8 + 1)) { - fRec8[l8] = 0.0; + for (int l24 = 0; (l24 < 2); l24 = (l24 + 1)) { + fVec12[l24] = 0.0; } - for (int l9 = 0; (l9 < 3); l9 = (l9 + 1)) { - fRec7[l9] = 0.0; + for (int l25 = 0; (l25 < 2); l25 = (l25 + 1)) { + fVec13[l25] = 0.0; + } + for (int l26 = 0; (l26 < 2); l26 = (l26 + 1)) { + fVec14[l26] = 0.0; + } + for (int l27 = 0; (l27 < 2); l27 = (l27 + 1)) { + fRec13[l27] = 0.0; + } + for (int l28 = 0; (l28 < 2); l28 = (l28 + 1)) { + fRec12[l28] = 0.0; + } + for (int l29 = 0; (l29 < 2); l29 = (l29 + 1)) { + fVec15[l29] = 0.0; + } + for (int l30 = 0; (l30 < 2); l30 = (l30 + 1)) { + fVec16[l30] = 0.0; + } + for (int l31 = 0; (l31 < 2); l31 = (l31 + 1)) { + fVec17[l31] = 0.0; + } + for (int l32 = 0; (l32 < 2); l32 = (l32 + 1)) { + fRec11[l32] = 0.0; + } + for (int l33 = 0; (l33 < 2); l33 = (l33 + 1)) { + fRec10[l33] = 0.0; } } @@ -177,40 +273,88 @@ class faust2chHpf6p : public sfzFilterDsp { double fSlow4 = (fSlow3 + 1.0); double fSlow5 = (1.0 - fSlow0); double fSlow6 = (((-1.0 - fSlow2) / fSlow4) * fSlow5); - double fSlow7 = (((0.0 - (2.0 * fSlow2)) / fSlow4) * fSlow5); + double fSlow7 = ((0.5 * ((fSlow2 + 1.0) / fSlow4)) * fSlow5); double fSlow8 = (((1.0 - fSlow3) / fSlow4) * fSlow5); - double fSlow9 = ((0.5 * ((fSlow2 + 1.0) / fSlow4)) * fSlow5); + double fSlow9 = (((0.0 - (2.0 * fSlow2)) / fSlow4) * fSlow5); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); - fRec0[0] = ((fSlow0 * fRec0[1]) + fSlow6); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow7); - fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow8); - fRec3[0] = (fTemp0 - ((fRec4[0] * fRec3[1]) + (fRec5[0] * fRec3[2]))); - fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow9); - fRec2[0] = (((fRec0[0] * fRec3[1]) + (fRec6[0] * (fRec3[0] + fRec3[2]))) - ((fRec4[0] * fRec2[1]) + (fRec5[0] * fRec2[2]))); - fRec1[0] = (((fRec0[0] * fRec2[1]) + (fRec6[0] * (fRec2[0] + fRec2[2]))) - ((fRec4[0] * fRec1[1]) + (fRec5[0] * fRec1[2]))); - output0[i] = FAUSTFLOAT(((fRec0[0] * fRec1[1]) + (fRec6[0] * (fRec1[0] + fRec1[2])))); - fRec9[0] = (fTemp1 - ((fRec4[0] * fRec9[1]) + (fRec5[0] * fRec9[2]))); - fRec8[0] = (((fRec0[0] * fRec9[1]) + (fRec6[0] * (fRec9[0] + fRec9[2]))) - ((fRec4[0] * fRec8[1]) + (fRec5[0] * fRec8[2]))); - fRec7[0] = (((fRec0[0] * fRec8[1]) + (fRec6[0] * (fRec8[0] + fRec8[2]))) - ((fRec4[0] * fRec7[1]) + (fRec5[0] * fRec7[2]))); - output1[i] = FAUSTFLOAT(((fRec0[0] * fRec7[1]) + (fRec6[0] * (fRec7[0] + fRec7[2])))); - fRec0[1] = fRec0[0]; - fRec4[1] = fRec4[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow6); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec7[0] = ((fSlow0 * fRec7[1]) + fSlow7); + double fTemp2 = (fTemp0 * fRec7[0]); + fVec1[0] = fTemp2; + fRec8[0] = ((fSlow0 * fRec8[1]) + fSlow8); + fVec2[0] = (fVec1[1] - (fRec8[0] * fRec5[1])); + fRec9[0] = ((fSlow0 * fRec9[1]) + fSlow9); + fRec6[0] = ((fVec0[1] + (fTemp2 + fVec2[1])) - (fRec9[0] * fRec6[1])); + fRec5[0] = fRec6[0]; + fVec3[0] = (fRec2[0] * fRec5[0]); + double fTemp3 = (fRec7[0] * fRec5[0]); + fVec4[0] = fTemp3; + fVec5[0] = (fVec4[1] - (fRec8[0] * fRec3[1])); + fRec4[0] = ((fVec3[1] + (fTemp3 + fVec5[1])) - (fRec9[0] * fRec4[1])); + fRec3[0] = fRec4[0]; + fVec6[0] = (fRec2[0] * fRec3[0]); + double fTemp4 = (fRec7[0] * fRec3[0]); + fVec7[0] = fTemp4; + fVec8[0] = (fVec7[1] - (fRec8[0] * fRec0[1])); + fRec1[0] = ((fVec6[1] + (fTemp4 + fVec8[1])) - (fRec9[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); + fVec9[0] = (fTemp1 * fRec2[0]); + double fTemp5 = (fTemp1 * fRec7[0]); + fVec10[0] = fTemp5; + fVec11[0] = (fVec10[1] - (fRec8[0] * fRec14[1])); + fRec15[0] = ((fVec9[1] + (fTemp5 + fVec11[1])) - (fRec9[0] * fRec15[1])); + fRec14[0] = fRec15[0]; + fVec12[0] = (fRec2[0] * fRec14[0]); + double fTemp6 = (fRec7[0] * fRec14[0]); + fVec13[0] = fTemp6; + fVec14[0] = (fVec13[1] - (fRec8[0] * fRec12[1])); + fRec13[0] = ((fVec12[1] + (fTemp6 + fVec14[1])) - (fRec9[0] * fRec13[1])); + fRec12[0] = fRec13[0]; + fVec15[0] = (fRec2[0] * fRec12[0]); + double fTemp7 = (fRec7[0] * fRec12[0]); + fVec16[0] = fTemp7; + fVec17[0] = (fVec16[1] - (fRec8[0] * fRec10[1])); + fRec11[0] = ((fVec15[1] + (fTemp7 + fVec17[1])) - (fRec9[0] * fRec11[1])); + fRec10[0] = fRec11[0]; + output1[i] = FAUSTFLOAT(fRec10[0]); + fRec2[1] = fRec2[0]; + fVec0[1] = fVec0[0]; + fRec7[1] = fRec7[0]; + fVec1[1] = fVec1[0]; + fRec8[1] = fRec8[0]; + fVec2[1] = fVec2[0]; + fRec9[1] = fRec9[0]; + fRec6[1] = fRec6[0]; fRec5[1] = fRec5[0]; - fRec3[2] = fRec3[1]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; + fRec4[1] = fRec4[0]; fRec3[1] = fRec3[0]; - fRec6[1] = fRec6[0]; - fRec2[2] = fRec2[1]; - fRec2[1] = fRec2[0]; - fRec1[2] = fRec1[1]; + fVec6[1] = fVec6[0]; + fVec7[1] = fVec7[0]; + fVec8[1] = fVec8[0]; fRec1[1] = fRec1[0]; - fRec9[2] = fRec9[1]; - fRec9[1] = fRec9[0]; - fRec8[2] = fRec8[1]; - fRec8[1] = fRec8[0]; - fRec7[2] = fRec7[1]; - fRec7[1] = fRec7[0]; + fRec0[1] = fRec0[0]; + fVec9[1] = fVec9[0]; + fVec10[1] = fVec10[0]; + fVec11[1] = fVec11[0]; + fRec15[1] = fRec15[0]; + fRec14[1] = fRec14[0]; + fVec12[1] = fVec12[0]; + fVec13[1] = fVec13[0]; + fVec14[1] = fVec14[0]; + fRec13[1] = fRec13[0]; + fRec12[1] = fRec12[0]; + fVec15[1] = fVec15[0]; + fVec16[1] = fVec16[0]; + fVec17[1] = fVec17[0]; + fRec11[1] = fRec11[0]; + fRec10[1] = fRec10[0]; } } diff --git a/src/sfizz/gen/filters/sfz2chHsh.cxx b/src/sfizz/gen/filters/sfz2chHsh.cxx index 84d61a962..f58b9574b 100644 --- a/src/sfizz/gen/filters/sfz2chHsh.cxx +++ b/src/sfizz/gen/filters/sfz2chHsh.cxx @@ -38,13 +38,21 @@ class faust2chHsh : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec1[2]; double fRec2[2]; - double fRec0[3]; + double fVec0[2]; double fRec3[2]; double fRec4[2]; + double fVec1[2]; double fRec5[2]; - double fRec6[3]; + double fVec2[2]; + double fRec6[2]; + double fRec1[2]; + double fRec0[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec8[2]; + double fRec7[2]; public: @@ -112,25 +120,49 @@ class faust2chHsh : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec1[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } - for (int l2 = 0; (l2 < 3); l2 = (l2 + 1)) { - fRec0[l2] = 0.0; + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0; } for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + fRec4[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec4[l4] = 0.0; + fVec1[l4] = 0.0; } for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { fRec5[l5] = 0.0; } - for (int l6 = 0; (l6 < 3); l6 = (l6 + 1)) { - fRec6[l6] = 0.0; + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec6[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec1[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec0[l9] = 0.0; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec3[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fVec4[l11] = 0.0; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fVec5[l12] = 0.0; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec8[l13] = 0.0; + } + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fRec7[l14] = 0.0; } } @@ -169,33 +201,47 @@ class faust2chHsh : public sfzFilterDsp { double fSlow6 = (fSlow3 * (fSlow1 + -1.0)); double fSlow7 = ((fSlow1 + fSlow5) + (1.0 - fSlow6)); double fSlow8 = (1.0 - fSlow0); - double fSlow9 = ((2.0 * ((fSlow1 + (-1.0 - fSlow4)) / fSlow7)) * fSlow8); - double fSlow10 = (((fSlow1 + (1.0 - (fSlow5 + fSlow6))) / fSlow7) * fSlow8); - double fSlow11 = (fSlow1 + fSlow6); - double fSlow12 = (((fSlow1 * ((fSlow5 + fSlow11) + 1.0)) / fSlow7) * fSlow8); - double fSlow13 = ((((0.0 - (2.0 * fSlow1)) * ((fSlow1 + fSlow4) + -1.0)) / fSlow7) * fSlow8); - double fSlow14 = (((fSlow1 * (fSlow11 + (1.0 - fSlow5))) / fSlow7) * fSlow8); + double fSlow9 = ((((0.0 - (2.0 * fSlow1)) * ((fSlow1 + fSlow4) + -1.0)) / fSlow7) * fSlow8); + double fSlow10 = (fSlow1 + fSlow6); + double fSlow11 = (((fSlow1 * ((fSlow5 + fSlow10) + 1.0)) / fSlow7) * fSlow8); + double fSlow12 = (((fSlow1 * (fSlow10 + (1.0 - fSlow5))) / fSlow7) * fSlow8); + double fSlow13 = (((fSlow1 + (1.0 - (fSlow5 + fSlow6))) / fSlow7) * fSlow8); + double fSlow14 = ((2.0 * ((fSlow1 + (-1.0 - fSlow4)) / fSlow7)) * fSlow8); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); - fRec1[0] = ((fSlow0 * fRec1[1]) + fSlow9); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow10); - fRec0[0] = (fTemp0 - ((fRec1[0] * fRec0[1]) + (fRec2[0] * fRec0[2]))); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow12); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow13); - fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow14); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec3[0]) + (fRec4[0] * fRec0[1])) + (fRec5[0] * fRec0[2]))); - fRec6[0] = (fTemp1 - ((fRec1[0] * fRec6[1]) + (fRec2[0] * fRec6[2]))); - output1[i] = FAUSTFLOAT((((fRec3[0] * fRec6[0]) + (fRec4[0] * fRec6[1])) + (fRec5[0] * fRec6[2]))); - fRec1[1] = fRec1[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow9); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow11); + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow12); + fVec1[0] = (fTemp0 * fRec4[0]); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow13); + fVec2[0] = (fVec1[1] - (fRec5[0] * fRec0[1])); + fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow14); + fRec1[0] = ((fVec0[1] + ((fTemp0 * fRec3[0]) + fVec2[1])) - (fRec6[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); + fVec3[0] = (fTemp1 * fRec2[0]); + fVec4[0] = (fTemp1 * fRec4[0]); + fVec5[0] = (fVec4[1] - (fRec5[0] * fRec7[1])); + fRec8[0] = ((fVec3[1] + ((fTemp1 * fRec3[0]) + fVec5[1])) - (fRec6[0] * fRec8[1])); + fRec7[0] = fRec8[0]; + output1[i] = FAUSTFLOAT(fRec7[0]); fRec2[1] = fRec2[0]; - fRec0[2] = fRec0[1]; - fRec0[1] = fRec0[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; fRec4[1] = fRec4[0]; + fVec1[1] = fVec1[0]; fRec5[1] = fRec5[0]; - fRec6[2] = fRec6[1]; + fVec2[1] = fVec2[0]; fRec6[1] = fRec6[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; + fRec8[1] = fRec8[0]; + fRec7[1] = fRec7[0]; } } diff --git a/src/sfizz/gen/filters/sfz2chLsh.cxx b/src/sfizz/gen/filters/sfz2chLsh.cxx index 38f8f4ff1..ba8105de5 100644 --- a/src/sfizz/gen/filters/sfz2chLsh.cxx +++ b/src/sfizz/gen/filters/sfz2chLsh.cxx @@ -38,13 +38,21 @@ class faust2chLsh : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec1[2]; double fRec2[2]; - double fRec0[3]; + double fVec0[2]; double fRec3[2]; double fRec4[2]; + double fVec1[2]; double fRec5[2]; - double fRec6[3]; + double fVec2[2]; + double fRec6[2]; + double fRec1[2]; + double fRec0[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec8[2]; + double fRec7[2]; public: @@ -112,25 +120,49 @@ class faust2chLsh : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec1[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } - for (int l2 = 0; (l2 < 3); l2 = (l2 + 1)) { - fRec0[l2] = 0.0; + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0; } for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + fRec4[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec4[l4] = 0.0; + fVec1[l4] = 0.0; } for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { fRec5[l5] = 0.0; } - for (int l6 = 0; (l6 < 3); l6 = (l6 + 1)) { - fRec6[l6] = 0.0; + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec6[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec1[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec0[l9] = 0.0; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec3[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fVec4[l11] = 0.0; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fVec5[l12] = 0.0; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec8[l13] = 0.0; + } + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fRec7[l14] = 0.0; } } @@ -170,32 +202,46 @@ class faust2chLsh : public sfzFilterDsp { double fSlow7 = (fSlow1 + fSlow6); double fSlow8 = ((fSlow5 + fSlow7) + 1.0); double fSlow9 = (1.0 - fSlow0); - double fSlow10 = (((0.0 - (2.0 * ((fSlow1 + fSlow4) + -1.0))) / fSlow8) * fSlow9); - double fSlow11 = (((fSlow7 + (1.0 - fSlow5)) / fSlow8) * fSlow9); - double fSlow12 = (((fSlow1 * ((fSlow1 + fSlow5) + (1.0 - fSlow6))) / fSlow8) * fSlow9); - double fSlow13 = ((2.0 * ((fSlow1 * (fSlow1 + (-1.0 - fSlow4))) / fSlow8)) * fSlow9); - double fSlow14 = (((fSlow1 * (fSlow1 + (1.0 - (fSlow5 + fSlow6)))) / fSlow8) * fSlow9); + double fSlow10 = ((2.0 * ((fSlow1 * (fSlow1 + (-1.0 - fSlow4))) / fSlow8)) * fSlow9); + double fSlow11 = (((fSlow1 * ((fSlow1 + fSlow5) + (1.0 - fSlow6))) / fSlow8) * fSlow9); + double fSlow12 = (((fSlow1 * (fSlow1 + (1.0 - (fSlow5 + fSlow6)))) / fSlow8) * fSlow9); + double fSlow13 = (((fSlow7 + (1.0 - fSlow5)) / fSlow8) * fSlow9); + double fSlow14 = (((0.0 - (2.0 * ((fSlow1 + fSlow4) + -1.0))) / fSlow8) * fSlow9); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); - fRec1[0] = ((fSlow0 * fRec1[1]) + fSlow10); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow11); - fRec0[0] = (fTemp0 - ((fRec1[0] * fRec0[1]) + (fRec2[0] * fRec0[2]))); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow12); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow13); - fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow14); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec3[0]) + (fRec4[0] * fRec0[1])) + (fRec5[0] * fRec0[2]))); - fRec6[0] = (fTemp1 - ((fRec1[0] * fRec6[1]) + (fRec2[0] * fRec6[2]))); - output1[i] = FAUSTFLOAT((((fRec3[0] * fRec6[0]) + (fRec4[0] * fRec6[1])) + (fRec5[0] * fRec6[2]))); - fRec1[1] = fRec1[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow10); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow11); + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow12); + fVec1[0] = (fTemp0 * fRec4[0]); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow13); + fVec2[0] = (fVec1[1] - (fRec5[0] * fRec0[1])); + fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow14); + fRec1[0] = ((fVec0[1] + ((fTemp0 * fRec3[0]) + fVec2[1])) - (fRec6[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); + fVec3[0] = (fTemp1 * fRec2[0]); + fVec4[0] = (fTemp1 * fRec4[0]); + fVec5[0] = (fVec4[1] - (fRec5[0] * fRec7[1])); + fRec8[0] = ((fVec3[1] + ((fTemp1 * fRec3[0]) + fVec5[1])) - (fRec6[0] * fRec8[1])); + fRec7[0] = fRec8[0]; + output1[i] = FAUSTFLOAT(fRec7[0]); fRec2[1] = fRec2[0]; - fRec0[2] = fRec0[1]; - fRec0[1] = fRec0[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; fRec4[1] = fRec4[0]; + fVec1[1] = fVec1[0]; fRec5[1] = fRec5[0]; - fRec6[2] = fRec6[1]; + fVec2[1] = fVec2[0]; fRec6[1] = fRec6[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; + fRec8[1] = fRec8[0]; + fRec7[1] = fRec7[0]; } } diff --git a/src/sfizz/gen/filters/sfz2chPeq.cxx b/src/sfizz/gen/filters/sfz2chPeq.cxx index 886f3771d..482b2933e 100644 --- a/src/sfizz/gen/filters/sfz2chPeq.cxx +++ b/src/sfizz/gen/filters/sfz2chPeq.cxx @@ -38,12 +38,20 @@ class faust2chPeq : public sfzFilterDsp { FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; FAUSTFLOAT fPkShGain; - double fRec1[2]; double fRec2[2]; - double fRec0[3]; + double fVec0[2]; double fRec3[2]; double fRec4[2]; - double fRec5[3]; + double fVec1[2]; + double fRec5[2]; + double fVec2[2]; + double fRec1[2]; + double fRec0[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec7[2]; + double fRec6[2]; public: @@ -111,23 +119,47 @@ class faust2chPeq : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec1[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } - for (int l2 = 0; (l2 < 3); l2 = (l2 + 1)) { - fRec0[l2] = 0.0; + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0; } for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + fRec4[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec4[l4] = 0.0; + fVec1[l4] = 0.0; } - for (int l5 = 0; (l5 < 3); l5 = (l5 + 1)) { + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { fRec5[l5] = 0.0; } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec1[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec0[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fVec3[l9] = 0.0; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec4[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fVec5[l11] = 0.0; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fRec7[l12] = 0.0; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec6[l13] = 0.0; + } } virtual void init(int sample_rate) { @@ -165,31 +197,43 @@ class faust2chPeq : public sfzFilterDsp { double fSlow6 = (fSlow5 + 1.0); double fSlow7 = (1.0 - fSlow0); double fSlow8 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow6) * fSlow7); - double fSlow9 = (((1.0 - fSlow5) / fSlow6) * fSlow7); - double fSlow10 = (0.5 * ((fSlow2 * fSlow4) / fSlow3)); - double fSlow11 = (((fSlow10 + 1.0) / fSlow6) * fSlow7); - double fSlow12 = (((1.0 - fSlow10) / fSlow6) * fSlow7); + double fSlow9 = (0.5 * ((fSlow2 * fSlow4) / fSlow3)); + double fSlow10 = (((fSlow9 + 1.0) / fSlow6) * fSlow7); + double fSlow11 = (((1.0 - fSlow9) / fSlow6) * fSlow7); + double fSlow12 = (((1.0 - fSlow5) / fSlow6) * fSlow7); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); - fRec1[0] = ((fSlow0 * fRec1[1]) + fSlow8); - double fTemp2 = (fRec1[0] * fRec0[1]); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow9); - fRec0[0] = (fTemp0 - (fTemp2 + (fRec2[0] * fRec0[2]))); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow11); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow12); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec3[0]) + fTemp2) + (fRec4[0] * fRec0[2]))); - double fTemp3 = (fRec1[0] * fRec5[1]); - fRec5[0] = (fTemp1 - (fTemp3 + (fRec2[0] * fRec5[2]))); - output1[i] = FAUSTFLOAT(((fTemp3 + (fRec3[0] * fRec5[0])) + (fRec4[0] * fRec5[2]))); - fRec1[1] = fRec1[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow8); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow10); + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow11); + fVec1[0] = (fTemp0 * fRec4[0]); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow12); + fVec2[0] = (fVec1[1] - (fRec5[0] * fRec0[1])); + fRec1[0] = ((fVec0[1] + ((fTemp0 * fRec3[0]) + fVec2[1])) - (fRec2[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); + fVec3[0] = (fTemp1 * fRec2[0]); + fVec4[0] = (fTemp1 * fRec4[0]); + fVec5[0] = (fVec4[1] - (fRec5[0] * fRec6[1])); + fRec7[0] = ((fVec3[1] + ((fTemp1 * fRec3[0]) + fVec5[1])) - (fRec2[0] * fRec7[1])); + fRec6[0] = fRec7[0]; + output1[i] = FAUSTFLOAT(fRec6[0]); fRec2[1] = fRec2[0]; - fRec0[2] = fRec0[1]; - fRec0[1] = fRec0[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; fRec4[1] = fRec4[0]; - fRec5[2] = fRec5[1]; + fVec1[1] = fVec1[0]; fRec5[1] = fRec5[0]; + fVec2[1] = fVec2[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; + fRec7[1] = fRec7[0]; + fRec6[1] = fRec6[0]; } } diff --git a/src/sfizz/gen/filters/sfzBpf2p.cxx b/src/sfizz/gen/filters/sfzBpf2p.cxx index c609d6287..5068871c1 100644 --- a/src/sfizz/gen/filters/sfzBpf2p.cxx +++ b/src/sfizz/gen/filters/sfzBpf2p.cxx @@ -34,15 +34,19 @@ class faustBpf2p : public sfzFilterDsp { int fSampleRate; double fConst0; double fConst1; + double fRec2[2]; + double fVec0[2]; double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec1[2]; - double fRec2[2]; - double fRec0[3]; double fRec3[2]; double fRec4[2]; + double fVec1[2]; double fRec5[2]; + double fVec2[2]; + double fRec6[2]; + double fRec1[2]; + double fRec0[2]; public: @@ -101,23 +105,35 @@ class faustBpf2p : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec1[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } - for (int l2 = 0; (l2 < 3); l2 = (l2 + 1)) { - fRec0[l2] = 0.0; + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0; } for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + fRec4[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec4[l4] = 0.0; + fVec1[l4] = 0.0; } for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { fRec5[l5] = 0.0; } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec6[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec1[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec0[l9] = 0.0; + } } virtual void init(int sample_rate) { @@ -150,28 +166,35 @@ class faustBpf2p : public sfzFilterDsp { double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * double(fQ)))); double fSlow4 = (0.5 * (fSlow2 / fSlow3)); double fSlow5 = (fSlow4 + 1.0); - double fSlow6 = (1.0 - fSlow0); - double fSlow7 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow5) * fSlow6); - double fSlow8 = (((1.0 - fSlow4) / fSlow5) * fSlow6); - double fSlow9 = (0.5 * (fSlow2 / (fSlow3 * fSlow5))); - double fSlow10 = (fSlow9 * fSlow6); - double fSlow11 = ((0.0 - fSlow9) * fSlow6); + double fSlow6 = (0.5 * (fSlow2 / (fSlow3 * fSlow5))); + double fSlow7 = (1.0 - fSlow0); + double fSlow8 = (fSlow6 * fSlow7); + double fSlow9 = ((0.0 - fSlow6) * fSlow7); + double fSlow10 = (((1.0 - fSlow4) / fSlow5) * fSlow7); + double fSlow11 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow5) * fSlow7); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); - fRec1[0] = ((fSlow0 * fRec1[1]) + fSlow7); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow8); - fRec0[0] = (fTemp0 - ((fRec1[0] * fRec0[1]) + (fRec2[0] * fRec0[2]))); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow10); - fRec4[0] = (fSlow0 * fRec4[1]); - fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow11); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec3[0]) + (fRec4[0] * fRec0[1])) + (fRec5[0] * fRec0[2]))); - fRec1[1] = fRec1[0]; + fRec2[0] = (fSlow0 * fRec2[1]); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow8); + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow9); + fVec1[0] = (fTemp0 * fRec4[0]); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow10); + fVec2[0] = (fVec1[1] - (fRec5[0] * fRec0[1])); + fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow11); + fRec1[0] = ((fVec0[1] + ((fTemp0 * fRec3[0]) + fVec2[1])) - (fRec6[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); fRec2[1] = fRec2[0]; - fRec0[2] = fRec0[1]; - fRec0[1] = fRec0[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; fRec4[1] = fRec4[0]; + fVec1[1] = fVec1[0]; fRec5[1] = fRec5[0]; + fVec2[1] = fVec2[0]; + fRec6[1] = fRec6[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; } } diff --git a/src/sfizz/gen/filters/sfzBpf2pSv.cxx b/src/sfizz/gen/filters/sfzBpf2pSv.cxx index 00d33c907..8a81bb74f 100644 --- a/src/sfizz/gen/filters/sfzBpf2pSv.cxx +++ b/src/sfizz/gen/filters/sfzBpf2pSv.cxx @@ -42,7 +42,6 @@ class faustBpf2pSv : public sfzFilterDsp { double fRec5[2]; double fRec1[2]; double fRec2[2]; - double fRec6[2]; public: @@ -115,9 +114,6 @@ class faustBpf2pSv : public sfzFilterDsp { for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { fRec2[l4] = 0.0; } - for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { - fRec6[l5] = 0.0; - } } virtual void init(int sample_rate) { @@ -148,7 +144,6 @@ class faustBpf2pSv : public sfzFilterDsp { double fSlow1 = (1.0 - fSlow0); double fSlow2 = (std::tan((fConst2 * double(fCutoff))) * fSlow1); double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * double(fQ)))); - double fSlow4 = (fSlow3 * fSlow1); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow2); @@ -162,14 +157,12 @@ class faustBpf2pSv : public sfzFilterDsp { fRec1[0] = (fRec1[1] + (2.0 * (fRec3[0] * fTemp4))); double fTemp5 = (fRec2[1] + (2.0 * fTemp3)); fRec2[0] = fTemp5; - fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow4); - output0[i] = FAUSTFLOAT((fRec0 * fRec6[0])); + output0[i] = FAUSTFLOAT(fRec0); fRec3[1] = fRec3[0]; fRec4[1] = fRec4[0]; fRec5[1] = fRec5[0]; fRec1[1] = fRec1[0]; fRec2[1] = fRec2[0]; - fRec6[1] = fRec6[0]; } } diff --git a/src/sfizz/gen/filters/sfzBpf4p.cxx b/src/sfizz/gen/filters/sfzBpf4p.cxx index 2b3a8731b..33af5534f 100644 --- a/src/sfizz/gen/filters/sfzBpf4p.cxx +++ b/src/sfizz/gen/filters/sfzBpf4p.cxx @@ -37,13 +37,21 @@ class faustBpf4p : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec0[2]; - double fRec3[2]; - double fRec4[2]; - double fRec2[3]; + double fRec2[2]; double fRec5[2]; + double fVec0[2]; double fRec6[2]; - double fRec1[3]; + double fVec1[2]; + double fRec7[2]; + double fVec2[2]; + double fRec8[2]; + double fRec4[2]; + double fRec3[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec1[2]; + double fRec0[2]; public: @@ -102,25 +110,49 @@ class faustBpf4p : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec0[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec3[l1] = 0.0; + fRec5[l1] = 0.0; } for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { - fRec4[l2] = 0.0; + fVec0[l2] = 0.0; } - for (int l3 = 0; (l3 < 3); l3 = (l3 + 1)) { - fRec2[l3] = 0.0; + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fRec6[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec5[l4] = 0.0; + fVec1[l4] = 0.0; } for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { - fRec6[l5] = 0.0; + fRec7[l5] = 0.0; + } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec8[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec4[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec3[l9] = 0.0; } - for (int l6 = 0; (l6 < 3); l6 = (l6 + 1)) { - fRec1[l6] = 0.0; + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec3[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fVec4[l11] = 0.0; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fVec5[l12] = 0.0; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec1[l13] = 0.0; + } + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fRec0[l14] = 0.0; } } @@ -156,29 +188,43 @@ class faustBpf4p : public sfzFilterDsp { double fSlow5 = (fSlow4 + 1.0); double fSlow6 = (0.5 * (fSlow2 / (fSlow3 * fSlow5))); double fSlow7 = (1.0 - fSlow0); - double fSlow8 = (fSlow6 * fSlow7); - double fSlow9 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow5) * fSlow7); + double fSlow8 = ((0.0 - fSlow6) * fSlow7); + double fSlow9 = (fSlow6 * fSlow7); double fSlow10 = (((1.0 - fSlow4) / fSlow5) * fSlow7); - double fSlow11 = ((0.0 - fSlow6) * fSlow7); + double fSlow11 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow5) * fSlow7); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); - fRec0[0] = ((fSlow0 * fRec0[1]) + fSlow8); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow9); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow10); - fRec2[0] = (fTemp0 - ((fRec3[0] * fRec2[1]) + (fRec4[0] * fRec2[2]))); + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow8); fRec5[0] = (fSlow0 * fRec5[1]); - fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow11); - fRec1[0] = ((((fRec2[0] * fRec0[0]) + (fRec5[0] * fRec2[1])) + (fRec6[0] * fRec2[2])) - ((fRec3[0] * fRec1[1]) + (fRec4[0] * fRec1[2]))); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec1[0]) + (fRec5[0] * fRec1[1])) + (fRec6[0] * fRec1[2]))); - fRec0[1] = fRec0[0]; - fRec3[1] = fRec3[0]; - fRec4[1] = fRec4[0]; - fRec2[2] = fRec2[1]; + fVec0[0] = (fTemp0 * fRec5[0]); + fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow9); + fVec1[0] = (fTemp0 * fRec2[0]); + fRec7[0] = ((fSlow0 * fRec7[1]) + fSlow10); + fVec2[0] = (fVec1[1] - (fRec7[0] * fRec3[1])); + fRec8[0] = ((fSlow0 * fRec8[1]) + fSlow11); + fRec4[0] = ((fVec0[1] + ((fTemp0 * fRec6[0]) + fVec2[1])) - (fRec8[0] * fRec4[1])); + fRec3[0] = fRec4[0]; + fVec3[0] = (fRec2[0] * fRec3[0]); + fVec4[0] = (fVec3[1] - (fRec7[0] * fRec0[1])); + fVec5[0] = (fRec5[0] * fRec3[0]); + fRec1[0] = (((fVec4[1] + fVec5[1]) + (fRec6[0] * fRec3[0])) - (fRec8[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); fRec2[1] = fRec2[0]; fRec5[1] = fRec5[0]; + fVec0[1] = fVec0[0]; fRec6[1] = fRec6[0]; - fRec1[2] = fRec1[1]; + fVec1[1] = fVec1[0]; + fRec7[1] = fRec7[0]; + fVec2[1] = fVec2[0]; + fRec8[1] = fRec8[0]; + fRec4[1] = fRec4[0]; + fRec3[1] = fRec3[0]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; } } diff --git a/src/sfizz/gen/filters/sfzBpf6p.cxx b/src/sfizz/gen/filters/sfzBpf6p.cxx index ee81b2153..0888d48b7 100644 --- a/src/sfizz/gen/filters/sfzBpf6p.cxx +++ b/src/sfizz/gen/filters/sfzBpf6p.cxx @@ -37,14 +37,26 @@ class faustBpf6p : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec0[2]; - double fRec4[2]; - double fRec5[2]; - double fRec3[3]; - double fRec6[2]; + double fRec2[2]; double fRec7[2]; - double fRec2[3]; - double fRec1[3]; + double fVec0[2]; + double fRec8[2]; + double fVec1[2]; + double fRec9[2]; + double fVec2[2]; + double fRec10[2]; + double fRec6[2]; + double fRec5[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec4[2]; + double fRec3[2]; + double fVec6[2]; + double fVec7[2]; + double fVec8[2]; + double fRec1[2]; + double fRec0[2]; public: @@ -103,28 +115,64 @@ class faustBpf6p : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec0[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec4[l1] = 0.0; + fRec7[l1] = 0.0; } for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { - fRec5[l2] = 0.0; + fVec0[l2] = 0.0; } - for (int l3 = 0; (l3 < 3); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fRec8[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec6[l4] = 0.0; + fVec1[l4] = 0.0; } for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { - fRec7[l5] = 0.0; + fRec9[l5] = 0.0; + } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec10[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec6[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec5[l9] = 0.0; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec3[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fVec4[l11] = 0.0; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fVec5[l12] = 0.0; } - for (int l6 = 0; (l6 < 3); l6 = (l6 + 1)) { - fRec2[l6] = 0.0; + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec4[l13] = 0.0; } - for (int l7 = 0; (l7 < 3); l7 = (l7 + 1)) { - fRec1[l7] = 0.0; + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fRec3[l14] = 0.0; + } + for (int l15 = 0; (l15 < 2); l15 = (l15 + 1)) { + fVec6[l15] = 0.0; + } + for (int l16 = 0; (l16 < 2); l16 = (l16 + 1)) { + fVec7[l16] = 0.0; + } + for (int l17 = 0; (l17 < 2); l17 = (l17 + 1)) { + fVec8[l17] = 0.0; + } + for (int l18 = 0; (l18 < 2); l18 = (l18 + 1)) { + fRec1[l18] = 0.0; + } + for (int l19 = 0; (l19 < 2); l19 = (l19 + 1)) { + fRec0[l19] = 0.0; } } @@ -160,32 +208,53 @@ class faustBpf6p : public sfzFilterDsp { double fSlow5 = (fSlow4 + 1.0); double fSlow6 = (0.5 * (fSlow2 / (fSlow3 * fSlow5))); double fSlow7 = (1.0 - fSlow0); - double fSlow8 = (fSlow6 * fSlow7); - double fSlow9 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow5) * fSlow7); + double fSlow8 = ((0.0 - fSlow6) * fSlow7); + double fSlow9 = (fSlow6 * fSlow7); double fSlow10 = (((1.0 - fSlow4) / fSlow5) * fSlow7); - double fSlow11 = ((0.0 - fSlow6) * fSlow7); + double fSlow11 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow5) * fSlow7); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); - fRec0[0] = ((fSlow0 * fRec0[1]) + fSlow8); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow9); - fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow10); - fRec3[0] = (fTemp0 - ((fRec4[0] * fRec3[1]) + (fRec5[0] * fRec3[2]))); - fRec6[0] = (fSlow0 * fRec6[1]); - fRec7[0] = ((fSlow0 * fRec7[1]) + fSlow11); - fRec2[0] = ((((fRec3[0] * fRec0[0]) + (fRec6[0] * fRec3[1])) + (fRec7[0] * fRec3[2])) - ((fRec4[0] * fRec2[1]) + (fRec5[0] * fRec2[2]))); - fRec1[0] = ((((fRec0[0] * fRec2[0]) + (fRec6[0] * fRec2[1])) + (fRec7[0] * fRec2[2])) - ((fRec4[0] * fRec1[1]) + (fRec5[0] * fRec1[2]))); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec1[0]) + (fRec6[0] * fRec1[1])) + (fRec7[0] * fRec1[2]))); - fRec0[1] = fRec0[0]; - fRec4[1] = fRec4[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow8); + fRec7[0] = (fSlow0 * fRec7[1]); + fVec0[0] = (fTemp0 * fRec7[0]); + fRec8[0] = ((fSlow0 * fRec8[1]) + fSlow9); + fVec1[0] = (fTemp0 * fRec2[0]); + fRec9[0] = ((fSlow0 * fRec9[1]) + fSlow10); + fVec2[0] = (fVec1[1] - (fRec9[0] * fRec5[1])); + fRec10[0] = ((fSlow0 * fRec10[1]) + fSlow11); + fRec6[0] = ((fVec0[1] + ((fTemp0 * fRec8[0]) + fVec2[1])) - (fRec10[0] * fRec6[1])); + fRec5[0] = fRec6[0]; + fVec3[0] = (fRec2[0] * fRec5[0]); + fVec4[0] = (fVec3[1] - (fRec9[0] * fRec3[1])); + fVec5[0] = (fRec7[0] * fRec5[0]); + fRec4[0] = (((fVec4[1] + fVec5[1]) + (fRec8[0] * fRec5[0])) - (fRec10[0] * fRec4[1])); + fRec3[0] = fRec4[0]; + fVec6[0] = (fRec2[0] * fRec3[0]); + fVec7[0] = (fVec6[1] - (fRec9[0] * fRec0[1])); + fVec8[0] = (fRec7[0] * fRec3[0]); + fRec1[0] = (((fVec7[1] + fVec8[1]) + (fRec8[0] * fRec3[0])) - (fRec10[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); + fRec2[1] = fRec2[0]; + fRec7[1] = fRec7[0]; + fVec0[1] = fVec0[0]; + fRec8[1] = fRec8[0]; + fVec1[1] = fVec1[0]; + fRec9[1] = fRec9[0]; + fVec2[1] = fVec2[0]; + fRec10[1] = fRec10[0]; + fRec6[1] = fRec6[0]; fRec5[1] = fRec5[0]; - fRec3[2] = fRec3[1]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; + fRec4[1] = fRec4[0]; fRec3[1] = fRec3[0]; - fRec6[1] = fRec6[0]; - fRec7[1] = fRec7[0]; - fRec2[2] = fRec2[1]; - fRec2[1] = fRec2[0]; - fRec1[2] = fRec1[1]; + fVec6[1] = fVec6[0]; + fVec7[1] = fVec7[0]; + fVec8[1] = fVec8[0]; fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; } } diff --git a/src/sfizz/gen/filters/sfzBrf2p.cxx b/src/sfizz/gen/filters/sfzBrf2p.cxx index 9705729cb..70838f48c 100644 --- a/src/sfizz/gen/filters/sfzBrf2p.cxx +++ b/src/sfizz/gen/filters/sfzBrf2p.cxx @@ -37,10 +37,14 @@ class faustBrf2p : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec0[2]; double fRec2[2]; - double fRec1[3]; + double fVec0[2]; double fRec3[2]; + double fVec1[2]; + double fRec4[2]; + double fVec2[2]; + double fRec1[2]; + double fRec0[2]; public: @@ -99,16 +103,28 @@ class faustBrf2p : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec0[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } - for (int l2 = 0; (l2 < 3); l2 = (l2 + 1)) { - fRec1[l2] = 0.0; + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0; } for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + fVec1[l3] = 0.0; + } + for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { + fRec4[l4] = 0.0; + } + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fVec2[l5] = 0.0; + } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fRec1[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec0[l7] = 0.0; } } @@ -142,21 +158,28 @@ class faustBrf2p : public sfzFilterDsp { double fSlow3 = (fSlow2 + 1.0); double fSlow4 = (1.0 - fSlow0); double fSlow5 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow3) * fSlow4); - double fSlow6 = (((1.0 - fSlow2) / fSlow3) * fSlow4); - double fSlow7 = ((1.0 / fSlow3) * fSlow4); + double fSlow6 = ((1.0 / fSlow3) * fSlow4); + double fSlow7 = (((1.0 - fSlow2) / fSlow3) * fSlow4); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); - fRec0[0] = ((fSlow0 * fRec0[1]) + fSlow5); - double fTemp1 = (fRec0[0] * fRec1[1]); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow6); - fRec1[0] = (fTemp0 - (fTemp1 + (fRec2[0] * fRec1[2]))); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow7); - output0[i] = FAUSTFLOAT((fTemp1 + (fRec3[0] * (fRec1[0] + fRec1[2])))); - fRec0[1] = fRec0[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow5); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow6); + double fTemp1 = (fTemp0 * fRec3[0]); + fVec1[0] = fTemp1; + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow7); + fVec2[0] = (fVec1[1] - (fRec4[0] * fRec0[1])); + fRec1[0] = ((fVec0[1] + (fTemp1 + fVec2[1])) - (fRec2[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); fRec2[1] = fRec2[0]; - fRec1[2] = fRec1[1]; - fRec1[1] = fRec1[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; + fVec1[1] = fVec1[0]; + fRec4[1] = fRec4[0]; + fVec2[1] = fVec2[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; } } diff --git a/src/sfizz/gen/filters/sfzEqHshelf.cxx b/src/sfizz/gen/filters/sfzEqHshelf.cxx index 8d7227e58..43d9ac0a8 100644 --- a/src/sfizz/gen/filters/sfzEqHshelf.cxx +++ b/src/sfizz/gen/filters/sfzEqHshelf.cxx @@ -41,12 +41,16 @@ class faustEqHshelf : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fBandwidth; - double fRec1[2]; double fRec2[2]; - double fRec0[3]; + double fVec0[2]; double fRec3[2]; double fRec4[2]; + double fVec1[2]; double fRec5[2]; + double fVec2[2]; + double fRec6[2]; + double fRec1[2]; + double fRec0[2]; public: @@ -106,23 +110,35 @@ class faustEqHshelf : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec1[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } - for (int l2 = 0; (l2 < 3); l2 = (l2 + 1)) { - fRec0[l2] = 0.0; + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0; } for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + fRec4[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec4[l4] = 0.0; + fVec1[l4] = 0.0; } for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { fRec5[l5] = 0.0; } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec6[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec1[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec0[l9] = 0.0; + } } virtual void init(int sample_rate) { @@ -161,28 +177,35 @@ class faustEqHshelf : public sfzFilterDsp { double fSlow9 = (fSlow3 * fSlow6); double fSlow10 = ((fSlow1 + fSlow8) + (1.0 - fSlow9)); double fSlow11 = (1.0 - fSlow0); - double fSlow12 = ((2.0 * ((fSlow1 + (-1.0 - fSlow4)) / fSlow10)) * fSlow11); + double fSlow12 = ((((0.0 - (2.0 * fSlow1)) * ((fSlow1 + fSlow4) + -1.0)) / fSlow10) * fSlow11); double fSlow13 = (fSlow9 + fSlow8); - double fSlow14 = (((fSlow1 + (1.0 - fSlow13)) / fSlow10) * fSlow11); - double fSlow15 = (((fSlow1 * ((fSlow1 + fSlow13) + 1.0)) / fSlow10) * fSlow11); - double fSlow16 = ((((0.0 - (2.0 * fSlow1)) * ((fSlow1 + fSlow4) + -1.0)) / fSlow10) * fSlow11); - double fSlow17 = (((fSlow1 * ((fSlow1 + fSlow9) + (1.0 - fSlow8))) / fSlow10) * fSlow11); + double fSlow14 = (((fSlow1 * ((fSlow1 + fSlow13) + 1.0)) / fSlow10) * fSlow11); + double fSlow15 = (((fSlow1 * ((fSlow1 + fSlow9) + (1.0 - fSlow8))) / fSlow10) * fSlow11); + double fSlow16 = (((fSlow1 + (1.0 - fSlow13)) / fSlow10) * fSlow11); + double fSlow17 = ((2.0 * ((fSlow1 + (-1.0 - fSlow4)) / fSlow10)) * fSlow11); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); - fRec1[0] = ((fSlow0 * fRec1[1]) + fSlow12); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow14); - fRec0[0] = (fTemp0 - ((fRec1[0] * fRec0[1]) + (fRec2[0] * fRec0[2]))); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow15); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow16); - fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow17); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec3[0]) + (fRec4[0] * fRec0[1])) + (fRec5[0] * fRec0[2]))); - fRec1[1] = fRec1[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow12); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow14); + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow15); + fVec1[0] = (fTemp0 * fRec4[0]); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow16); + fVec2[0] = (fVec1[1] - (fRec5[0] * fRec0[1])); + fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow17); + fRec1[0] = ((fVec0[1] + ((fTemp0 * fRec3[0]) + fVec2[1])) - (fRec6[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); fRec2[1] = fRec2[0]; - fRec0[2] = fRec0[1]; - fRec0[1] = fRec0[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; fRec4[1] = fRec4[0]; + fVec1[1] = fVec1[0]; fRec5[1] = fRec5[0]; + fVec2[1] = fVec2[0]; + fRec6[1] = fRec6[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; } } diff --git a/src/sfizz/gen/filters/sfzEqLshelf.cxx b/src/sfizz/gen/filters/sfzEqLshelf.cxx index f9a45b4ac..89b93bb9f 100644 --- a/src/sfizz/gen/filters/sfzEqLshelf.cxx +++ b/src/sfizz/gen/filters/sfzEqLshelf.cxx @@ -41,12 +41,16 @@ class faustEqLshelf : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fBandwidth; - double fRec1[2]; double fRec2[2]; - double fRec0[3]; + double fVec0[2]; double fRec3[2]; double fRec4[2]; + double fVec1[2]; double fRec5[2]; + double fVec2[2]; + double fRec6[2]; + double fRec1[2]; + double fRec0[2]; public: @@ -106,23 +110,35 @@ class faustEqLshelf : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec1[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } - for (int l2 = 0; (l2 < 3); l2 = (l2 + 1)) { - fRec0[l2] = 0.0; + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0; } for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + fRec4[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec4[l4] = 0.0; + fVec1[l4] = 0.0; } for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { fRec5[l5] = 0.0; } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec6[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec1[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec0[l9] = 0.0; + } } virtual void init(int sample_rate) { @@ -162,27 +178,34 @@ class faustEqLshelf : public sfzFilterDsp { double fSlow10 = (fSlow6 + fSlow9); double fSlow11 = ((fSlow1 + fSlow10) + 1.0); double fSlow12 = (1.0 - fSlow0); - double fSlow13 = (((0.0 - (2.0 * ((fSlow1 + fSlow4) + -1.0))) / fSlow11) * fSlow12); - double fSlow14 = ((((fSlow1 + fSlow6) + (1.0 - fSlow9)) / fSlow11) * fSlow12); - double fSlow15 = (((fSlow1 * ((fSlow1 + fSlow9) + (1.0 - fSlow6))) / fSlow11) * fSlow12); - double fSlow16 = ((2.0 * ((fSlow1 * (fSlow1 + (-1.0 - fSlow4))) / fSlow11)) * fSlow12); - double fSlow17 = (((fSlow1 * (fSlow1 + (1.0 - fSlow10))) / fSlow11) * fSlow12); + double fSlow13 = ((2.0 * ((fSlow1 * (fSlow1 + (-1.0 - fSlow4))) / fSlow11)) * fSlow12); + double fSlow14 = (((fSlow1 * ((fSlow1 + fSlow9) + (1.0 - fSlow6))) / fSlow11) * fSlow12); + double fSlow15 = (((fSlow1 * (fSlow1 + (1.0 - fSlow10))) / fSlow11) * fSlow12); + double fSlow16 = ((((fSlow1 + fSlow6) + (1.0 - fSlow9)) / fSlow11) * fSlow12); + double fSlow17 = (((0.0 - (2.0 * ((fSlow1 + fSlow4) + -1.0))) / fSlow11) * fSlow12); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); - fRec1[0] = ((fSlow0 * fRec1[1]) + fSlow13); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow14); - fRec0[0] = (fTemp0 - ((fRec1[0] * fRec0[1]) + (fRec2[0] * fRec0[2]))); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow15); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow16); - fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow17); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec3[0]) + (fRec4[0] * fRec0[1])) + (fRec5[0] * fRec0[2]))); - fRec1[1] = fRec1[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow13); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow14); + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow15); + fVec1[0] = (fTemp0 * fRec4[0]); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow16); + fVec2[0] = (fVec1[1] - (fRec5[0] * fRec0[1])); + fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow17); + fRec1[0] = ((fVec0[1] + ((fTemp0 * fRec3[0]) + fVec2[1])) - (fRec6[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); fRec2[1] = fRec2[0]; - fRec0[2] = fRec0[1]; - fRec0[1] = fRec0[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; fRec4[1] = fRec4[0]; + fVec1[1] = fVec1[0]; fRec5[1] = fRec5[0]; + fVec2[1] = fVec2[0]; + fRec6[1] = fRec6[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; } } diff --git a/src/sfizz/gen/filters/sfzEqPeak.cxx b/src/sfizz/gen/filters/sfzEqPeak.cxx index 5eb2f7f27..fb16a2d41 100644 --- a/src/sfizz/gen/filters/sfzEqPeak.cxx +++ b/src/sfizz/gen/filters/sfzEqPeak.cxx @@ -40,11 +40,15 @@ class faustEqPeak : public sfzFilterDsp { double fConst3; FAUSTFLOAT fBandwidth; FAUSTFLOAT fPkShGain; - double fRec1[2]; double fRec2[2]; - double fRec0[3]; + double fVec0[2]; double fRec3[2]; double fRec4[2]; + double fVec1[2]; + double fRec5[2]; + double fVec2[2]; + double fRec1[2]; + double fRec0[2]; public: @@ -105,19 +109,31 @@ class faustEqPeak : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec1[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } - for (int l2 = 0; (l2 < 3); l2 = (l2 + 1)) { - fRec0[l2] = 0.0; + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0; } for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + fRec4[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec4[l4] = 0.0; + fVec1[l4] = 0.0; + } + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fRec5[l5] = 0.0; + } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec1[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec0[l8] = 0.0; } } @@ -155,25 +171,31 @@ class faustEqPeak : public sfzFilterDsp { double fSlow7 = (fSlow6 + 1.0); double fSlow8 = (1.0 - fSlow0); double fSlow9 = (((0.0 - (2.0 * std::cos(fSlow2))) / fSlow7) * fSlow8); - double fSlow10 = (((1.0 - fSlow6) / fSlow7) * fSlow8); - double fSlow11 = (0.5 * ((fSlow3 * fSlow5) / fSlow4)); - double fSlow12 = (((fSlow11 + 1.0) / fSlow7) * fSlow8); - double fSlow13 = (((1.0 - fSlow11) / fSlow7) * fSlow8); + double fSlow10 = (0.5 * ((fSlow3 * fSlow5) / fSlow4)); + double fSlow11 = (((fSlow10 + 1.0) / fSlow7) * fSlow8); + double fSlow12 = (((1.0 - fSlow10) / fSlow7) * fSlow8); + double fSlow13 = (((1.0 - fSlow6) / fSlow7) * fSlow8); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); - fRec1[0] = ((fSlow0 * fRec1[1]) + fSlow9); - double fTemp1 = (fRec1[0] * fRec0[1]); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow10); - fRec0[0] = (fTemp0 - (fTemp1 + (fRec2[0] * fRec0[2]))); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow12); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow13); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec3[0]) + fTemp1) + (fRec4[0] * fRec0[2]))); - fRec1[1] = fRec1[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow9); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow11); + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow12); + fVec1[0] = (fTemp0 * fRec4[0]); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow13); + fVec2[0] = (fVec1[1] - (fRec5[0] * fRec0[1])); + fRec1[0] = ((fVec0[1] + ((fTemp0 * fRec3[0]) + fVec2[1])) - (fRec2[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); fRec2[1] = fRec2[0]; - fRec0[2] = fRec0[1]; - fRec0[1] = fRec0[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; fRec4[1] = fRec4[0]; + fVec1[1] = fVec1[0]; + fRec5[1] = fRec5[0]; + fVec2[1] = fVec2[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; } } diff --git a/src/sfizz/gen/filters/sfzHpf2p.cxx b/src/sfizz/gen/filters/sfzHpf2p.cxx index 2a77dc904..bfca60e89 100644 --- a/src/sfizz/gen/filters/sfzHpf2p.cxx +++ b/src/sfizz/gen/filters/sfzHpf2p.cxx @@ -37,11 +37,15 @@ class faustHpf2p : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec0[2]; double fRec2[2]; + double fVec0[2]; double fRec3[2]; - double fRec1[3]; + double fVec1[2]; double fRec4[2]; + double fVec2[2]; + double fRec5[2]; + double fRec1[2]; + double fRec0[2]; public: @@ -100,20 +104,32 @@ class faustHpf2p : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec0[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { fRec3[l2] = 0.0; } - for (int l3 = 0; (l3 < 3); l3 = (l3 + 1)) { - fRec1[l3] = 0.0; + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fVec1[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { fRec4[l4] = 0.0; } + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fVec2[l5] = 0.0; + } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fRec5[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec1[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec0[l8] = 0.0; + } } virtual void init(int sample_rate) { @@ -147,23 +163,31 @@ class faustHpf2p : public sfzFilterDsp { double fSlow4 = (fSlow3 + 1.0); double fSlow5 = (1.0 - fSlow0); double fSlow6 = (((-1.0 - fSlow2) / fSlow4) * fSlow5); - double fSlow7 = (((0.0 - (2.0 * fSlow2)) / fSlow4) * fSlow5); + double fSlow7 = ((0.5 * ((fSlow2 + 1.0) / fSlow4)) * fSlow5); double fSlow8 = (((1.0 - fSlow3) / fSlow4) * fSlow5); - double fSlow9 = ((0.5 * ((fSlow2 + 1.0) / fSlow4)) * fSlow5); + double fSlow9 = (((0.0 - (2.0 * fSlow2)) / fSlow4) * fSlow5); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); - fRec0[0] = ((fSlow0 * fRec0[1]) + fSlow6); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow7); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow8); - fRec1[0] = (fTemp0 - ((fRec2[0] * fRec1[1]) + (fRec3[0] * fRec1[2]))); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow9); - output0[i] = FAUSTFLOAT(((fRec0[0] * fRec1[1]) + (fRec4[0] * (fRec1[0] + fRec1[2])))); - fRec0[1] = fRec0[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow6); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow7); + double fTemp1 = (fTemp0 * fRec3[0]); + fVec1[0] = fTemp1; + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow8); + fVec2[0] = (fVec1[1] - (fRec4[0] * fRec0[1])); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow9); + fRec1[0] = ((fVec0[1] + (fTemp1 + fVec2[1])) - (fRec5[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); fRec2[1] = fRec2[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; - fRec1[2] = fRec1[1]; - fRec1[1] = fRec1[0]; + fVec1[1] = fVec1[0]; fRec4[1] = fRec4[0]; + fVec2[1] = fVec2[0]; + fRec5[1] = fRec5[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; } } diff --git a/src/sfizz/gen/filters/sfzHpf4p.cxx b/src/sfizz/gen/filters/sfzHpf4p.cxx index 3dbc17074..6ae2cf55e 100644 --- a/src/sfizz/gen/filters/sfzHpf4p.cxx +++ b/src/sfizz/gen/filters/sfzHpf4p.cxx @@ -37,12 +37,20 @@ class faustHpf4p : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec0[2]; - double fRec3[2]; - double fRec4[2]; - double fRec2[3]; + double fRec2[2]; + double fVec0[2]; double fRec5[2]; - double fRec1[3]; + double fVec1[2]; + double fRec6[2]; + double fVec2[2]; + double fRec7[2]; + double fRec4[2]; + double fRec3[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec1[2]; + double fRec0[2]; public: @@ -101,22 +109,46 @@ class faustHpf4p : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec0[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec3[l1] = 0.0; + fVec0[l1] = 0.0; } for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { - fRec4[l2] = 0.0; + fRec5[l2] = 0.0; } - for (int l3 = 0; (l3 < 3); l3 = (l3 + 1)) { - fRec2[l3] = 0.0; + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fVec1[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec5[l4] = 0.0; + fRec6[l4] = 0.0; + } + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fVec2[l5] = 0.0; + } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fRec7[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec4[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec3[l8] = 0.0; } - for (int l5 = 0; (l5 < 3); l5 = (l5 + 1)) { - fRec1[l5] = 0.0; + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fVec3[l9] = 0.0; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec4[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fVec5[l11] = 0.0; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fRec1[l12] = 0.0; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec0[l13] = 0.0; } } @@ -151,26 +183,42 @@ class faustHpf4p : public sfzFilterDsp { double fSlow4 = (fSlow3 + 1.0); double fSlow5 = (1.0 - fSlow0); double fSlow6 = (((-1.0 - fSlow2) / fSlow4) * fSlow5); - double fSlow7 = (((0.0 - (2.0 * fSlow2)) / fSlow4) * fSlow5); + double fSlow7 = ((0.5 * ((fSlow2 + 1.0) / fSlow4)) * fSlow5); double fSlow8 = (((1.0 - fSlow3) / fSlow4) * fSlow5); - double fSlow9 = ((0.5 * ((fSlow2 + 1.0) / fSlow4)) * fSlow5); + double fSlow9 = (((0.0 - (2.0 * fSlow2)) / fSlow4) * fSlow5); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); - fRec0[0] = ((fSlow0 * fRec0[1]) + fSlow6); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow7); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow8); - fRec2[0] = (fTemp0 - ((fRec3[0] * fRec2[1]) + (fRec4[0] * fRec2[2]))); - fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow9); - fRec1[0] = (((fRec0[0] * fRec2[1]) + (fRec5[0] * (fRec2[0] + fRec2[2]))) - ((fRec3[0] * fRec1[1]) + (fRec4[0] * fRec1[2]))); - output0[i] = FAUSTFLOAT(((fRec0[0] * fRec1[1]) + (fRec5[0] * (fRec1[0] + fRec1[2])))); - fRec0[1] = fRec0[0]; - fRec3[1] = fRec3[0]; - fRec4[1] = fRec4[0]; - fRec2[2] = fRec2[1]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow6); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow7); + double fTemp1 = (fTemp0 * fRec5[0]); + fVec1[0] = fTemp1; + fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow8); + fVec2[0] = (fVec1[1] - (fRec6[0] * fRec3[1])); + fRec7[0] = ((fSlow0 * fRec7[1]) + fSlow9); + fRec4[0] = ((fVec0[1] + (fTemp1 + fVec2[1])) - (fRec7[0] * fRec4[1])); + fRec3[0] = fRec4[0]; + fVec3[0] = (fRec2[0] * fRec3[0]); + double fTemp2 = (fRec5[0] * fRec3[0]); + fVec4[0] = fTemp2; + fVec5[0] = (fVec4[1] - (fRec6[0] * fRec0[1])); + fRec1[0] = ((fVec3[1] + (fTemp2 + fVec5[1])) - (fRec7[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); fRec2[1] = fRec2[0]; + fVec0[1] = fVec0[0]; fRec5[1] = fRec5[0]; - fRec1[2] = fRec1[1]; + fVec1[1] = fVec1[0]; + fRec6[1] = fRec6[0]; + fVec2[1] = fVec2[0]; + fRec7[1] = fRec7[0]; + fRec4[1] = fRec4[0]; + fRec3[1] = fRec3[0]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; } } diff --git a/src/sfizz/gen/filters/sfzHpf6p.cxx b/src/sfizz/gen/filters/sfzHpf6p.cxx index 54879eb64..32a72cd65 100644 --- a/src/sfizz/gen/filters/sfzHpf6p.cxx +++ b/src/sfizz/gen/filters/sfzHpf6p.cxx @@ -37,13 +37,25 @@ class faustHpf6p : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec0[2]; - double fRec4[2]; - double fRec5[2]; - double fRec3[3]; + double fRec2[2]; + double fVec0[2]; + double fRec7[2]; + double fVec1[2]; + double fRec8[2]; + double fVec2[2]; + double fRec9[2]; double fRec6[2]; - double fRec2[3]; - double fRec1[3]; + double fRec5[2]; + double fVec3[2]; + double fVec4[2]; + double fVec5[2]; + double fRec4[2]; + double fRec3[2]; + double fVec6[2]; + double fVec7[2]; + double fVec8[2]; + double fRec1[2]; + double fRec0[2]; public: @@ -102,25 +114,61 @@ class faustHpf6p : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec0[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec4[l1] = 0.0; + fVec0[l1] = 0.0; } for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { - fRec5[l2] = 0.0; + fRec7[l2] = 0.0; } - for (int l3 = 0; (l3 < 3); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fVec1[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec6[l4] = 0.0; + fRec8[l4] = 0.0; + } + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fVec2[l5] = 0.0; + } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fRec9[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec6[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec5[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fVec3[l9] = 0.0; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fVec4[l10] = 0.0; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fVec5[l11] = 0.0; } - for (int l5 = 0; (l5 < 3); l5 = (l5 + 1)) { - fRec2[l5] = 0.0; + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fRec4[l12] = 0.0; } - for (int l6 = 0; (l6 < 3); l6 = (l6 + 1)) { - fRec1[l6] = 0.0; + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec3[l13] = 0.0; + } + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fVec6[l14] = 0.0; + } + for (int l15 = 0; (l15 < 2); l15 = (l15 + 1)) { + fVec7[l15] = 0.0; + } + for (int l16 = 0; (l16 < 2); l16 = (l16 + 1)) { + fVec8[l16] = 0.0; + } + for (int l17 = 0; (l17 < 2); l17 = (l17 + 1)) { + fRec1[l17] = 0.0; + } + for (int l18 = 0; (l18 < 2); l18 = (l18 + 1)) { + fRec0[l18] = 0.0; } } @@ -155,29 +203,53 @@ class faustHpf6p : public sfzFilterDsp { double fSlow4 = (fSlow3 + 1.0); double fSlow5 = (1.0 - fSlow0); double fSlow6 = (((-1.0 - fSlow2) / fSlow4) * fSlow5); - double fSlow7 = (((0.0 - (2.0 * fSlow2)) / fSlow4) * fSlow5); + double fSlow7 = ((0.5 * ((fSlow2 + 1.0) / fSlow4)) * fSlow5); double fSlow8 = (((1.0 - fSlow3) / fSlow4) * fSlow5); - double fSlow9 = ((0.5 * ((fSlow2 + 1.0) / fSlow4)) * fSlow5); + double fSlow9 = (((0.0 - (2.0 * fSlow2)) / fSlow4) * fSlow5); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); - fRec0[0] = ((fSlow0 * fRec0[1]) + fSlow6); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow7); - fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow8); - fRec3[0] = (fTemp0 - ((fRec4[0] * fRec3[1]) + (fRec5[0] * fRec3[2]))); - fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow9); - fRec2[0] = (((fRec0[0] * fRec3[1]) + (fRec6[0] * (fRec3[0] + fRec3[2]))) - ((fRec4[0] * fRec2[1]) + (fRec5[0] * fRec2[2]))); - fRec1[0] = (((fRec0[0] * fRec2[1]) + (fRec6[0] * (fRec2[0] + fRec2[2]))) - ((fRec4[0] * fRec1[1]) + (fRec5[0] * fRec1[2]))); - output0[i] = FAUSTFLOAT(((fRec0[0] * fRec1[1]) + (fRec6[0] * (fRec1[0] + fRec1[2])))); - fRec0[1] = fRec0[0]; - fRec4[1] = fRec4[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow6); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec7[0] = ((fSlow0 * fRec7[1]) + fSlow7); + double fTemp1 = (fTemp0 * fRec7[0]); + fVec1[0] = fTemp1; + fRec8[0] = ((fSlow0 * fRec8[1]) + fSlow8); + fVec2[0] = (fVec1[1] - (fRec8[0] * fRec5[1])); + fRec9[0] = ((fSlow0 * fRec9[1]) + fSlow9); + fRec6[0] = ((fVec0[1] + (fTemp1 + fVec2[1])) - (fRec9[0] * fRec6[1])); + fRec5[0] = fRec6[0]; + fVec3[0] = (fRec2[0] * fRec5[0]); + double fTemp2 = (fRec7[0] * fRec5[0]); + fVec4[0] = fTemp2; + fVec5[0] = (fVec4[1] - (fRec8[0] * fRec3[1])); + fRec4[0] = ((fVec3[1] + (fTemp2 + fVec5[1])) - (fRec9[0] * fRec4[1])); + fRec3[0] = fRec4[0]; + fVec6[0] = (fRec2[0] * fRec3[0]); + double fTemp3 = (fRec7[0] * fRec3[0]); + fVec7[0] = fTemp3; + fVec8[0] = (fVec7[1] - (fRec8[0] * fRec0[1])); + fRec1[0] = ((fVec6[1] + (fTemp3 + fVec8[1])) - (fRec9[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); + fRec2[1] = fRec2[0]; + fVec0[1] = fVec0[0]; + fRec7[1] = fRec7[0]; + fVec1[1] = fVec1[0]; + fRec8[1] = fRec8[0]; + fVec2[1] = fVec2[0]; + fRec9[1] = fRec9[0]; + fRec6[1] = fRec6[0]; fRec5[1] = fRec5[0]; - fRec3[2] = fRec3[1]; + fVec3[1] = fVec3[0]; + fVec4[1] = fVec4[0]; + fVec5[1] = fVec5[0]; + fRec4[1] = fRec4[0]; fRec3[1] = fRec3[0]; - fRec6[1] = fRec6[0]; - fRec2[2] = fRec2[1]; - fRec2[1] = fRec2[0]; - fRec1[2] = fRec1[1]; + fVec6[1] = fVec6[0]; + fVec7[1] = fVec7[0]; + fVec8[1] = fVec8[0]; fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; } } diff --git a/src/sfizz/gen/filters/sfzHsh.cxx b/src/sfizz/gen/filters/sfzHsh.cxx index dbae8ae80..6934d8f53 100644 --- a/src/sfizz/gen/filters/sfzHsh.cxx +++ b/src/sfizz/gen/filters/sfzHsh.cxx @@ -38,12 +38,16 @@ class faustHsh : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec1[2]; double fRec2[2]; - double fRec0[3]; + double fVec0[2]; double fRec3[2]; double fRec4[2]; + double fVec1[2]; double fRec5[2]; + double fVec2[2]; + double fRec6[2]; + double fRec1[2]; + double fRec0[2]; public: @@ -103,23 +107,35 @@ class faustHsh : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec1[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } - for (int l2 = 0; (l2 < 3); l2 = (l2 + 1)) { - fRec0[l2] = 0.0; + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0; } for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + fRec4[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec4[l4] = 0.0; + fVec1[l4] = 0.0; } for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { fRec5[l5] = 0.0; } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec6[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec1[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec0[l9] = 0.0; + } } virtual void init(int sample_rate) { @@ -155,28 +171,35 @@ class faustHsh : public sfzFilterDsp { double fSlow6 = (fSlow3 * (fSlow1 + -1.0)); double fSlow7 = ((fSlow1 + fSlow5) + (1.0 - fSlow6)); double fSlow8 = (1.0 - fSlow0); - double fSlow9 = ((2.0 * ((fSlow1 + (-1.0 - fSlow4)) / fSlow7)) * fSlow8); - double fSlow10 = (((fSlow1 + (1.0 - (fSlow5 + fSlow6))) / fSlow7) * fSlow8); - double fSlow11 = (fSlow1 + fSlow6); - double fSlow12 = (((fSlow1 * ((fSlow5 + fSlow11) + 1.0)) / fSlow7) * fSlow8); - double fSlow13 = ((((0.0 - (2.0 * fSlow1)) * ((fSlow1 + fSlow4) + -1.0)) / fSlow7) * fSlow8); - double fSlow14 = (((fSlow1 * (fSlow11 + (1.0 - fSlow5))) / fSlow7) * fSlow8); + double fSlow9 = ((((0.0 - (2.0 * fSlow1)) * ((fSlow1 + fSlow4) + -1.0)) / fSlow7) * fSlow8); + double fSlow10 = (fSlow1 + fSlow6); + double fSlow11 = (((fSlow1 * ((fSlow5 + fSlow10) + 1.0)) / fSlow7) * fSlow8); + double fSlow12 = (((fSlow1 * (fSlow10 + (1.0 - fSlow5))) / fSlow7) * fSlow8); + double fSlow13 = (((fSlow1 + (1.0 - (fSlow5 + fSlow6))) / fSlow7) * fSlow8); + double fSlow14 = ((2.0 * ((fSlow1 + (-1.0 - fSlow4)) / fSlow7)) * fSlow8); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); - fRec1[0] = ((fSlow0 * fRec1[1]) + fSlow9); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow10); - fRec0[0] = (fTemp0 - ((fRec1[0] * fRec0[1]) + (fRec2[0] * fRec0[2]))); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow12); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow13); - fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow14); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec3[0]) + (fRec4[0] * fRec0[1])) + (fRec5[0] * fRec0[2]))); - fRec1[1] = fRec1[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow9); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow11); + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow12); + fVec1[0] = (fTemp0 * fRec4[0]); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow13); + fVec2[0] = (fVec1[1] - (fRec5[0] * fRec0[1])); + fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow14); + fRec1[0] = ((fVec0[1] + ((fTemp0 * fRec3[0]) + fVec2[1])) - (fRec6[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); fRec2[1] = fRec2[0]; - fRec0[2] = fRec0[1]; - fRec0[1] = fRec0[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; fRec4[1] = fRec4[0]; + fVec1[1] = fVec1[0]; fRec5[1] = fRec5[0]; + fVec2[1] = fVec2[0]; + fRec6[1] = fRec6[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; } } diff --git a/src/sfizz/gen/filters/sfzLsh.cxx b/src/sfizz/gen/filters/sfzLsh.cxx index 746645d6a..8ff05ae5e 100644 --- a/src/sfizz/gen/filters/sfzLsh.cxx +++ b/src/sfizz/gen/filters/sfzLsh.cxx @@ -38,12 +38,16 @@ class faustLsh : public sfzFilterDsp { double fConst2; FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; - double fRec1[2]; double fRec2[2]; - double fRec0[3]; + double fVec0[2]; double fRec3[2]; double fRec4[2]; + double fVec1[2]; double fRec5[2]; + double fVec2[2]; + double fRec6[2]; + double fRec1[2]; + double fRec0[2]; public: @@ -103,23 +107,35 @@ class faustLsh : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec1[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } - for (int l2 = 0; (l2 < 3); l2 = (l2 + 1)) { - fRec0[l2] = 0.0; + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0; } for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + fRec4[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec4[l4] = 0.0; + fVec1[l4] = 0.0; } for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { fRec5[l5] = 0.0; } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec6[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec1[l8] = 0.0; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec0[l9] = 0.0; + } } virtual void init(int sample_rate) { @@ -156,27 +172,34 @@ class faustLsh : public sfzFilterDsp { double fSlow7 = (fSlow1 + fSlow6); double fSlow8 = ((fSlow5 + fSlow7) + 1.0); double fSlow9 = (1.0 - fSlow0); - double fSlow10 = (((0.0 - (2.0 * ((fSlow1 + fSlow4) + -1.0))) / fSlow8) * fSlow9); - double fSlow11 = (((fSlow7 + (1.0 - fSlow5)) / fSlow8) * fSlow9); - double fSlow12 = (((fSlow1 * ((fSlow1 + fSlow5) + (1.0 - fSlow6))) / fSlow8) * fSlow9); - double fSlow13 = ((2.0 * ((fSlow1 * (fSlow1 + (-1.0 - fSlow4))) / fSlow8)) * fSlow9); - double fSlow14 = (((fSlow1 * (fSlow1 + (1.0 - (fSlow5 + fSlow6)))) / fSlow8) * fSlow9); + double fSlow10 = ((2.0 * ((fSlow1 * (fSlow1 + (-1.0 - fSlow4))) / fSlow8)) * fSlow9); + double fSlow11 = (((fSlow1 * ((fSlow1 + fSlow5) + (1.0 - fSlow6))) / fSlow8) * fSlow9); + double fSlow12 = (((fSlow1 * (fSlow1 + (1.0 - (fSlow5 + fSlow6)))) / fSlow8) * fSlow9); + double fSlow13 = (((fSlow7 + (1.0 - fSlow5)) / fSlow8) * fSlow9); + double fSlow14 = (((0.0 - (2.0 * ((fSlow1 + fSlow4) + -1.0))) / fSlow8) * fSlow9); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); - fRec1[0] = ((fSlow0 * fRec1[1]) + fSlow10); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow11); - fRec0[0] = (fTemp0 - ((fRec1[0] * fRec0[1]) + (fRec2[0] * fRec0[2]))); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow12); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow13); - fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow14); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec3[0]) + (fRec4[0] * fRec0[1])) + (fRec5[0] * fRec0[2]))); - fRec1[1] = fRec1[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow10); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow11); + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow12); + fVec1[0] = (fTemp0 * fRec4[0]); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow13); + fVec2[0] = (fVec1[1] - (fRec5[0] * fRec0[1])); + fRec6[0] = ((fSlow0 * fRec6[1]) + fSlow14); + fRec1[0] = ((fVec0[1] + ((fTemp0 * fRec3[0]) + fVec2[1])) - (fRec6[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); fRec2[1] = fRec2[0]; - fRec0[2] = fRec0[1]; - fRec0[1] = fRec0[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; fRec4[1] = fRec4[0]; + fVec1[1] = fVec1[0]; fRec5[1] = fRec5[0]; + fVec2[1] = fVec2[0]; + fRec6[1] = fRec6[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; } } diff --git a/src/sfizz/gen/filters/sfzPeq.cxx b/src/sfizz/gen/filters/sfzPeq.cxx index 38bc2d9bf..0603c45f3 100644 --- a/src/sfizz/gen/filters/sfzPeq.cxx +++ b/src/sfizz/gen/filters/sfzPeq.cxx @@ -38,11 +38,15 @@ class faustPeq : public sfzFilterDsp { FAUSTFLOAT fCutoff; FAUSTFLOAT fQ; FAUSTFLOAT fPkShGain; - double fRec1[2]; double fRec2[2]; - double fRec0[3]; + double fVec0[2]; double fRec3[2]; double fRec4[2]; + double fVec1[2]; + double fRec5[2]; + double fVec2[2]; + double fRec1[2]; + double fRec0[2]; public: @@ -102,19 +106,31 @@ class faustPeq : public sfzFilterDsp { virtual void instanceClear() { for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { - fRec1[l0] = 0.0; + fRec2[l0] = 0.0; } for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { - fRec2[l1] = 0.0; + fVec0[l1] = 0.0; } - for (int l2 = 0; (l2 < 3); l2 = (l2 + 1)) { - fRec0[l2] = 0.0; + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0; } for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { - fRec3[l3] = 0.0; + fRec4[l3] = 0.0; } for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { - fRec4[l4] = 0.0; + fVec1[l4] = 0.0; + } + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fRec5[l5] = 0.0; + } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fVec2[l6] = 0.0; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec1[l7] = 0.0; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec0[l8] = 0.0; } } @@ -151,25 +167,31 @@ class faustPeq : public sfzFilterDsp { double fSlow6 = (fSlow5 + 1.0); double fSlow7 = (1.0 - fSlow0); double fSlow8 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow6) * fSlow7); - double fSlow9 = (((1.0 - fSlow5) / fSlow6) * fSlow7); - double fSlow10 = (0.5 * ((fSlow2 * fSlow4) / fSlow3)); - double fSlow11 = (((fSlow10 + 1.0) / fSlow6) * fSlow7); - double fSlow12 = (((1.0 - fSlow10) / fSlow6) * fSlow7); + double fSlow9 = (0.5 * ((fSlow2 * fSlow4) / fSlow3)); + double fSlow10 = (((fSlow9 + 1.0) / fSlow6) * fSlow7); + double fSlow11 = (((1.0 - fSlow9) / fSlow6) * fSlow7); + double fSlow12 = (((1.0 - fSlow5) / fSlow6) * fSlow7); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); - fRec1[0] = ((fSlow0 * fRec1[1]) + fSlow8); - double fTemp1 = (fRec1[0] * fRec0[1]); - fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow9); - fRec0[0] = (fTemp0 - (fTemp1 + (fRec2[0] * fRec0[2]))); - fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow11); - fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow12); - output0[i] = FAUSTFLOAT((((fRec0[0] * fRec3[0]) + fTemp1) + (fRec4[0] * fRec0[2]))); - fRec1[1] = fRec1[0]; + fRec2[0] = ((fSlow0 * fRec2[1]) + fSlow8); + fVec0[0] = (fTemp0 * fRec2[0]); + fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow10); + fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow11); + fVec1[0] = (fTemp0 * fRec4[0]); + fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow12); + fVec2[0] = (fVec1[1] - (fRec5[0] * fRec0[1])); + fRec1[0] = ((fVec0[1] + ((fTemp0 * fRec3[0]) + fVec2[1])) - (fRec2[0] * fRec1[1])); + fRec0[0] = fRec1[0]; + output0[i] = FAUSTFLOAT(fRec0[0]); fRec2[1] = fRec2[0]; - fRec0[2] = fRec0[1]; - fRec0[1] = fRec0[0]; + fVec0[1] = fVec0[0]; fRec3[1] = fRec3[0]; fRec4[1] = fRec4[0]; + fVec1[1] = fVec1[0]; + fRec5[1] = fRec5[0]; + fVec2[1] = fVec2[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; } } From 5790dd3ace594f3def5be3fd0dbd702133b1f417 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 22 Apr 2020 05:23:43 +0200 Subject: [PATCH 105/444] Set the type of stepcc to float --- src/sfizz/Defaults.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 2b12a5048..6226ee9d6 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -82,7 +82,7 @@ namespace Default // Region logic: MIDI conditions constexpr Range channelRange { 1, 16 }; constexpr Range midiChannelRange { 0, 15 }; - constexpr Range stepCCRange { 0, 127 }; + constexpr Range stepCCRange { 0, 127 }; constexpr Range smoothCCRange { 0, 127 }; constexpr Range curveCCRange { 0, 255 }; constexpr Range ccNumberRange { 0, config::numCCs }; From fc07833ec15ed0cb4b3e5e6f10078564ee9da43b Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Wed, 22 Apr 2020 10:08:54 +0200 Subject: [PATCH 106/444] Add a sfizz_render client in-tree --- .gitmodules | 3 + clients/CMakeLists.txt | 8 +- clients/MidiHelpers.h | 29 + clients/cxxopts.hpp | 2215 ++++++++++++++++++++++++++++++++++++++ clients/external/fmidi | 1 + clients/jack_client.cpp | 28 +- clients/sfizz_render.cpp | 208 ++++ 7 files changed, 2464 insertions(+), 28 deletions(-) create mode 100644 clients/MidiHelpers.h create mode 100644 clients/cxxopts.hpp create mode 160000 clients/external/fmidi create mode 100644 clients/sfizz_render.cpp diff --git a/.gitmodules b/.gitmodules index 04936207b..85224a25c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,3 +18,6 @@ path = vst/external/VST_SDK/VST3_SDK/vstgui4 url = https://github.com/sfztools/vstgui.git shallow = true +[submodule "clients/external/fmidi"] + path = clients/external/fmidi + url = https://github.com/jpcima/fmidi.git diff --git a/clients/CMakeLists.txt b/clients/CMakeLists.txt index eb45daf39..6657702b7 100644 --- a/clients/CMakeLists.txt +++ b/clients/CMakeLists.txt @@ -8,4 +8,10 @@ target_include_directories (sfizz_jack PRIVATE ${JACK_INCLUDE_DIRS}) target_link_libraries (sfizz_jack PRIVATE sfizz::sfizz jack absl::flags_parse ${JACK_LIBRARIES}) sfizz_enable_lto_if_needed (sfizz_jack) install (TARGETS sfizz_jack DESTINATION ${CMAKE_INSTALL_BINDIR} - COMPONENT "jack" OPTIONAL) +COMPONENT "jack" OPTIONAL) + +add_subdirectory(external/fmidi EXCLUDE_FROM_ALL) +add_executable(sfizz_render sfizz_render.cpp) +target_link_libraries(sfizz_render sfizz::sfizz fmidi-player fmidi) +sfizz_enable_lto_if_needed (sfizz_render) +install (TARGETS sfizz_render DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL) diff --git a/clients/MidiHelpers.h b/clients/MidiHelpers.h new file mode 100644 index 000000000..10168229b --- /dev/null +++ b/clients/MidiHelpers.h @@ -0,0 +1,29 @@ +#pragma once +#include + +namespace midi { +constexpr uint8_t statusMask { 0b11110000 }; +constexpr uint8_t channelMask { 0b00001111 }; +constexpr uint8_t noteOff { 0x80 }; +constexpr uint8_t noteOn { 0x90 }; +constexpr uint8_t polyphonicPressure { 0xA0 }; +constexpr uint8_t controlChange { 0xB0 }; +constexpr uint8_t programChange { 0xC0 }; +constexpr uint8_t channelPressure { 0xD0 }; +constexpr uint8_t pitchBend { 0xE0 }; +constexpr uint8_t systemMessage { 0xF0 }; + +constexpr uint8_t status(uint8_t midiStatusByte) +{ + return midiStatusByte & statusMask; +} +constexpr uint8_t channel(uint8_t midiStatusByte) +{ + return midiStatusByte & channelMask; +} + +constexpr int buildAndCenterPitch(uint8_t firstByte, uint8_t secondByte) +{ + return (int)(((unsigned int)secondByte << 7) + (unsigned int)firstByte) - 8192; +} +} diff --git a/clients/cxxopts.hpp b/clients/cxxopts.hpp new file mode 100644 index 000000000..16446922d --- /dev/null +++ b/clients/cxxopts.hpp @@ -0,0 +1,2215 @@ +/* + +Copyright (c) 2014, 2015, 2016, 2017 Jarryd Beck + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +#ifndef CXXOPTS_HPP_INCLUDED +#define CXXOPTS_HPP_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cpp_lib_optional +#include +#define CXXOPTS_HAS_OPTIONAL +#endif + +#ifndef CXXOPTS_VECTOR_DELIMITER +#define CXXOPTS_VECTOR_DELIMITER ',' +#endif + +#define CXXOPTS__VERSION_MAJOR 2 +#define CXXOPTS__VERSION_MINOR 2 +#define CXXOPTS__VERSION_PATCH 0 + +namespace cxxopts +{ + static constexpr struct { + uint8_t major, minor, patch; + } version = { + CXXOPTS__VERSION_MAJOR, + CXXOPTS__VERSION_MINOR, + CXXOPTS__VERSION_PATCH + }; +} + +//when we ask cxxopts to use Unicode, help strings are processed using ICU, +//which results in the correct lengths being computed for strings when they +//are formatted for the help output +//it is necessary to make sure that can be found by the +//compiler, and that icu-uc is linked in to the binary. + +#ifdef CXXOPTS_USE_UNICODE +#include + +namespace cxxopts +{ + typedef icu::UnicodeString String; + + inline + String + toLocalString(std::string s) + { + return icu::UnicodeString::fromUTF8(std::move(s)); + } + + class UnicodeStringIterator : public + std::iterator + { + public: + + UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos) + : s(string) + , i(pos) + { + } + + value_type + operator*() const + { + return s->char32At(i); + } + + bool + operator==(const UnicodeStringIterator& rhs) const + { + return s == rhs.s && i == rhs.i; + } + + bool + operator!=(const UnicodeStringIterator& rhs) const + { + return !(*this == rhs); + } + + UnicodeStringIterator& + operator++() + { + ++i; + return *this; + } + + UnicodeStringIterator + operator+(int32_t v) + { + return UnicodeStringIterator(s, i + v); + } + + private: + const icu::UnicodeString* s; + int32_t i; + }; + + inline + String& + stringAppend(String&s, String a) + { + return s.append(std::move(a)); + } + + inline + String& + stringAppend(String& s, int n, UChar32 c) + { + for (int i = 0; i != n; ++i) + { + s.append(c); + } + + return s; + } + + template + String& + stringAppend(String& s, Iterator begin, Iterator end) + { + while (begin != end) + { + s.append(*begin); + ++begin; + } + + return s; + } + + inline + size_t + stringLength(const String& s) + { + return s.length(); + } + + inline + std::string + toUTF8String(const String& s) + { + std::string result; + s.toUTF8String(result); + + return result; + } + + inline + bool + empty(const String& s) + { + return s.isEmpty(); + } +} + +namespace std +{ + inline + cxxopts::UnicodeStringIterator + begin(const icu::UnicodeString& s) + { + return cxxopts::UnicodeStringIterator(&s, 0); + } + + inline + cxxopts::UnicodeStringIterator + end(const icu::UnicodeString& s) + { + return cxxopts::UnicodeStringIterator(&s, s.length()); + } +} + +//ifdef CXXOPTS_USE_UNICODE +#else + +namespace cxxopts +{ + typedef std::string String; + + template + T + toLocalString(T&& t) + { + return std::forward(t); + } + + inline + size_t + stringLength(const String& s) + { + return s.length(); + } + + inline + String& + stringAppend(String&s, String a) + { + return s.append(std::move(a)); + } + + inline + String& + stringAppend(String& s, size_t n, char c) + { + return s.append(n, c); + } + + template + String& + stringAppend(String& s, Iterator begin, Iterator end) + { + return s.append(begin, end); + } + + template + std::string + toUTF8String(T&& t) + { + return std::forward(t); + } + + inline + bool + empty(const std::string& s) + { + return s.empty(); + } +} + +//ifdef CXXOPTS_USE_UNICODE +#endif + +namespace cxxopts +{ + namespace + { +#ifdef _WIN32 + const std::string LQUOTE("\'"); + const std::string RQUOTE("\'"); +#else + const std::string LQUOTE("‘"); + const std::string RQUOTE("’"); +#endif + } + + class Value : public std::enable_shared_from_this + { + public: + + virtual ~Value() = default; + + virtual + std::shared_ptr + clone() const = 0; + + virtual void + parse(const std::string& text) const = 0; + + virtual void + parse() const = 0; + + virtual bool + has_default() const = 0; + + virtual bool + is_container() const = 0; + + virtual bool + has_implicit() const = 0; + + virtual std::string + get_default_value() const = 0; + + virtual std::string + get_implicit_value() const = 0; + + virtual std::shared_ptr + default_value(const std::string& value) = 0; + + virtual std::shared_ptr + implicit_value(const std::string& value) = 0; + + virtual std::shared_ptr + no_implicit_value() = 0; + + virtual bool + is_boolean() const = 0; + }; + + class OptionException : public std::exception + { + public: + OptionException(const std::string& message) + : m_message(message) + { + } + + virtual const char* + what() const noexcept + { + return m_message.c_str(); + } + + private: + std::string m_message; + }; + + class OptionSpecException : public OptionException + { + public: + + OptionSpecException(const std::string& message) + : OptionException(message) + { + } + }; + + class OptionParseException : public OptionException + { + public: + OptionParseException(const std::string& message) + : OptionException(message) + { + } + }; + + class option_exists_error : public OptionSpecException + { + public: + option_exists_error(const std::string& option) + : OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists") + { + } + }; + + class invalid_option_format_error : public OptionSpecException + { + public: + invalid_option_format_error(const std::string& format) + : OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE) + { + } + }; + + class option_syntax_exception : public OptionParseException { + public: + option_syntax_exception(const std::string& text) + : OptionParseException("Argument " + LQUOTE + text + RQUOTE + + " starts with a - but has incorrect syntax") + { + } + }; + + class option_not_exists_exception : public OptionParseException + { + public: + option_not_exists_exception(const std::string& option) + : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist") + { + } + }; + + class missing_argument_exception : public OptionParseException + { + public: + missing_argument_exception(const std::string& option) + : OptionParseException( + "Option " + LQUOTE + option + RQUOTE + " is missing an argument" + ) + { + } + }; + + class option_requires_argument_exception : public OptionParseException + { + public: + option_requires_argument_exception(const std::string& option) + : OptionParseException( + "Option " + LQUOTE + option + RQUOTE + " requires an argument" + ) + { + } + }; + + class option_not_has_argument_exception : public OptionParseException + { + public: + option_not_has_argument_exception + ( + const std::string& option, + const std::string& arg + ) + : OptionParseException( + "Option " + LQUOTE + option + RQUOTE + + " does not take an argument, but argument " + + LQUOTE + arg + RQUOTE + " given" + ) + { + } + }; + + class option_not_present_exception : public OptionParseException + { + public: + option_not_present_exception(const std::string& option) + : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present") + { + } + }; + + class argument_incorrect_type : public OptionParseException + { + public: + argument_incorrect_type + ( + const std::string& arg + ) + : OptionParseException( + "Argument " + LQUOTE + arg + RQUOTE + " failed to parse" + ) + { + } + }; + + class option_required_exception : public OptionParseException + { + public: + option_required_exception(const std::string& option) + : OptionParseException( + "Option " + LQUOTE + option + RQUOTE + " is required but not present" + ) + { + } + }; + + template + void throw_or_mimic(const std::string& text) + { + static_assert(std::is_base_of::value, + "throw_or_mimic only works on std::exception and " + "deriving classes"); + +#ifndef CXXOPTS_NO_EXCEPTIONS + // If CXXOPTS_NO_EXCEPTIONS is not defined, just throw + throw T{text}; +#else + // Otherwise manually instantiate the exception, print what() to stderr, + // and abort + T exception{text}; + std::cerr << exception.what() << std::endl; + std::cerr << "Aborting (exceptions disabled)..." << std::endl; + std::abort(); +#endif + } + + namespace values + { + namespace + { + std::basic_regex integer_pattern + ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)"); + std::basic_regex truthy_pattern + ("(t|T)(rue)?|1"); + std::basic_regex falsy_pattern + ("(f|F)(alse)?|0"); + } + + namespace detail + { + template + struct SignedCheck; + + template + struct SignedCheck + { + template + void + operator()(bool negative, U u, const std::string& text) + { + if (negative) + { + if (u > static_cast((std::numeric_limits::min)())) + { + throw_or_mimic(text); + } + } + else + { + if (u > static_cast((std::numeric_limits::max)())) + { + throw_or_mimic(text); + } + } + } + }; + + template + struct SignedCheck + { + template + void + operator()(bool, U, const std::string&) {} + }; + + template + void + check_signed_range(bool negative, U value, const std::string& text) + { + SignedCheck::is_signed>()(negative, value, text); + } + } + + template + R + checked_negate(T&& t, const std::string&, std::true_type) + { + // if we got to here, then `t` is a positive number that fits into + // `R`. So to avoid MSVC C4146, we first cast it to `R`. + // See https://github.com/jarro2783/cxxopts/issues/62 for more details. + return static_cast(-static_cast(t-1)-1); + } + + template + T + checked_negate(T&& t, const std::string& text, std::false_type) + { + throw_or_mimic(text); + return t; + } + + template + void + integer_parser(const std::string& text, T& value) + { + std::smatch match; + std::regex_match(text, match, integer_pattern); + + if (match.length() == 0) + { + throw_or_mimic(text); + } + + if (match.length(4) > 0) + { + value = 0; + return; + } + + using US = typename std::make_unsigned::type; + + constexpr bool is_signed = std::numeric_limits::is_signed; + const bool negative = match.length(1) > 0; + const uint8_t base = match.length(2) > 0 ? 16 : 10; + + auto value_match = match[3]; + + US result = 0; + + for (auto iter = value_match.first; iter != value_match.second; ++iter) + { + US digit = 0; + + if (*iter >= '0' && *iter <= '9') + { + digit = static_cast(*iter - '0'); + } + else if (base == 16 && *iter >= 'a' && *iter <= 'f') + { + digit = static_cast(*iter - 'a' + 10); + } + else if (base == 16 && *iter >= 'A' && *iter <= 'F') + { + digit = static_cast(*iter - 'A' + 10); + } + else + { + throw_or_mimic(text); + } + + const US next = static_cast(result * base + digit); + if (result > next) + { + throw_or_mimic(text); + } + + result = next; + } + + detail::check_signed_range(negative, result, text); + + if (negative) + { + value = checked_negate(result, + text, + std::integral_constant()); + } + else + { + value = static_cast(result); + } + } + + template + void stringstream_parser(const std::string& text, T& value) + { + std::stringstream in(text); + in >> value; + if (!in) { + throw_or_mimic(text); + } + } + + inline + void + parse_value(const std::string& text, uint8_t& value) + { + integer_parser(text, value); + } + + inline + void + parse_value(const std::string& text, int8_t& value) + { + integer_parser(text, value); + } + + inline + void + parse_value(const std::string& text, uint16_t& value) + { + integer_parser(text, value); + } + + inline + void + parse_value(const std::string& text, int16_t& value) + { + integer_parser(text, value); + } + + inline + void + parse_value(const std::string& text, uint32_t& value) + { + integer_parser(text, value); + } + + inline + void + parse_value(const std::string& text, int32_t& value) + { + integer_parser(text, value); + } + + inline + void + parse_value(const std::string& text, uint64_t& value) + { + integer_parser(text, value); + } + + inline + void + parse_value(const std::string& text, int64_t& value) + { + integer_parser(text, value); + } + + inline + void + parse_value(const std::string& text, bool& value) + { + std::smatch result; + std::regex_match(text, result, truthy_pattern); + + if (!result.empty()) + { + value = true; + return; + } + + std::regex_match(text, result, falsy_pattern); + if (!result.empty()) + { + value = false; + return; + } + + throw_or_mimic(text); + } + + inline + void + parse_value(const std::string& text, std::string& value) + { + value = text; + } + + // The fallback parser. It uses the stringstream parser to parse all types + // that have not been overloaded explicitly. It has to be placed in the + // source code before all other more specialized templates. + template + void + parse_value(const std::string& text, T& value) { + stringstream_parser(text, value); + } + + template + void + parse_value(const std::string& text, std::vector& value) + { + std::stringstream in(text); + std::string token; + while(in.eof() == false && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) { + T v; + parse_value(token, v); + value.emplace_back(std::move(v)); + } + } + +#ifdef CXXOPTS_HAS_OPTIONAL + template + void + parse_value(const std::string& text, std::optional& value) + { + T result; + parse_value(text, result); + value = std::move(result); + } +#endif + + inline + void parse_value(const std::string& text, char& c) + { + if (text.length() != 1) + { + throw_or_mimic(text); + } + + c = text[0]; + } + + template + struct type_is_container + { + static constexpr bool value = false; + }; + + template + struct type_is_container> + { + static constexpr bool value = true; + }; + + template + class abstract_value : public Value + { + using Self = abstract_value; + + public: + abstract_value() + : m_result(std::make_shared()) + , m_store(m_result.get()) + { + } + + abstract_value(T* t) + : m_store(t) + { + } + + virtual ~abstract_value() = default; + + abstract_value(const abstract_value& rhs) + { + if (rhs.m_result) + { + m_result = std::make_shared(); + m_store = m_result.get(); + } + else + { + m_store = rhs.m_store; + } + + m_default = rhs.m_default; + m_implicit = rhs.m_implicit; + m_default_value = rhs.m_default_value; + m_implicit_value = rhs.m_implicit_value; + } + + void + parse(const std::string& text) const + { + parse_value(text, *m_store); + } + + bool + is_container() const + { + return type_is_container::value; + } + + void + parse() const + { + parse_value(m_default_value, *m_store); + } + + bool + has_default() const + { + return m_default; + } + + bool + has_implicit() const + { + return m_implicit; + } + + std::shared_ptr + default_value(const std::string& value) + { + m_default = true; + m_default_value = value; + return shared_from_this(); + } + + std::shared_ptr + implicit_value(const std::string& value) + { + m_implicit = true; + m_implicit_value = value; + return shared_from_this(); + } + + std::shared_ptr + no_implicit_value() + { + m_implicit = false; + return shared_from_this(); + } + + std::string + get_default_value() const + { + return m_default_value; + } + + std::string + get_implicit_value() const + { + return m_implicit_value; + } + + bool + is_boolean() const + { + return std::is_same::value; + } + + const T& + get() const + { + if (m_store == nullptr) + { + return *m_result; + } + else + { + return *m_store; + } + } + + protected: + std::shared_ptr m_result; + T* m_store; + + bool m_default = false; + bool m_implicit = false; + + std::string m_default_value; + std::string m_implicit_value; + }; + + template + class standard_value : public abstract_value + { + public: + using abstract_value::abstract_value; + + std::shared_ptr + clone() const + { + return std::make_shared>(*this); + } + }; + + template <> + class standard_value : public abstract_value + { + public: + ~standard_value() = default; + + standard_value() + { + set_default_and_implicit(); + } + + standard_value(bool* b) + : abstract_value(b) + { + set_default_and_implicit(); + } + + std::shared_ptr + clone() const + { + return std::make_shared>(*this); + } + + private: + + void + set_default_and_implicit() + { + m_default = true; + m_default_value = "false"; + m_implicit = true; + m_implicit_value = "true"; + } + }; + } + + template + std::shared_ptr + value() + { + return std::make_shared>(); + } + + template + std::shared_ptr + value(T& t) + { + return std::make_shared>(&t); + } + + class OptionAdder; + + class OptionDetails + { + public: + OptionDetails + ( + const std::string& short_, + const std::string& long_, + const String& desc, + std::shared_ptr val + ) + : m_short(short_) + , m_long(long_) + , m_desc(desc) + , m_value(val) + , m_count(0) + { + } + + OptionDetails(const OptionDetails& rhs) + : m_desc(rhs.m_desc) + , m_count(rhs.m_count) + { + m_value = rhs.m_value->clone(); + } + + OptionDetails(OptionDetails&& rhs) = default; + + const String& + description() const + { + return m_desc; + } + + const Value& value() const { + return *m_value; + } + + std::shared_ptr + make_storage() const + { + return m_value->clone(); + } + + const std::string& + short_name() const + { + return m_short; + } + + const std::string& + long_name() const + { + return m_long; + } + + private: + std::string m_short; + std::string m_long; + String m_desc; + std::shared_ptr m_value; + int m_count; + }; + + struct HelpOptionDetails + { + std::string s; + std::string l; + String desc; + bool has_default; + std::string default_value; + bool has_implicit; + std::string implicit_value; + std::string arg_help; + bool is_container; + bool is_boolean; + }; + + struct HelpGroupDetails + { + std::string name; + std::string description; + std::vector options; + }; + + class OptionValue + { + public: + void + parse + ( + std::shared_ptr details, + const std::string& text + ) + { + ensure_value(details); + ++m_count; + m_value->parse(text); + } + + void + parse_default(std::shared_ptr details) + { + ensure_value(details); + m_default = true; + m_value->parse(); + } + + size_t + count() const noexcept + { + return m_count; + } + + // TODO: maybe default options should count towards the number of arguments + bool + has_default() const noexcept + { + return m_default; + } + + template + const T& + as() const + { + if (m_value == nullptr) { + throw_or_mimic("No value"); + } + +#ifdef CXXOPTS_NO_RTTI + return static_cast&>(*m_value).get(); +#else + return dynamic_cast&>(*m_value).get(); +#endif + } + + private: + void + ensure_value(std::shared_ptr details) + { + if (m_value == nullptr) + { + m_value = details->make_storage(); + } + } + + std::shared_ptr m_value; + size_t m_count = 0; + bool m_default = false; + }; + + class KeyValue + { + public: + KeyValue(std::string key_, std::string value_) + : m_key(std::move(key_)) + , m_value(std::move(value_)) + { + } + + const + std::string& + key() const + { + return m_key; + } + + const + std::string& + value() const + { + return m_value; + } + + template + T + as() const + { + T result; + values::parse_value(m_value, result); + return result; + } + + private: + std::string m_key; + std::string m_value; + }; + + class ParseResult + { + public: + + ParseResult( + const std::shared_ptr< + std::unordered_map> + >, + std::vector, + bool allow_unrecognised, + int&, char**&); + + size_t + count(const std::string& o) const + { + auto iter = m_options->find(o); + if (iter == m_options->end()) + { + return 0; + } + + auto riter = m_results.find(iter->second); + + return riter->second.count(); + } + + const OptionValue& + operator[](const std::string& option) const + { + auto iter = m_options->find(option); + + if (iter == m_options->end()) + { + throw_or_mimic(option); + } + + auto riter = m_results.find(iter->second); + + return riter->second; + } + + const std::vector& + arguments() const + { + return m_sequential; + } + + private: + + void + parse(int& argc, char**& argv); + + void + add_to_option(const std::string& option, const std::string& arg); + + bool + consume_positional(std::string a); + + void + parse_option + ( + std::shared_ptr value, + const std::string& name, + const std::string& arg = "" + ); + + void + parse_default(std::shared_ptr details); + + void + checked_parse_arg + ( + int argc, + char* argv[], + int& current, + std::shared_ptr value, + const std::string& name + ); + + const std::shared_ptr< + std::unordered_map> + > m_options; + std::vector m_positional; + std::vector::iterator m_next_positional; + std::unordered_set m_positional_set; + std::unordered_map, OptionValue> m_results; + + bool m_allow_unrecognised; + + std::vector m_sequential; + }; + + struct Option + { + Option + ( + const std::string& opts, + const std::string& desc, + const std::shared_ptr& value = ::cxxopts::value(), + const std::string& arg_help = "" + ) + : opts_(opts) + , desc_(desc) + , value_(value) + , arg_help_(arg_help) + { + } + + std::string opts_; + std::string desc_; + std::shared_ptr value_; + std::string arg_help_; + }; + + class Options + { + typedef std::unordered_map> + OptionMap; + public: + + Options(std::string program, std::string help_string = "") + : m_program(std::move(program)) + , m_help_string(toLocalString(std::move(help_string))) + , m_custom_help("[OPTION...]") + , m_positional_help("positional parameters") + , m_show_positional(false) + , m_allow_unrecognised(false) + , m_options(std::make_shared()) + , m_next_positional(m_positional.end()) + { + } + + Options& + positional_help(std::string help_text) + { + m_positional_help = std::move(help_text); + return *this; + } + + Options& + custom_help(std::string help_text) + { + m_custom_help = std::move(help_text); + return *this; + } + + Options& + show_positional_help() + { + m_show_positional = true; + return *this; + } + + Options& + allow_unrecognised_options() + { + m_allow_unrecognised = true; + return *this; + } + + ParseResult + parse(int& argc, char**& argv); + + OptionAdder + add_options(std::string group = ""); + + void + add_options + ( + const std::string& group, + std::initializer_list