From 4921a9ea791de133a72a2804c49cf7b08cdd2d38 Mon Sep 17 00:00:00 2001 From: redtide Date: Fri, 16 Apr 2021 20:33:27 +0200 Subject: [PATCH 001/193] Misc Doxygen fixes --- scripts/doxygen/Doxyfile.in | 6 +++--- scripts/doxygen/doxy2json.py | 6 +++++- src/sfizz.h | 30 +++++++++++++++--------------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/scripts/doxygen/Doxyfile.in b/scripts/doxygen/Doxyfile.in index baccc1518..584d6f692 100644 --- a/scripts/doxygen/Doxyfile.in +++ b/scripts/doxygen/Doxyfile.in @@ -259,9 +259,9 @@ 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" \ - "null=NULL" +ALIASES = "true=\true\" \ + "false=\false\" \ + "null=\nullptr\" # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For diff --git a/scripts/doxygen/doxy2json.py b/scripts/doxygen/doxy2json.py index 4ca1aa695..a6d0332cf 100644 --- a/scripts/doxygen/doxy2json.py +++ b/scripts/doxygen/doxy2json.py @@ -22,7 +22,11 @@ - Tags in ALIASES list needs to be escaped (parsed by tag_list_as_string()) to avoid Doxygen convert them in its own tag structure, by loosing some - details (e.g.: true becomes true). + details (e.g.: true becomes true, + use \ as escaping and replace double quotes with apostrophe instead). + + - Some link references are lost like in + `sfizz_export_midnam` -> `sfizz_free_memory` - Merge the work done previously to be able to fully automate the process to be used in various CIs. diff --git a/src/sfizz.h b/src/sfizz.h index 1c1526f1b..bc578a912 100644 --- a/src/sfizz.h +++ b/src/sfizz.h @@ -510,7 +510,7 @@ SFIZZ_EXPORTED_API void sfizz_send_hd_pitch_wheel(sfizz_synth_t* synth, int dela * * @param synth 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(). + * than the size of the block in the next call to sfizz_render_block(). * @param aftertouch The aftertouch value, in domain 0 to 127. * * @par Thread-safety constraints @@ -528,7 +528,7 @@ SFIZZ_EXPORTED_API SFIZZ_DEPRECATED_API void sfizz_send_aftertouch(sfizz_synth_t * * @param synth 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(). + * than the size of the block in the next call to sfizz_render_block(). * @param aftertouch The aftertouch value, in domain 0 to 127. * * @par Thread-safety constraints @@ -546,7 +546,7 @@ SFIZZ_EXPORTED_API void sfizz_send_channel_aftertouch(sfizz_synth_t* synth, int * * @param synth 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(). + * than the size of the block in the next call to sfizz_render_block(). * @param aftertouch The normalized aftertouch value, in domain 0 to 1. * * @par Thread-safety constraints @@ -565,7 +565,7 @@ SFIZZ_EXPORTED_API void sfizz_send_hd_channel_aftertouch(sfizz_synth_t* synth, i * * @param synth 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(). + * than the size of the block in the next call to sfizz_render_block(). * @param note_number The note number, in domain 0 to 127. * @param aftertouch The aftertouch value, in domain 0 to 127. * @@ -585,7 +585,7 @@ SFIZZ_EXPORTED_API void sfizz_send_poly_aftertouch(sfizz_synth_t* synth, int del * * @param synth 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(). + * than the size of the block in the next call to sfizz_render_block(). * @param note_number The note number, in domain 0 to 127. * @param aftertouch The normalized aftertouch value, in domain 0 to 1. * @@ -598,8 +598,8 @@ SFIZZ_EXPORTED_API void sfizz_send_hd_poly_aftertouch(sfizz_synth_t* synth, int * @brief Send a tempo event. * * This command should be delay-ordered with all other time/signature commands, namely - * tempo(), timeSignature(), timePosition(), and playbackState(), otherwise the behavior - * of the synth is undefined. + * sfizz_send_tempo(), sfizz_send_time_signature(), sfizz_send_time_position(), + * and sfizz_send_playback_state(), otherwise the behavior of the synth is undefined. * * @since 0.2.0 * @@ -616,8 +616,8 @@ SFIZZ_EXPORTED_API SFIZZ_DEPRECATED_API void sfizz_send_tempo(sfizz_synth_t* syn * @brief Send a tempo event. * * This command should be delay-ordered with all other time/signature commands, namely - * tempo(), timeSignature(), timePosition(), and playbackState(), otherwise the behavior - * of the synth is undefined. + * sfizz_send_tempo(), sfizz_send_time_signature(), sfizz_send_time_position(), + * and sfizz_send_playback_state(), otherwise the behavior of the synth is undefined. * * @since 1.0.0 * @@ -634,8 +634,8 @@ SFIZZ_EXPORTED_API void sfizz_send_bpm_tempo(sfizz_synth_t* synth, int delay, fl * @brief Send the time signature. * * This command should be delay-ordered with all other time/signature commands, namely - * tempo(), timeSignature(), timePosition(), and playbackState(), otherwise the behavior - * of the synth is undefined. + * sfizz_send_tempo(), sfizz_send_time_signature(), sfizz_send_time_position(), + * and sfizz_send_playback_state(), otherwise the behavior of the synth is undefined. * * @since 0.5.0 * @@ -653,8 +653,8 @@ SFIZZ_EXPORTED_API void sfizz_send_time_signature(sfizz_synth_t* synth, int dela * @brief Send the time position. * * This command should be delay-ordered with all other time/signature commands, namely - * tempo(), timeSignature(), timePosition(), and playbackState(), otherwise the behavior - * of the synth is undefined. + * sfizz_send_tempo(), sfizz_send_time_signature(), sfizz_send_time_position(), + * and sfizz_send_playback_state(), otherwise the behavior of the synth is undefined. * * @since 0.5.0 * @@ -672,8 +672,8 @@ SFIZZ_EXPORTED_API void sfizz_send_time_position(sfizz_synth_t* synth, int delay * @brief Send the playback state. * * This command should be delay-ordered with all other time/signature commands, namely - * tempo(), timeSignature(), timePosition(), and playbackState(), otherwise the behavior - * of the synth is undefined. + * sfizz_send_tempo(), sfizz_send_time_signature(), sfizz_send_time_position(), + * and sfizz_send_playback_state(), otherwise the behavior of the synth is undefined. * * @since 0.5.0 * From 5da137a8d6704cb27fbe8bb9ff75abfc7c181366 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 16 Apr 2021 21:01:56 +0200 Subject: [PATCH 002/193] Add deployments for Windows installers --- .github/workflows/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 510768370..3e460e95e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -436,6 +436,12 @@ jobs: - uses: actions/download-artifact@v2 with: name: Win64 MinGW tarball + - uses: actions/download-artifact@v2 + with: + name: Win32 installer + - uses: actions/download-artifact@v2 + with: + name: Win64 installer - uses: actions/download-artifact@v2 with: name: Source code tarball From cea8981f745d18ed7d3881fe4c52161f4d5344d5 Mon Sep 17 00:00:00 2001 From: redtide Date: Sat, 17 Apr 2021 11:21:22 +0200 Subject: [PATCH 003/193] Update README about audio libraries --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index da36e532e..a17d276d3 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,14 @@ We invite you to check out the [GOVERNANCE](GOVERNANCE.md) file to see how the o ## Dependencies and licenses +The sfizz library has the option to be compiled against either the `dr_libs` +audio libraries, which is now the default option, or `libsndfile`. + +- [dr_libs] is licensed under the MIT No Attribution license +- [libsndfile] is licensed under the GNU Lesser General Public License v2.1 + The sfizz library makes primary use of: -- [libsndfile], licensed under the GNU Lesser General Public License v2.1 - [Abseil], licensed under the Apache License 2.0 - [atomic_queue] by Maxim Egorushkin, licensed under the MIT license - [filesystem] by Steffen Schümann, licensed under the BSD 3-Clause license From 9c7eca288dad2af0b74c72e2761e6b418b6a57be Mon Sep 17 00:00:00 2001 From: redtide Date: Sat, 17 Apr 2021 11:27:15 +0200 Subject: [PATCH 004/193] Missing link in README [ci skip] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a17d276d3..845bbc625 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ The sfizz library also uses in some subprojects: [pugixml]: https://pugixml.org/ [cephes]: https://www.netlib.org/cephes/ [cpuid]: https://github.com/steinwurf/cpuid +[dr_libs]: https://github.com/mackron/dr_libs [faust-libraries]: https://github.com/grame-cncm/faustlibraries [hiir]: http://ldesoras.free.fr/prod.html#src_hiir [KISS FFT]: http://kissfft.sourceforge.net/ From f8f41fab1fe965bdb8dfff79dd2b57dba100c939 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 17 Apr 2021 21:02:38 +0200 Subject: [PATCH 005/193] Fix MSVC duplicate target sfizz.lib --- src/CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6c36e909e..7294623e8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -315,7 +315,12 @@ add_library(sfizz_static STATIC sfizz/sfizz_wrapper.cpp sfizz/sfizz.cpp sfizz/sf add_library(sfizz::static ALIAS sfizz_static) target_include_directories(sfizz_static PUBLIC .) target_link_libraries(sfizz_static PRIVATE sfizz::internal) -set_target_properties(sfizz_static PROPERTIES OUTPUT_NAME "sfizz" PUBLIC_HEADER "sfizz.h;sfizz.hpp;sfizz_message.h") +set_target_properties(sfizz_static PROPERTIES PUBLIC_HEADER "sfizz.h;sfizz.hpp;sfizz_message.h") +if(MSVC) + set_target_properties(sfizz_static PROPERTIES OUTPUT_NAME "sfizz_static") +else() + set_target_properties(sfizz_static PROPERTIES OUTPUT_NAME "sfizz") +endif() # Shared library and installation target if(SFIZZ_SHARED) From 57eb4ca87af777129985c5940147578a3e2be8e7 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 20 Apr 2021 12:28:47 +0200 Subject: [PATCH 006/193] Use better variables for the pkg-config file The previous version created problems when setting manually CMAKE_INSTALL_LIBDIR on configure, e.g. for OBS/Debian packages. The `libdir` value ended up as `libdir=${exec_prefix}//usr/lib` in this case, leading pkg-config to complain (obviously). --- scripts/sfizz.pc.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/sfizz.pc.in b/scripts/sfizz.pc.in index 145f9db84..0d294c590 100644 --- a/scripts/sfizz.pc.in +++ b/scripts/sfizz.pc.in @@ -1,7 +1,7 @@ prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@ -libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ -includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ Name: @PROJECT_NAME@ Description: @PROJECT_DESCRIPTION@ From e2c667c42fc95d1be4421216da1af897afb19a73 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 24 Apr 2021 16:42:46 +0200 Subject: [PATCH 007/193] Store extended CCs in the midistate for those targets that do not rely on the modmatrix --- src/sfizz/Layer.cpp | 4 +- src/sfizz/MidiState.cpp | 23 ++++-- src/sfizz/MidiState.h | 18 ++--- src/sfizz/Synth.cpp | 5 +- src/sfizz/SynthMessaging.cpp | 10 +++ src/sfizz/Voice.cpp | 24 ++++-- src/sfizz/Voice.h | 13 +++ tests/MidiStateT.cpp | 149 ++++++++++++++++++++++++++++++++++- 8 files changed, 218 insertions(+), 28 deletions(-) diff --git a/src/sfizz/Layer.cpp b/src/sfizz/Layer.cpp index 16a1b745c..146d21816 100644 --- a/src/sfizz/Layer.cpp +++ b/src/sfizz/Layer.cpp @@ -69,7 +69,7 @@ bool Layer::registerNoteOn(int noteNumber, float velocity, float randValue) noex return false; if (region.velocityOverride == VelocityOverride::previous) - velocity = midiState_.getLastVelocity(); + velocity = midiState_.getVelocityOverride(); const bool velOk = region.velocityRange.containsWithEnd(velocity); const bool randOk = region.randRange.contains(randValue) || (randValue >= 1.0f && region.randRange.isValid() && region.randRange.getEnd() >= 1.0f); @@ -131,8 +131,6 @@ bool Layer::registerNoteOff(int noteNumber, float velocity, float randValue) noe bool Layer::registerCC(int ccNumber, float ccValue) noexcept { - ASSERT(ccValue >= 0.0f && ccValue <= 1.0f); - const Region& region = region_; if (ccNumber == region.sustainCC) diff --git a/src/sfizz/MidiState.cpp b/src/sfizz/MidiState.cpp index e077dfad1..e82c3dba9 100644 --- a/src/sfizz/MidiState.cpp +++ b/src/sfizz/MidiState.cpp @@ -19,11 +19,19 @@ void sfz::MidiState::noteOnEvent(int delay, int noteNumber, float velocity) noex ASSERT(velocity >= 0 && velocity <= 1.0); if (noteNumber >= 0 && noteNumber < 128) { + velocityOverride = lastNoteVelocities[lastNotePlayed]; lastNoteVelocities[noteNumber] = velocity; noteOnTimes[noteNumber] = internalClock + static_cast(delay); lastNotePlayed = noteNumber; - activeNotes++; noteStates[noteNumber] = true; + ccEvent(delay, ExtendedCCs::noteOnVelocity, velocity); + ccEvent(delay, ExtendedCCs::keyboardNoteNumber, normalize7Bits(noteNumber)); + ccEvent(delay, ExtendedCCs::unipolarRandom, unipolarDist(Random::randomGenerator)); + ccEvent(delay, ExtendedCCs::bipolarRandom, bipolarDist(Random::randomGenerator)); + ccEvent(delay, ExtendedCCs::keyboardNoteGate, activeNotes > 0 ? 1.0f : 0.0f); + activeNotes++; + + ccEvent(delay, ExtendedCCs::alternate, alternate); alternate = alternate == 0.0f ? 1.0f : 0.0f; } @@ -37,6 +45,10 @@ void sfz::MidiState::noteOffEvent(int delay, int noteNumber, float velocity) noe UNUSED(velocity); if (noteNumber >= 0 && noteNumber < 128) { noteOffTimes[noteNumber] = internalClock + static_cast(delay); + ccEvent(delay, ExtendedCCs::noteOffVelocity, velocity); + ccEvent(delay, ExtendedCCs::keyboardNoteNumber, normalize7Bits(noteNumber)); + ccEvent(delay, ExtendedCCs::unipolarRandom, unipolarDist(Random::randomGenerator)); + ccEvent(delay, ExtendedCCs::bipolarRandom, bipolarDist(Random::randomGenerator)); if (activeNotes > 0) activeNotes--; noteStates[noteNumber] = false; @@ -121,9 +133,9 @@ float sfz::MidiState::getNoteVelocity(int noteNumber) const noexcept return lastNoteVelocities[noteNumber]; } -float sfz::MidiState::getLastVelocity() const noexcept +float sfz::MidiState::getVelocityOverride() const noexcept { - return lastNoteVelocities[lastNotePlayed]; + return velocityOverride; } void sfz::MidiState::insertEventInVector(EventVector& events, int delay, float value) @@ -172,7 +184,7 @@ float sfz::MidiState::getPolyAftertouch(int noteNumber) const noexcept { if (noteNumber < 0 || noteNumber > 127) return 0.0f; - + ASSERT(polyAftertouchEvents[noteNumber].size() > 0); return polyAftertouchEvents[noteNumber].back().value; } @@ -191,7 +203,7 @@ float sfz::MidiState::getCCValue(int ccNumber) const noexcept void sfz::MidiState::reset() noexcept { for (auto& velocity: lastNoteVelocities) - velocity = 0; + velocity = 0.0f; auto clearEvents = [] (EventVector& events) { events.clear(); @@ -207,6 +219,7 @@ void sfz::MidiState::reset() noexcept clearEvents(pitchEvents); clearEvents(channelAftertouchEvents); + velocityOverride = 0.0f; activeNotes = 0; internalClock = 0; lastNotePlayed = 0; diff --git a/src/sfizz/MidiState.h b/src/sfizz/MidiState.h index 3dab87bfb..c92f45a45 100644 --- a/src/sfizz/MidiState.h +++ b/src/sfizz/MidiState.h @@ -86,11 +86,11 @@ class MidiState float getNoteVelocity(int noteNumber) const noexcept; /** - * @brief Get the velocity of the last note played + * @brief Get the velocity override value (sw_vel in SFZ) * * @return float */ - float getLastVelocity() const noexcept; + float getVelocityOverride() const noexcept; /** * @brief Register a pitch bend event @@ -190,13 +190,6 @@ class MidiState const EventVector& getPitchEvents() const noexcept; const EventVector& getChannelAftertouchEvents() const noexcept; - /** - * @brief Get the alternate state value, for extended CC 137 - * - * @return float - */ - float getAlternateState() const noexcept { return alternate; } - private: /** @@ -236,6 +229,11 @@ class MidiState */ MidiNoteArray lastNoteVelocities; + /** + * @brief Velocity override value (sw_vel in SFZ) + */ + float velocityOverride; + /** * @brief Last note played */ @@ -272,5 +270,7 @@ class MidiState int samplesPerBlock { config::defaultSamplesPerBlock }; float alternate { 0.0f }; unsigned internalClock { 0 }; + fast_real_distribution unipolarDist { 0.0f, 1.0f }; + fast_real_distribution bipolarDist { -1.0f, 1.0f }; }; } diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 77173a4ed..feb5621b3 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -1073,8 +1073,8 @@ void Synth::hdNoteOn(int delay, int noteNumber, float normalizedVelocity) noexce ASSERT(noteNumber >= 0); Impl& impl = *impl_; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; - impl.noteOnDispatch(delay, noteNumber, normalizedVelocity); impl.resources_.midiState.noteOnEvent(delay, noteNumber, normalizedVelocity); + impl.noteOnDispatch(delay, noteNumber, normalizedVelocity); } void Synth::noteOff(int delay, int noteNumber, int velocity) noexcept @@ -1087,20 +1087,19 @@ void Synth::hdNoteOff(int delay, int noteNumber, float normalizedVelocity) noexc { ASSERT(noteNumber < 128); ASSERT(noteNumber >= 0); - UNUSED(normalizedVelocity); Impl& impl = *impl_; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; // 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? // auto replacedVelocity = (velocity == 0 ? getNoteVelocity(noteNumber) : velocity); + impl.resources_.midiState.noteOffEvent(delay, noteNumber, normalizedVelocity); const auto replacedVelocity = impl.resources_.midiState.getNoteVelocity(noteNumber); for (auto& voice : impl.voiceManager_) voice.registerNoteOff(delay, noteNumber, replacedVelocity); impl.noteOffDispatch(delay, noteNumber, replacedVelocity); - impl.resources_.midiState.noteOffEvent(delay, noteNumber, normalizedVelocity); } void Synth::Impl::startVoice(Layer* layer, int delay, const TriggerEvent& triggerEvent, SisterVoiceRingBuilder& ring) noexcept diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index 14353f88d..acd07c82c 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -1454,6 +1454,16 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co } break; + MATCH("/voice&/remaining_delay", "") { + GET_VOICE_OR_BREAK(indices[0]) + client.receive<'i'>(delay, path, voice.getRemainingDelay()); + } break; + + MATCH("/voice&/source_position", "") { + GET_VOICE_OR_BREAK(indices[0]) + client.receive<'i'>(delay, path, voice.getSourcePosition()); + } break; + #undef MATCH // TODO... } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 64e6589a9..10b699328 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -304,8 +304,6 @@ struct Voice::Impl PowerFollower powerFollower_; ExtendedCCValues extendedCCValues_; - fast_real_distribution unipolarDist { 0.0f, 1.0f }; - fast_real_distribution bipolarDist { -1.0f, 1.0f }; }; Voice::Voice(int voiceNumber, Resources& resources) @@ -389,10 +387,10 @@ const ExtendedCCValues& Voice::getExtendedCCValues() const noexcept void Voice::Impl::updateExtendedCCValues() noexcept { - extendedCCValues_.unipolar = unipolarDist(Random::randomGenerator); - extendedCCValues_.bipolar = bipolarDist(Random::randomGenerator); - extendedCCValues_.alternate = resources_.midiState.getAlternateState(); - extendedCCValues_.noteGate = resources_.midiState.getActiveNotes() > 0 ? 1.0f : 0.0f; + extendedCCValues_.unipolar = resources_.midiState.getCCValue(ExtendedCCs::unipolarRandom); + extendedCCValues_.bipolar = resources_.midiState.getCCValue(ExtendedCCs::bipolarRandom); + extendedCCValues_.alternate = resources_.midiState.getCCValue(ExtendedCCs::alternate); + extendedCCValues_.noteGate = resources_.midiState.getCCValue(ExtendedCCs::keyboardNoteGate); } bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexcept @@ -410,7 +408,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc impl.triggerEvent_.number = region.pitchKeycenter; if (region.velocityOverride == VelocityOverride::previous) - impl.triggerEvent_.value = resources.midiState.getLastVelocity(); + impl.triggerEvent_.value = resources.midiState.getVelocityOverride(); if (region.disabled()) { impl.switchState(State::cleanMeUp); @@ -2000,6 +1998,18 @@ const Region* Voice::getRegion() const noexcept return impl.region_; } +int Voice::getRemainingDelay() const noexcept +{ + Impl& impl = *impl_; + return impl.initialDelay_; +} + +int Voice::getSourcePosition() const noexcept +{ + Impl& impl = *impl_; + return impl.sourcePosition_; +} + LFO* Voice::getLFO(size_t index) { Impl& impl = *impl_; diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 9e7f3593c..0643004f0 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -416,6 +416,19 @@ class Voice { */ const ExtendedCCValues& getExtendedCCValues() const noexcept; + /** + * @brief Get the remaining delay before the sample starts, in samples + * + * @return int + */ + int getRemainingDelay() const noexcept; + /** + * @brief Get the current position in the source sample + * + * @return int + */ + int getSourcePosition() const noexcept; + public: /** * @brief Check if the voice already belongs to a sister ring diff --git a/tests/MidiStateT.cpp b/tests/MidiStateT.cpp index 5bec78eeb..8bb44d40a 100644 --- a/tests/MidiStateT.cpp +++ b/tests/MidiStateT.cpp @@ -11,9 +11,11 @@ */ #include "sfizz/MidiState.h" +#include "sfizz/Synth.h" #include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" #include "absl/strings/string_view.h" +#include "TestHelpers.h" using namespace Catch::literals; using namespace sfz::literals; @@ -88,5 +90,150 @@ TEST_CASE("[MidiState] Last note velocity") sfz::MidiState state; state.noteOnEvent(0, 62, 64_norm); state.noteOnEvent(0, 60, 10_norm); - REQUIRE(state.getLastVelocity() == 10_norm); + REQUIRE(state.getVelocityOverride() == 64_norm); +} + + +TEST_CASE("[CC] Extended CCs on offset and delay") +{ + sfz::Synth synth; + std::vector messageList; + sfz::Client client(&messageList); + client.setReceiveCallback(&simpleMessageReceiver); + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + synth.setSampleRate(48000); + + SECTION("CC131 - Note on velocity") + { + synth.loadSfzString(fs::current_path() / "tests/TestFiles/extended_ccs.sfz", R"( + key=60 delay_cc131=1 sample=kick.wav + key=61 offset_cc131=100 sample=snare.wav + )"); + synth.hdNoteOn(0, 60, 0.0f); + synth.hdNoteOn(0, 60, 0.5f); + synth.hdNoteOn(0, 61, 0.0f); + synth.hdNoteOn(0, 61, 0.5f); + synth.dispatchMessage(client, 0, "/voice0/remaining_delay", "", nullptr); + synth.dispatchMessage(client, 0, "/voice1/remaining_delay", "", nullptr); + synth.dispatchMessage(client, 0, "/voice2/source_position", "", nullptr); + synth.dispatchMessage(client, 0, "/voice3/source_position", "", nullptr); + std::vector expected { + "/voice0/remaining_delay,i : { 0 }", + "/voice1/remaining_delay,i : { 24000 }", + "/voice2/source_position,i : { 0 }", + "/voice3/source_position,i : { 50 }", + }; + REQUIRE(messageList == expected); + } + + SECTION("CC132 - Note off velocity") + { + synth.loadSfzString(fs::current_path() / "tests/TestFiles/extended_ccs.sfz", R"( + key=60 sample=*silence + key=60 delay_cc132=1 sample=kick.wav trigger=release + key=61 sample=snare.wav + key=61 offset_cc132=100 sample=snare.wav trigger=release + )"); + synth.hdNoteOn(0, 60, 1.0f); + synth.hdNoteOff(1, 60, 0.0f); + synth.hdNoteOn(2, 60, 1.0f); + synth.hdNoteOff(3, 60, 0.5f); + synth.hdNoteOn(4, 61, 1.0f); + synth.hdNoteOff(5, 61, 0.0f); + synth.hdNoteOn(6, 61, 1.0f); + synth.hdNoteOff(7, 61, 0.5f); + synth.dispatchMessage(client, 10, "/voice1/remaining_delay", "", nullptr); + synth.dispatchMessage(client, 10, "/voice3/remaining_delay", "", nullptr); + synth.dispatchMessage(client, 10, "/voice5/source_position", "", nullptr); + synth.dispatchMessage(client, 10, "/voice7/source_position", "", nullptr); + std::vector expected { + "/voice1/remaining_delay,i : { 1 }", // 1 is the note off event delay + "/voice3/remaining_delay,i : { 24003 }", // 3 is the note off event delay + "/voice5/source_position,i : { 0 }", + "/voice7/source_position,i : { 50 }", + }; + REQUIRE(messageList == expected); + } + + SECTION("CC133 - Note number") + { + synth.loadSfzString(fs::current_path() / "tests/TestFiles/extended_ccs.sfz", R"( + delay_cc133=1 offset_cc133=100 sample=kick.wav + )"); + synth.hdNoteOn(0, 0, 1.0f); + synth.hdNoteOn(0, 127, 1.0f); + synth.dispatchMessage(client, 0, "/voice0/remaining_delay", "", nullptr); + synth.dispatchMessage(client, 0, "/voice1/remaining_delay", "", nullptr); + synth.dispatchMessage(client, 0, "/voice0/source_position", "", nullptr); + synth.dispatchMessage(client, 0, "/voice1/source_position", "", nullptr); + std::vector expected { + "/voice0/remaining_delay,i : { 0 }", + "/voice1/remaining_delay,i : { 48000 }", + "/voice0/source_position,i : { 0 }", + "/voice1/source_position,i : { 100 }", + }; + REQUIRE(messageList == expected); + } + + SECTION("CC134 - Note gate") + { + synth.loadSfzString(fs::current_path() / "tests/TestFiles/extended_ccs.sfz", R"( + delay_cc134=1 offset_cc134=100 sample=kick.wav + )"); + synth.hdNoteOn(0, 60, 1.0f); + synth.hdNoteOn(0, 127, 1.0f); + synth.hdNoteOff(1, 60, 1.0f); + synth.hdNoteOff(1, 127, 1.0f); + synth.hdNoteOn(2, 60, 1.0f); + synth.hdNoteOn(2, 127, 1.0f); + synth.dispatchMessage(client, 0, "/voice0/remaining_delay", "", nullptr); + synth.dispatchMessage(client, 0, "/voice1/remaining_delay", "", nullptr); + synth.dispatchMessage(client, 0, "/voice2/remaining_delay", "", nullptr); + synth.dispatchMessage(client, 0, "/voice3/remaining_delay", "", nullptr); + synth.dispatchMessage(client, 0, "/voice0/source_position", "", nullptr); + synth.dispatchMessage(client, 0, "/voice1/source_position", "", nullptr); + synth.dispatchMessage(client, 0, "/voice2/source_position", "", nullptr); + synth.dispatchMessage(client, 0, "/voice3/source_position", "", nullptr); + std::vector expected { + "/voice0/remaining_delay,i : { 0 }", + "/voice1/remaining_delay,i : { 48000 }", + "/voice2/remaining_delay,i : { 2 }", // 2 is the event delay + "/voice3/remaining_delay,i : { 48002 }", // 2 is the event delay + "/voice0/source_position,i : { 0 }", + "/voice1/source_position,i : { 100 }", + "/voice2/source_position,i : { 0 }", + "/voice3/source_position,i : { 100 }", + }; + REQUIRE(messageList == expected); + } + + SECTION("CC137 - Alternate") + { + synth.loadSfzString(fs::current_path() / "tests/TestFiles/extended_ccs.sfz", R"( + delay_cc137=1 offset_cc137=100 sample=kick.wav + )"); + synth.hdNoteOn(0, 60, 1.0f); + synth.hdNoteOn(0, 127, 1.0f); + synth.hdNoteOn(0, 54, 1.0f); + synth.hdNoteOn(0, 12, 1.0f); + synth.dispatchMessage(client, 0, "/voice0/remaining_delay", "", nullptr); + synth.dispatchMessage(client, 0, "/voice1/remaining_delay", "", nullptr); + synth.dispatchMessage(client, 0, "/voice2/remaining_delay", "", nullptr); + synth.dispatchMessage(client, 0, "/voice3/remaining_delay", "", nullptr); + synth.dispatchMessage(client, 0, "/voice0/source_position", "", nullptr); + synth.dispatchMessage(client, 0, "/voice1/source_position", "", nullptr); + synth.dispatchMessage(client, 0, "/voice2/source_position", "", nullptr); + synth.dispatchMessage(client, 0, "/voice3/source_position", "", nullptr); + std::vector expected { + "/voice0/remaining_delay,i : { 0 }", + "/voice1/remaining_delay,i : { 48000 }", + "/voice2/remaining_delay,i : { 0 }", + "/voice3/remaining_delay,i : { 48000 }", + "/voice0/source_position,i : { 0 }", + "/voice1/source_position,i : { 100 }", + "/voice2/source_position,i : { 0 }", + "/voice3/source_position,i : { 100 }", + }; + REQUIRE(messageList == expected); + } } From 32682380c5114353d2a5ada0d0ac7bad2a44da53 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 25 Apr 2021 00:28:58 +0200 Subject: [PATCH 008/193] Convert discrete CC conditions without gaps Something like `hicc1=13` is actually translated to float as `13.999999999/127` instead of `13/127`. --- src/sfizz/Defaults.cpp | 6 +++++- src/sfizz/Defaults.h | 13 +++++++++++-- src/sfizz/Region.cpp | 8 ++++---- tests/FilesT.cpp | 3 ++- tests/RegionValuesT.cpp | 10 +++++----- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index 95d131458..dece22d50 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -49,7 +49,11 @@ UInt8Spec key { 60, {0, 127}, kCanBeNote }; UInt8Spec loKey { 0, {0, 127}, kCanBeNote }; UInt8Spec hiKey { 127, {0, 127}, kCanBeNote }; FloatSpec loCC { 0, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; -FloatSpec hiCC { 127, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; +FloatSpec hiCC { 127, {0.0f, 127.0f}, kNormalizeMidi|kFillGap|kPermissiveBounds }; +FloatSpec xfoutLoCC { 127.0f, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; +FloatSpec xfoutHiCC { 127.0f, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; +FloatSpec xfinLoCC { 0.0f, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; +FloatSpec xfinHiCC { 0.0f, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; FloatSpec loVel { 0, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; FloatSpec hiVel { 127, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; FloatSpec loChannelAftertouch { 0, {0, 127}, kNormalizeMidi|kPermissiveBounds }; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index bee569770..7b7183ce7 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -60,6 +60,7 @@ enum OpcodeFlags : int { kNormalizeBend = 1 << 7, kWrapPhase = 1 << 8, kDb2Mag = 1 << 9, + kFillGap = 1 << 10, // Fill in the gap when converting from discrete midi values to float, so that 13 is actually 13.999999... }; template @@ -97,8 +98,12 @@ struct OpcodeSpec return input; else if (flags & kNormalizePercent) return static_cast(input / U(100)); - else if (flags & kNormalizeMidi) - return static_cast(input / U(127)); + else if (flags & kNormalizeMidi) { + if ((flags & kFillGap) && (input <= U(126)) && input >= 0) + return std::nextafter(static_cast((input + 1.0f) / U(127)), 0.0f); + else + return static_cast(input / U(127)); + } else if (flags & kNormalizeBend) return static_cast(input / U(8191)); else if (flags & kDb2Mag) @@ -159,6 +164,10 @@ namespace Default extern const OpcodeSpec hiVel; extern const OpcodeSpec loCC; extern const OpcodeSpec hiCC; + extern const OpcodeSpec xfoutLoCC; + extern const OpcodeSpec xfoutHiCC; + extern const OpcodeSpec xfinHiCC; + extern const OpcodeSpec xfinLoCC; extern const OpcodeSpec loBend; extern const OpcodeSpec hiBend; extern const OpcodeSpec loNormalized; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 05e73b678..481cf0c8a 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -495,28 +495,28 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode, bool cleanOpcode) if (opcode.parameters.back() >= config::numCCs) return false; crossfadeCCInRange[opcode.parameters.back()].setStart( - opcode.read(Default::loCC) + opcode.read(Default::xfinLoCC) ); break; case hash("xfin_hicc&"): if (opcode.parameters.back() >= config::numCCs) return false; crossfadeCCInRange[opcode.parameters.back()].setEnd( - opcode.read(Default::loCC) // loCC for the proper default + opcode.read(Default::xfinHiCC) ); break; case hash("xfout_locc&"): if (opcode.parameters.back() >= config::numCCs) return false; crossfadeCCOutRange[opcode.parameters.back()].setStart( - opcode.read(Default::hiCC) // hiCC for the proper default + opcode.read(Default::xfoutLoCC) ); break; case hash("xfout_hicc&"): if (opcode.parameters.back() >= config::numCCs) return false; crossfadeCCOutRange[opcode.parameters.back()].setEnd( - opcode.read(Default::hiCC) + opcode.read(Default::xfoutHiCC) ); break; case hash("xf_cccurve"): diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index c1d568b21..8ea1c0e53 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -245,7 +245,8 @@ TEST_CASE("[Files] Pizz basic") REQUIRE(synth.getRegionView(i)->keyRange == Range(12, 22)); almostEqualRanges(synth.getRegionView(i)->velocityRange, { 97_norm, 127_norm }); REQUIRE(synth.getRegionView(i)->pitchKeycenter == 21); - almostEqualRanges(synth.getRegionView(i)->ccConditions.getWithDefault(107), { 0_norm, 13_norm }); + almostEqualRanges(synth.getRegionView(i)->ccConditions.getWithDefault(107), + { 0_norm, std::nextafter(14_norm, 0.0f) }); // Fill in the gap from 13_norm to "almost 14_norm" } almostEqualRanges(synth.getRegionView(0)->randRange, { 0, 0.25 }); almostEqualRanges(synth.getRegionView(1)->randRange, { 0.25, 0.5 }); diff --git a/tests/RegionValuesT.cpp b/tests/RegionValuesT.cpp index 9628d3a68..d9402d9d4 100644 --- a/tests/RegionValuesT.cpp +++ b/tests/RegionValuesT.cpp @@ -653,9 +653,9 @@ TEST_CASE("[Values] CC condition range") synth.dispatchMessage(client, 0, "/region3/cc_range1", "", nullptr); std::vector expected { "/region0/cc_range1,ff : { 0, 1 }", - "/region1/cc_range1,ff : { 0, 0.425197 }", - "/region2/cc_range1,ff : { 0, 0.425197 }", - "/region2/cc_range2,ff : { 0.015748, 0.0787402 }", + "/region1/cc_range1,ff : { 0, 0.433071 }", + "/region2/cc_range1,ff : { 0, 0.433071 }", + "/region2/cc_range2,ff : { 0.015748, 0.0866142 }", "/region3/cc_range1,ff : { 0.0787402, -0.00787402 }", }; REQUIRE(messageList == expected); @@ -1123,8 +1123,8 @@ TEST_CASE("[Values] Start on cc range") "/region0/start_cc_range1,N : { }", "/region0/start_cc_range2,N : { }", "/region1/start_cc_range1,ff : { 0.11811, 1 }", - "/region2/start_cc_range1,ff : { 0, 0.661417 }", - "/region3/start_cc_range1,ff : { 0.11811, 0.661417 }", + "/region2/start_cc_range1,ff : { 0, 0.669291 }", + "/region3/start_cc_range1,ff : { 0.11811, 0.669291 }", "/region4/start_cc_range2,ff : { 0.1, 1 }", "/region5/start_cc_range2,ff : { 0, 0.4 }", "/region6/start_cc_range2,ff : { 0.1, 0.4 }", From 8551caebf65b9720ec3e3cfea633d799b144c986 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 25 Apr 2021 03:52:14 +0200 Subject: [PATCH 009/193] Build time optimization: resources --- common.mk | 1 + src/CMakeLists.txt | 1 + src/sfizz/EQPool.cpp | 19 +- src/sfizz/EQPool.h | 12 +- src/sfizz/FilterPool.cpp | 14 +- src/sfizz/FilterPool.h | 11 +- src/sfizz/LFO.cpp | 16 +- src/sfizz/LFO.h | 2 +- src/sfizz/Resources.cpp | 137 +++++++++++++ src/sfizz/Resources.h | 98 +++++---- src/sfizz/Synth.cpp | 201 +++++++++++-------- src/sfizz/SynthMessaging.cpp | 11 +- src/sfizz/Voice.cpp | 162 +++++++++------ src/sfizz/Voice.h | 1 + src/sfizz/modulations/sources/Controller.cpp | 10 +- src/sfizz/modulations/sources/Controller.h | 2 +- tests/FilesT.cpp | 6 +- tests/FlexEGT.cpp | 7 +- tests/ModulationsT.cpp | 47 ++--- tests/SynthT.cpp | 4 +- 20 files changed, 494 insertions(+), 268 deletions(-) create mode 100644 src/sfizz/Resources.cpp diff --git a/common.mk b/common.mk index 722287259..37e82c1ef 100644 --- a/common.mk +++ b/common.mk @@ -107,6 +107,7 @@ SFIZZ_SOURCES = \ src/sfizz/PowerFollower.cpp \ src/sfizz/Region.cpp \ src/sfizz/RegionSet.cpp \ + src/sfizz/Resources.cpp \ src/sfizz/RTSemaphore.cpp \ src/sfizz/ScopedFTZ.cpp \ src/sfizz/sfizz.cpp \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7294623e8..96eed54c7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -161,6 +161,7 @@ set(SFIZZ_SOURCES sfizz/WindowedSinc.cpp sfizz/Interpolators.cpp sfizz/Layer.cpp + sfizz/Resources.cpp sfizz/modulations/ModId.cpp sfizz/modulations/ModKey.cpp sfizz/modulations/ModKeyHash.cpp diff --git a/src/sfizz/EQPool.cpp b/src/sfizz/EQPool.cpp index 88b00e622..28675eb2a 100644 --- a/src/sfizz/EQPool.cpp +++ b/src/sfizz/EQPool.cpp @@ -1,4 +1,7 @@ #include "EQPool.h" +#include "Region.h" +#include "Resources.h" +#include "BufferPool.h" #include "SIMDHelpers.h" #include "utility/SwapAndPop.h" #include @@ -31,9 +34,10 @@ void sfz::EQHolder::setup(const Region& region, unsigned eqId, float velocity) baseBandwidth = description->bandwidth; baseGain = description->gain + velocity * description->vel2gain; - gainTarget = resources.modMatrix.findTarget(ModKey::createNXYZ(ModId::EqGain, region.id, eqId)); - bandwidthTarget = resources.modMatrix.findTarget(ModKey::createNXYZ(ModId::EqBandwidth, region.id, eqId)); - frequencyTarget = resources.modMatrix.findTarget(ModKey::createNXYZ(ModId::EqFrequency, region.id, eqId)); + const ModMatrix& mm = resources.getModMatrix(); + gainTarget = mm.findTarget(ModKey::createNXYZ(ModId::EqGain, region.id, eqId)); + bandwidthTarget = mm.findTarget(ModKey::createNXYZ(ModId::EqBandwidth, region.id, eqId)); + frequencyTarget = mm.findTarget(ModKey::createNXYZ(ModId::EqFrequency, region.id, eqId)); // Disables smoothing of the parameters on the first call prepared = false; @@ -47,10 +51,11 @@ void sfz::EQHolder::process(const float** inputs, float** outputs, unsigned numF return; } - ModMatrix& mm = resources.modMatrix; - auto frequencySpan = resources.bufferPool.getBuffer(numFrames); - auto bandwidthSpan = resources.bufferPool.getBuffer(numFrames); - auto gainSpan = resources.bufferPool.getBuffer(numFrames); + ModMatrix& mm = resources.getModMatrix(); + BufferPool& bufferPool = resources.getBufferPool(); + auto frequencySpan = bufferPool.getBuffer(numFrames); + auto bandwidthSpan = bufferPool.getBuffer(numFrames); + auto gainSpan = bufferPool.getBuffer(numFrames); if (!frequencySpan || !bandwidthSpan || !gainSpan) return; diff --git a/src/sfizz/EQPool.h b/src/sfizz/EQPool.h index 54567b2fa..c7e44b81e 100644 --- a/src/sfizz/EQPool.h +++ b/src/sfizz/EQPool.h @@ -1,12 +1,14 @@ #pragma once #include "SfzFilter.h" -#include "Region.h" -#include "Resources.h" +#include "Defaults.h" +#include "modulations/ModMatrix.h" #include #include -namespace sfz -{ +namespace sfz { +struct Region; +class Resources; +struct EQDescription; class EQHolder { @@ -52,4 +54,4 @@ class EQHolder ModMatrix::TargetId bandwidthTarget; }; -} +} // namespace sfz diff --git a/src/sfizz/FilterPool.cpp b/src/sfizz/FilterPool.cpp index 1fd77988c..88b1802f5 100644 --- a/src/sfizz/FilterPool.cpp +++ b/src/sfizz/FilterPool.cpp @@ -1,4 +1,7 @@ #include "FilterPool.h" +#include "Region.h" +#include "Resources.h" +#include "BufferPool.h" #include "SIMDHelpers.h" #include "utility/SwapAndPop.h" #include @@ -42,7 +45,7 @@ void sfz::FilterHolder::setup(const Region& region, unsigned filterId, int noteN baseGain = description->gain; baseResonance = description->resonance; - ModMatrix& mm = resources.modMatrix; + ModMatrix& mm = resources.getModMatrix(); gainTarget = mm.findTarget(ModKey::createNXYZ(ModId::FilGain, region.id, filterId)); cutoffTarget = mm.findTarget(ModKey::createNXYZ(ModId::FilCutoff, region.id, filterId)); resonanceTarget = mm.findTarget(ModKey::createNXYZ(ModId::FilResonance, region.id, filterId)); @@ -62,10 +65,11 @@ void sfz::FilterHolder::process(const float** inputs, float** outputs, unsigned return; } - ModMatrix& mm = resources.modMatrix; - auto cutoffSpan = resources.bufferPool.getBuffer(numFrames); - auto resonanceSpan = resources.bufferPool.getBuffer(numFrames); - auto gainSpan = resources.bufferPool.getBuffer(numFrames); + ModMatrix& mm = resources.getModMatrix(); + BufferPool& bufferPool = resources.getBufferPool(); + auto cutoffSpan = bufferPool.getBuffer(numFrames); + auto resonanceSpan = bufferPool.getBuffer(numFrames); + auto gainSpan = bufferPool.getBuffer(numFrames); if (!cutoffSpan || !resonanceSpan || !gainSpan) return; diff --git a/src/sfizz/FilterPool.h b/src/sfizz/FilterPool.h index ab7611240..1ab12b003 100644 --- a/src/sfizz/FilterPool.h +++ b/src/sfizz/FilterPool.h @@ -1,13 +1,14 @@ #pragma once #include "SfzFilter.h" -#include "Region.h" -#include "Resources.h" #include "Defaults.h" +#include "modulations/ModMatrix.h" #include #include -namespace sfz -{ +namespace sfz { +struct Region; +class Resources; +struct FilterDescription; class FilterHolder { @@ -54,4 +55,4 @@ class FilterHolder bool prepared { false }; }; -} +} // namespace sfz diff --git a/src/sfizz/LFO.cpp b/src/sfizz/LFO.cpp index 5ec9aab70..0564f2b77 100644 --- a/src/sfizz/LFO.cpp +++ b/src/sfizz/LFO.cpp @@ -10,6 +10,10 @@ #include "SIMDHelpers.h" #include "Config.h" #include "Resources.h" +#include "BufferPool.h" +#include "BeatClock.h" +#include "MidiState.h" +#include "modulations/ModMatrix.h" #include "modulations/ModKey.h" #include "modulations/ModId.h" #include @@ -61,7 +65,7 @@ void LFO::setSampleRate(double sampleRate) void LFO::configure(const LFODescription* desc) { Impl& impl = *impl_; - ModMatrix& modMatrix = impl.resources_.modMatrix; + ModMatrix& modMatrix = impl.resources_.getModMatrix(); impl.desc_ = desc ? desc : &LFODescription::getDefault(); impl.beatsKeyId = modMatrix.findTarget(desc->beatsKey); impl.freqKeyId = modMatrix.findTarget(desc->freqKey); @@ -73,7 +77,7 @@ void LFO::start(unsigned triggerDelay) Impl& impl = *impl_; const LFODescription& desc = *impl.desc_; const float sampleRate = impl.sampleRate_; - const MidiState& state = impl.resources_.midiState; + const MidiState& state = impl.resources_.getMidiState(); impl.subPhases_.fill(0.0f); impl.sampleHoldMem_.fill(0.0f); @@ -230,7 +234,7 @@ void LFO::process(absl::Span out) { Impl& impl = *impl_; const LFODescription& desc = *impl.desc_; - BufferPool& pool = impl.resources_.bufferPool; + BufferPool& pool = impl.resources_.getBufferPool(); size_t numFrames = out.size(); fill(out, 0.0f); @@ -323,9 +327,9 @@ void LFO::processFadeIn(absl::Span out) void LFO::generatePhase(unsigned nth, absl::Span phases) { Impl& impl = *impl_; - BufferPool& bufferPool = impl.resources_.bufferPool; - BeatClock& beatClock = impl.resources_.beatClock; - ModMatrix& modMatrix = impl.resources_.modMatrix; + BufferPool& bufferPool = impl.resources_.getBufferPool(); + BeatClock& beatClock = impl.resources_.getBeatClock(); + ModMatrix& modMatrix = impl.resources_.getModMatrix(); const LFODescription& desc = *impl.desc_; const LFODescription::Sub& sub = desc.sub[nth]; const float samplePeriod = 1.0f / impl.sampleRate_; diff --git a/src/sfizz/LFO.h b/src/sfizz/LFO.h index 9ffa25d54..226fd6791 100644 --- a/src/sfizz/LFO.h +++ b/src/sfizz/LFO.h @@ -10,7 +10,7 @@ #include namespace sfz { -struct Resources; +class Resources; struct Region; enum class LFOWave : int; diff --git a/src/sfizz/Resources.cpp b/src/sfizz/Resources.cpp new file mode 100644 index 000000000..8770e60f1 --- /dev/null +++ b/src/sfizz/Resources.cpp @@ -0,0 +1,137 @@ +// 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 "Resources.h" +#include "SynthConfig.h" +#include "MidiState.h" +#include "FilePool.h" +#include "BufferPool.h" +#include "Logger.h" +#include "Wavetables.h" +#include "Curve.h" +#include "Tuning.h" +#include "BeatClock.h" +#include "Metronome.h" +#include "modulations/ModMatrix.h" + +namespace sfz { + +struct Resources::Impl { + SynthConfig synthConfig; + BufferPool bufferPool; + MidiState midiState; + Logger logger; + CurveSet curves; + FilePool filePool { logger }; + WavetablePool wavePool; + Tuning tuning; + absl::optional stretch; + ModMatrix modMatrix; + BeatClock beatClock; + Metronome metronome; +}; + +Resources::Resources() + : impl_(new Impl) +{ +} + +Resources::~Resources() +{ +} + +void Resources::setSampleRate(float samplerate) +{ + Impl& impl = *impl_; + impl.midiState.setSampleRate(samplerate); + impl.modMatrix.setSampleRate(samplerate); + impl.beatClock.setSampleRate(samplerate); + impl.metronome.init(samplerate); +} + +void Resources::setSamplesPerBlock(int samplesPerBlock) +{ + Impl& impl = *impl_; + impl.bufferPool.setBufferSize(samplesPerBlock); + impl.midiState.setSamplesPerBlock(samplesPerBlock); + impl.modMatrix.setSamplesPerBlock(samplesPerBlock); + impl.beatClock.setSamplesPerBlock(samplesPerBlock); +} + +void Resources::clear() +{ + Impl& impl = *impl_; + impl.curves = CurveSet::createPredefined(); + impl.filePool.clear(); + impl.wavePool.clearFileWaves(); + impl.logger.clear(); + impl.midiState.reset(); + impl.modMatrix.clear(); + impl.beatClock.clear(); + impl.metronome.clear(); +} + +const SynthConfig& Resources::getSynthConfig() const noexcept +{ + return impl_->synthConfig; +} + +const BufferPool& Resources::getBufferPool() const noexcept +{ + return impl_->bufferPool; +} + +const MidiState& Resources::getMidiState() const noexcept +{ + return impl_->midiState; +} + +const Logger& Resources::getLogger() const noexcept +{ + return impl_->logger; +} + +const CurveSet& Resources::getCurves() const noexcept +{ + return impl_->curves; +} + +const FilePool& Resources::getFilePool() const noexcept +{ + return impl_->filePool; +} + +const WavetablePool& Resources::getWavePool() const noexcept +{ + return impl_->wavePool; +} + +const Tuning& Resources::getTuning() const noexcept +{ + return impl_->tuning; +} + +const absl::optional& Resources::getStretch() const noexcept +{ + return impl_->stretch; +} + +const ModMatrix& Resources::getModMatrix() const noexcept +{ + return impl_->modMatrix; +} + +const BeatClock& Resources::getBeatClock() const noexcept +{ + return impl_->beatClock; +} + +const Metronome& Resources::getMetronome() const noexcept +{ + return impl_->metronome; +} + +} // namespace sfz diff --git a/src/sfizz/Resources.h b/src/sfizz/Resources.h index 27de6f1c2..b1928a8c4 100644 --- a/src/sfizz/Resources.h +++ b/src/sfizz/Resources.h @@ -5,64 +5,56 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #pragma once -#include "SynthConfig.h" -#include "MidiState.h" -#include "FilePool.h" -#include "BufferPool.h" -#include "Logger.h" -#include "Wavetables.h" -#include "Curve.h" -#include "Tuning.h" -#include "BeatClock.h" -#include "Metronome.h" -#include "modulations/ModMatrix.h" #include "absl/types/optional.h" +#include -namespace sfz -{ -class WavetableMulti; +namespace sfz { + +struct SynthConfig; +class BufferPool; +class MidiState; +class Logger; +class CurveSet; +class FilePool; +struct WavetablePool; +class Tuning; +class StretchTuning; +class ModMatrix; +class BeatClock; +class Metronome; -struct Resources +class Resources { - SynthConfig synthConfig; - BufferPool bufferPool; - MidiState midiState; - Logger logger; - CurveSet curves; - FilePool filePool { logger }; - WavetablePool wavePool; - Tuning tuning; - absl::optional stretch; - ModMatrix modMatrix; - BeatClock beatClock; - Metronome metronome; +public: + Resources(); + ~Resources(); + + void setSampleRate(float samplerate); + void setSamplesPerBlock(int samplesPerBlock); + void clear(); - void setSampleRate(float samplerate) - { - midiState.setSampleRate(samplerate); - modMatrix.setSampleRate(samplerate); - beatClock.setSampleRate(samplerate); - metronome.init(samplerate); - } + #define ACCESSOR_RW(Accessor, RetTy) \ + RetTy const& Accessor() const noexcept; \ + RetTy& Accessor() noexcept { return const_cast(const_cast(this)->Accessor()); } - void setSamplesPerBlock(int samplesPerBlock) - { - bufferPool.setBufferSize(samplesPerBlock); - midiState.setSamplesPerBlock(samplesPerBlock); - modMatrix.setSamplesPerBlock(samplesPerBlock); - beatClock.setSamplesPerBlock(samplesPerBlock); - } + ACCESSOR_RW(getSynthConfig, SynthConfig); + ACCESSOR_RW(getBufferPool, BufferPool); + ACCESSOR_RW(getMidiState, MidiState); + ACCESSOR_RW(getLogger, Logger); + ACCESSOR_RW(getCurves, CurveSet); + ACCESSOR_RW(getFilePool, FilePool); + ACCESSOR_RW(getWavePool, WavetablePool); + ACCESSOR_RW(getTuning, Tuning); + ACCESSOR_RW(getStretch, absl::optional); + ACCESSOR_RW(getModMatrix, ModMatrix); + ACCESSOR_RW(getBeatClock, BeatClock); + ACCESSOR_RW(getMetronome, Metronome); - void clear() - { - curves = CurveSet::createPredefined(); - filePool.clear(); - wavePool.clearFileWaves(); - logger.clear(); - midiState.reset(); - modMatrix.clear(); - beatClock.clear(); - metronome.clear(); - } + #undef ACCESSOR_RW + +private: + struct Impl; + std::unique_ptr impl_; }; -} + +} // namespace sfz diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index feb5621b3..c992d2394 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -16,6 +16,13 @@ #include "Region.h" #include "RegionSet.h" #include "Resources.h" +#include "BufferPool.h" +#include "FilePool.h" +#include "Wavetables.h" +#include "Tuning.h" +#include "BeatClock.h" +#include "Metronome.h" +#include "SynthConfig.h" #include "ScopedFTZ.h" #include "utility/StringViewHelpers.h" #include "utility/XmlHelpers.h" @@ -59,18 +66,19 @@ Synth::Impl::Impl() resetVoices(config::numVoices); // modulation sources + MidiState& midiState = resources_.getMidiState(); genController_.reset(new ControllerSource(resources_, voiceManager_)); genLFO_.reset(new LFOSource(voiceManager_)); genFlexEnvelope_.reset(new FlexEnvelopeSource(voiceManager_)); - genADSREnvelope_.reset(new ADSREnvelopeSource(voiceManager_, resources_.midiState)); - genChannelAftertouch_.reset(new ChannelAftertouchSource(voiceManager_, resources_.midiState)); - genPolyAftertouch_.reset(new PolyAftertouchSource(voiceManager_, resources_.midiState)); + genADSREnvelope_.reset(new ADSREnvelopeSource(voiceManager_, midiState)); + genChannelAftertouch_.reset(new ChannelAftertouchSource(voiceManager_, midiState)); + genPolyAftertouch_.reset(new PolyAftertouchSource(voiceManager_, midiState)); } Synth::Impl::~Impl() { voiceManager_.reset(); - resources_.filePool.emptyFileLoadingQueues(); + resources_.getFilePool().emptyFileLoadingQueues(); } void Synth::Impl::onParseFullBlock(const std::string& header, const std::vector& members) @@ -113,7 +121,7 @@ void Synth::Impl::onParseFullBlock(const std::string& header, const std::vector< buildRegion(members); break; case hash("curve"): - resources_.curves.addCurveFromHeader(members); + resources_.getCurves().addCurveFromHeader(members); break; case hash("effect"): handleEffectOpcodes(members); @@ -138,7 +146,8 @@ void Synth::Impl::onParseWarning(const SourceRange& range, const std::string& me void Synth::Impl::buildRegion(const std::vector& regionOpcodes) { int regionNumber = static_cast(layers_.size()); - Layer* lastLayer = new Layer(regionNumber, defaultPath_, resources_.midiState); + MidiState& midiState = resources_.getMidiState(); + Layer* lastLayer = new Layer(regionNumber, defaultPath_, midiState); layers_.emplace_back(lastLayer); Region* lastRegion = &lastLayer->getRegion(); @@ -221,8 +230,11 @@ void Synth::Impl::buildRegion(const std::vector& regionOpcodes) void Synth::Impl::clear() { + FilePool& filePool = resources_.getFilePool(); + MidiState& midiState = resources_.getMidiState(); + // Clear the background queues before removing everyone - resources_.filePool.waitForBackgroundLoading(); + filePool.waitForBackgroundLoading(); voiceManager_.reset(); for (auto& list : lastKeyswitchLists_) @@ -253,9 +265,9 @@ void Synth::Impl::clear() currentSwitch_ = absl::nullopt; defaultPath_ = ""; image_ = ""; - resources_.midiState.reset(); - resources_.filePool.clear(); - resources_.filePool.setRamLoading(config::loadInRam); + midiState.reset(); + filePool.clear(); + filePool.setRamLoading(config::loadInRam); clearCCLabels(); currentUsedCCs_.clear(); changedCCsThisCycle_.clear(); @@ -399,13 +411,16 @@ void Synth::Impl::handleControlOpcodes(const std::vector& members) octaveOffset_ = member.read(Default::octaveOffset); break; case hash("hint_ram_based"): + { + FilePool& filePool = resources_.getFilePool(); if (member.value == "1") - resources_.filePool.setRamLoading(true); + filePool.setRamLoading(true); else if (member.value == "0") - resources_.filePool.setRamLoading(false); + filePool.setRamLoading(false); else DBG("Unsupported value for hint_ram_based: " << member.value); break; + } case hash("hint_stealing"): switch(hash(member.value)) { case hash("first"): @@ -550,7 +565,8 @@ bool Synth::loadSfzString(const fs::path& path, absl::string_view text) void Synth::Impl::finalizeSfzLoad() { const fs::path& rootDirectory = parser_.originalDirectory(); - resources_.filePool.setRootDirectory(rootDirectory); + FilePool& filePool = resources_.getFilePool(); + filePool.setRootDirectory(rootDirectory); // a string representation used for OSC purposes rootPath_ = rootDirectory.u8string(); @@ -583,13 +599,16 @@ void Synth::Impl::finalizeSfzLoad() absl::optional fileInformation; + FilePool& filePool = resources_.getFilePool(); + WavetablePool& wavePool = resources_.getWavePool(); + if (!region.isGenerator()) { - if (!resources_.filePool.checkSampleId(*region.sampleId)) { + if (!filePool.checkSampleId(*region.sampleId)) { removeCurrentRegion(); continue; } - fileInformation = resources_.filePool.getFileInformation(*region.sampleId); + fileInformation = filePool.getFileInformation(*region.sampleId); if (!fileInformation) { removeCurrentRegion(); continue; @@ -598,7 +617,7 @@ void Synth::Impl::finalizeSfzLoad() region.hasWavetableSample = fileInformation->wavetable.has_value(); if (fileInformation->end < config::wavetableMaxFrames) { - auto sample = resources_.filePool.loadFile(*region.sampleId); + auto sample = filePool.loadFile(*region.sampleId); bool allZeros = true; int numChannels = sample->information.numChannels; for (int i = 0; i < numChannels; ++i) { @@ -653,13 +672,13 @@ void Synth::Impl::finalizeSfzLoad() return Default::offsetMod.bounds.clamp(sumOffsetCC); }(); - if (!resources_.filePool.preloadFile(*region.sampleId, maxOffset)) { + if (!filePool.preloadFile(*region.sampleId, maxOffset)) { removeCurrentRegion(); continue; } } else if (!region.isGenerator()) { - if (!resources_.wavePool.createFileWave(resources_.filePool, std::string(region.sampleId->filename()))) { + if (!wavePool.createFileWave(filePool, std::string(region.sampleId->filename()))) { removeCurrentRegion(); continue; } @@ -698,8 +717,9 @@ void Synth::Impl::finalizeSfzLoad() } // Defaults + MidiState& midiState = resources_.getMidiState(); for (int cc = 0; cc < config::numCCs; cc++) { - layer.registerCC(cc, resources_.midiState.getCCValue(cc)); + layer.registerCC(cc, midiState.getCCValue(cc)); } @@ -819,37 +839,37 @@ void Synth::Impl::finalizeSfzLoad() bool Synth::loadScalaFile(const fs::path& path) { Impl& impl = *impl_; - return impl.resources_.tuning.loadScalaFile(path); + return impl.resources_.getTuning().loadScalaFile(path); } bool Synth::loadScalaString(const std::string& text) { Impl& impl = *impl_; - return impl.resources_.tuning.loadScalaString(text); + return impl.resources_.getTuning().loadScalaString(text); } void Synth::setScalaRootKey(int rootKey) { Impl& impl = *impl_; - impl.resources_.tuning.setScalaRootKey(rootKey); + impl.resources_.getTuning().setScalaRootKey(rootKey); } int Synth::getScalaRootKey() const { Impl& impl = *impl_; - return impl.resources_.tuning.getScalaRootKey(); + return impl.resources_.getTuning().getScalaRootKey(); } void Synth::setTuningFrequency(float frequency) { Impl& impl = *impl_; - impl.resources_.tuning.setTuningFrequency(frequency); + impl.resources_.getTuning().setTuningFrequency(frequency); } float Synth::getTuningFrequency() const { Impl& impl = *impl_; - return impl.resources_.tuning.getTuningFrequency(); + return impl.resources_.getTuning().getTuningFrequency(); } void Synth::loadStretchTuningByRatio(float ratio) @@ -858,10 +878,11 @@ void Synth::loadStretchTuningByRatio(float ratio) SFIZZ_CHECK(ratio >= 0.0f && ratio <= 1.0f); ratio = clamp(ratio, 0.0f, 1.0f); + absl::optional& stretch = impl.resources_.getStretch(); if (ratio > 0.0f) - impl.resources_.stretch = StretchTuning::createRailsbackFromRatio(ratio); + stretch = StretchTuning::createRailsbackFromRatio(ratio); else - impl.resources_.stretch.reset(); + stretch.reset(); } int Synth::getNumActiveVoices() const noexcept @@ -931,8 +952,12 @@ void Synth::renderBlock(AudioSpan buffer) noexcept return; } - if (impl.resources_.synthConfig.freeWheeling) - impl.resources_.filePool.waitForBackgroundLoading(); + const SynthConfig& synthConfig = impl.resources_.getSynthConfig(); + FilePool& filePool = impl.resources_.getFilePool(); + BufferPool& bufferPool = impl.resources_.getBufferPool(); + + if (synthConfig.freeWheeling) + filePool.waitForBackgroundLoading(); const auto now = std::chrono::high_resolution_clock::now(); const auto timeSinceLastCollection = @@ -940,25 +965,27 @@ void Synth::renderBlock(AudioSpan buffer) noexcept if (timeSinceLastCollection.count() > config::fileClearingPeriod) { impl.lastGarbageCollection_ = now; - impl.resources_.filePool.triggerGarbageCollection(); + filePool.triggerGarbageCollection(); } - auto tempSpan = impl.resources_.bufferPool.getStereoBuffer(numFrames); - auto tempMixSpan = impl.resources_.bufferPool.getStereoBuffer(numFrames); - auto rampSpan = impl.resources_.bufferPool.getBuffer(numFrames); + auto tempSpan = bufferPool.getStereoBuffer(numFrames); + auto tempMixSpan = bufferPool.getStereoBuffer(numFrames); + auto rampSpan = bufferPool.getBuffer(numFrames); if (!tempSpan || !tempMixSpan || !rampSpan) { DBG("[sfizz] Could not get a temporary buffer; exiting callback... "); return; } - ModMatrix& mm = impl.resources_.modMatrix; + ModMatrix& mm = impl.resources_.getModMatrix(); mm.beginCycle(numFrames); - BeatClock& bc = impl.resources_.beatClock; + BeatClock& bc = impl.resources_.getBeatClock(); bc.beginCycle(numFrames); - if (impl.playheadMoved_ && impl.resources_.beatClock.isPlaying()) { - impl.resources_.midiState.flushEvents(); + MidiState& midiState = impl.resources_.getMidiState(); + + if (impl.playheadMoved_ && bc.isPlaying()) { + midiState.flushEvents(); impl.genController_->resetSmoothers(); impl.playheadMoved_ = false; } @@ -1028,7 +1055,8 @@ void Synth::renderBlock(AudioSpan buffer) noexcept // Process the metronome (debugging tool for host time info) constexpr bool metronomeEnabled = false; if (metronomeEnabled) { - impl.resources_.metronome.processAdding( + Metronome& metro = impl.resources_.getMetronome(); + metro.processAdding( bc.getRunningBeatNumber().data(), bc.getRunningBeatsPerBar().data(), buffer.getChannel(0), buffer.getChannel(1), numFrames); } @@ -1045,11 +1073,12 @@ void Synth::renderBlock(AudioSpan buffer) noexcept { // Clear events and advance midi time ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; - impl.resources_.midiState.advanceTime(buffer.getNumFrames()); + midiState.advanceTime(buffer.getNumFrames()); } callbackBreakdown.dispatch = impl.dispatchDuration_; - impl.resources_.logger.logCallbackTime( + Logger& logger = impl.resources_.getLogger(); + logger.logCallbackTime( callbackBreakdown, impl.voiceManager_.getNumActiveVoices(), numFrames); // Reset the dispatch counter @@ -1073,7 +1102,7 @@ void Synth::hdNoteOn(int delay, int noteNumber, float normalizedVelocity) noexce ASSERT(noteNumber >= 0); Impl& impl = *impl_; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; - impl.resources_.midiState.noteOnEvent(delay, noteNumber, normalizedVelocity); + impl.resources_.getMidiState().noteOnEvent(delay, noteNumber, normalizedVelocity); impl.noteOnDispatch(delay, noteNumber, normalizedVelocity); } @@ -1093,8 +1122,9 @@ void Synth::hdNoteOff(int delay, int noteNumber, float normalizedVelocity) noexc // 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? // auto replacedVelocity = (velocity == 0 ? getNoteVelocity(noteNumber) : velocity); - impl.resources_.midiState.noteOffEvent(delay, noteNumber, normalizedVelocity); - const auto replacedVelocity = impl.resources_.midiState.getNoteVelocity(noteNumber); + MidiState& midiState = impl.resources_.getMidiState(); + midiState.noteOffEvent(delay, noteNumber, normalizedVelocity); + const auto replacedVelocity = midiState.getNoteVelocity(noteNumber); for (auto& voice : impl.voiceManager_) voice.registerNoteOff(delay, noteNumber, replacedVelocity); @@ -1275,6 +1305,8 @@ void Synth::Impl::performHdcc(int delay, int ccNumber, float normValue, bool asM changedCCsThisCycle_.set(ccNumber); + MidiState& midiState = resources_.getMidiState(); + if (asMidi) { if (ccNumber == config::resetCC) { resetAllControllers(delay); @@ -1284,7 +1316,7 @@ void Synth::Impl::performHdcc(int delay, int ccNumber, float normValue, bool asM if (ccNumber == config::allNotesOffCC || ccNumber == config::allSoundOffCC) { for (auto& voice : voiceManager_) voice.reset(); - resources_.midiState.allNotesOff(delay); + midiState.allNotesOff(delay); return; } } @@ -1293,7 +1325,7 @@ void Synth::Impl::performHdcc(int delay, int ccNumber, float normValue, bool asM voice.registerCC(delay, ccNumber, normValue); ccDispatch(delay, ccNumber, normValue); - resources_.midiState.ccEvent(delay, ccNumber, normValue); + midiState.ccEvent(delay, ccNumber, normValue); } void Synth::Impl::setDefaultHdcc(int ccNumber, float value) @@ -1301,7 +1333,7 @@ void Synth::Impl::setDefaultHdcc(int ccNumber, float value) ASSERT(ccNumber >= 0); ASSERT(ccNumber < config::numCCs); defaultCCValues_[ccNumber] = value; - resources_.midiState.ccEvent(0, ccNumber, value); + resources_.getMidiState().ccEvent(0, ccNumber, value); } float Synth::getHdcc(int ccNumber) @@ -1309,7 +1341,7 @@ float Synth::getHdcc(int ccNumber) ASSERT(ccNumber >= 0); ASSERT(ccNumber < config::numCCs); Impl& impl = *impl_; - return impl.resources_.midiState.getCCValue(ccNumber); + return impl.resources_.getMidiState().getCCValue(ccNumber); } float Synth::getDefaultHdcc(int ccNumber) @@ -1331,7 +1363,7 @@ void Synth::hdPitchWheel(int delay, float normalizedPitch) noexcept Impl& impl = *impl_; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; - impl.resources_.midiState.pitchBendEvent(delay, normalizedPitch); + impl.resources_.getMidiState().pitchBendEvent(delay, normalizedPitch); for (const Impl::LayerPtr& layer : impl.layers_) { layer->registerPitchWheel(normalizedPitch); @@ -1355,7 +1387,7 @@ void Synth::hdChannelAftertouch(int delay, float normAftertouch) noexcept Impl& impl = *impl_; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; - impl.resources_.midiState.channelAftertouchEvent(delay, normAftertouch); + impl.resources_.getMidiState().channelAftertouchEvent(delay, normAftertouch); for (const Impl::LayerPtr& layerPtr : impl.layers_) { layerPtr->registerAftertouch(normAftertouch); @@ -1379,7 +1411,7 @@ void Synth::hdPolyAftertouch(int delay, int noteNumber, float normAftertouch) no Impl& impl = *impl_; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; - impl.resources_.midiState.polyAftertouchEvent(delay, noteNumber, normAftertouch); + impl.resources_.getMidiState().polyAftertouchEvent(delay, noteNumber, normAftertouch); for (auto& voice : impl.voiceManager_) voice.registerPolyAftertouch(delay, noteNumber, normAftertouch); @@ -1393,7 +1425,7 @@ void Synth::tempo(int delay, float secondsPerBeat) noexcept Impl& impl = *impl_; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; - impl.resources_.beatClock.setTempo(delay, secondsPerBeat); + impl.resources_.getBeatClock().setTempo(delay, secondsPerBeat); } void Synth::bpmTempo(int delay, float beatsPerMinute) noexcept @@ -1407,7 +1439,7 @@ void Synth::timeSignature(int delay, int beatsPerBar, int beatUnit) Impl& impl = *impl_; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; - impl.resources_.beatClock.setTimeSignature(delay, TimeSignature(beatsPerBar, beatUnit)); + impl.resources_.getBeatClock().setTimeSignature(delay, TimeSignature(beatsPerBar, beatUnit)); } void Synth::timePosition(int delay, int bar, double barBeat) @@ -1415,16 +1447,18 @@ void Synth::timePosition(int delay, int bar, double barBeat) Impl& impl = *impl_; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; + BeatClock& beatClock = impl.resources_.getBeatClock(); + const auto newPosition = BBT(bar, barBeat); - const auto newBeatPosition = newPosition.toBeats(impl.resources_.beatClock.getTimeSignature()); - const auto currentBeatPosition = impl.resources_.beatClock.getLastBeatPosition(); + const auto newBeatPosition = newPosition.toBeats(beatClock.getTimeSignature()); + const auto currentBeatPosition = beatClock.getLastBeatPosition(); const auto positionDifference = std::abs(newBeatPosition - currentBeatPosition); - const auto threshold = config::playheadMovedFrames * impl.resources_.beatClock.getBeatsPerFrame(); + const auto threshold = config::playheadMovedFrames * beatClock.getBeatsPerFrame(); if (positionDifference > threshold) impl.playheadMoved_ = true; - impl.resources_.beatClock.setTimePosition(delay, newPosition); + beatClock.setTimePosition(delay, newPosition); } void Synth::playbackState(int delay, int playbackState) @@ -1432,7 +1466,7 @@ void Synth::playbackState(int delay, int playbackState) Impl& impl = *impl_; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; - impl.resources_.beatClock.setPlaying(delay, playbackState == 1); + impl.resources_.getBeatClock().setPlaying(delay, playbackState == 1); } int Synth::getNumRegions() const noexcept @@ -1456,7 +1490,7 @@ int Synth::getNumMasters() const noexcept int Synth::getNumCurves() const noexcept { Impl& impl = *impl_; - return static_cast(impl.resources_.curves.getNumCurves()); + return static_cast(impl.resources_.getCurves().getNumCurves()); } std::string Synth::exportMidnam(absl::string_view model) const @@ -1638,17 +1672,18 @@ const std::vector& Synth::getUnknownOpcodes() const noexcept size_t Synth::getNumPreloadedSamples() const noexcept { Impl& impl = *impl_; - return impl.resources_.filePool.getNumPreloadedSamples(); + return impl.resources_.getFilePool().getNumPreloadedSamples(); } int Synth::getSampleQuality(ProcessMode mode) { Impl& impl = *impl_; + SynthConfig& synthConfig = impl.resources_.getSynthConfig(); switch (mode) { case ProcessLive: - return impl.resources_.synthConfig.liveSampleQuality; + return synthConfig.liveSampleQuality; case ProcessFreewheeling: - return impl.resources_.synthConfig.freeWheelingSampleQuality; + return synthConfig.freeWheelingSampleQuality; default: SFIZZ_CHECK(false); return 0; @@ -1660,13 +1695,14 @@ void Synth::setSampleQuality(ProcessMode mode, int quality) SFIZZ_CHECK(quality >= 0 && quality <= 10); Impl& impl = *impl_; quality = clamp(quality, 0, 10); + SynthConfig& synthConfig = impl.resources_.getSynthConfig(); switch (mode) { case ProcessLive: - impl.resources_.synthConfig.liveSampleQuality = quality; + synthConfig.liveSampleQuality = quality; break; case ProcessFreewheeling: - impl.resources_.synthConfig.freeWheelingSampleQuality = quality; + synthConfig.freeWheelingSampleQuality = quality; break; default: SFIZZ_CHECK(false); @@ -1677,11 +1713,12 @@ void Synth::setSampleQuality(ProcessMode mode, int quality) int Synth::getOscillatorQuality(ProcessMode mode) { Impl& impl = *impl_; + SynthConfig& synthConfig = impl.resources_.getSynthConfig(); switch (mode) { case ProcessLive: - return impl.resources_.synthConfig.liveOscillatorQuality; + return synthConfig.liveOscillatorQuality; case ProcessFreewheeling: - return impl.resources_.synthConfig.freeWheelingOscillatorQuality; + return synthConfig.freeWheelingOscillatorQuality; default: SFIZZ_CHECK(false); return 0; @@ -1693,13 +1730,14 @@ void Synth::setOscillatorQuality(ProcessMode mode, int quality) SFIZZ_CHECK(quality >= 0 && quality <= 3); Impl& impl = *impl_; quality = clamp(quality, 0, 3); + SynthConfig& synthConfig = impl.resources_.getSynthConfig(); switch (mode) { case ProcessLive: - impl.resources_.synthConfig.liveOscillatorQuality = quality; + synthConfig.liveOscillatorQuality = quality; break; case ProcessFreewheeling: - impl.resources_.synthConfig.freeWheelingOscillatorQuality = quality; + synthConfig.freeWheelingOscillatorQuality = quality; break; default: SFIZZ_CHECK(false); @@ -1770,7 +1808,7 @@ void Synth::Impl::applySettingsPerVoice() void Synth::Impl::setupModMatrix() { - ModMatrix& mm = resources_.modMatrix; + ModMatrix& mm = resources_.getModMatrix(); for (const LayerPtr& layerPtr : layers_) { const Region& region = layerPtr->getRegion(); @@ -1851,40 +1889,43 @@ void Synth::Impl::setupModMatrix() void Synth::setPreloadSize(uint32_t preloadSize) noexcept { Impl& impl = *impl_; + FilePool& filePool = impl.resources_.getFilePool(); // fast path - if (preloadSize == impl.resources_.filePool.getPreloadSize()) + if (preloadSize == filePool.getPreloadSize()) return; - impl.resources_.filePool.setPreloadSize(preloadSize); + filePool.setPreloadSize(preloadSize); } uint32_t Synth::getPreloadSize() const noexcept { Impl& impl = *impl_; - return impl.resources_.filePool.getPreloadSize(); + return impl.resources_.getFilePool().getPreloadSize(); } void Synth::enableFreeWheeling() noexcept { Impl& impl = *impl_; - if (!impl.resources_.synthConfig.freeWheeling) { - impl.resources_.synthConfig.freeWheeling = true; + SynthConfig& synthConfig = impl.resources_.getSynthConfig(); + if (!synthConfig.freeWheeling) { + synthConfig.freeWheeling = true; DBG("Enabling freewheeling"); } } void Synth::disableFreeWheeling() noexcept { Impl& impl = *impl_; - if (impl.resources_.synthConfig.freeWheeling) { - impl.resources_.synthConfig.freeWheeling = false; + SynthConfig& synthConfig = impl.resources_.getSynthConfig(); + if (synthConfig.freeWheeling) { + synthConfig.freeWheeling = false; DBG("Disabling freewheeling"); } } void Synth::Impl::resetAllControllers(int delay) noexcept { - resources_.midiState.resetAllControllers(delay); + resources_.getMidiState().resetAllControllers(delay); for (auto& voice : voiceManager_) { voice.registerPitchWheel(delay, 0); @@ -1931,19 +1972,19 @@ bool Synth::shouldReloadFile() bool Synth::shouldReloadScala() { Impl& impl = *impl_; - return impl.resources_.tuning.shouldReloadScala(); + return impl.resources_.getTuning().shouldReloadScala(); } void Synth::enableLogging(absl::string_view prefix) noexcept { Impl& impl = *impl_; - impl.resources_.logger.enableLogging(prefix); + impl.resources_.getLogger().enableLogging(prefix); } void Synth::disableLogging() noexcept { Impl& impl = *impl_; - impl.resources_.logger.disableLogging(); + impl.resources_.getLogger().disableLogging(); } void Synth::allSoundOff() noexcept @@ -2038,7 +2079,7 @@ BitArray Synth::Impl::collectAllUsedCCs() BitArray used; for (const LayerPtr& layerPtr : layers_) collectUsedCCsFromRegion(used, layerPtr->getRegion()); - collectUsedCCsFromModulations(used, resources_.modMatrix); + collectUsedCCsFromModulations(used, resources_.getModMatrix()); return used; } diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index acd07c82c..b53cf1a09 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -5,6 +5,9 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "SynthPrivate.h" +#include "FilePool.h" +#include "Curve.h" +#include "MidiState.h" #include "utility/StringViewHelpers.h" #include #include @@ -67,11 +70,11 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co } break; MATCH("/num_curves", "") { - client.receive<'i'>(delay, path, int(impl.resources_.curves.getNumCurves())); + client.receive<'i'>(delay, path, int(impl.resources_.getCurves().getNumCurves())); } break; MATCH("/num_samples", "") { - client.receive<'i'>(delay, path, int(impl.resources_.filePool.getNumPreloadedSamples())); + client.receive<'i'>(delay, path, int(impl.resources_.getFilePool().getNumPreloadedSamples())); } break; //---------------------------------------------------------------------- @@ -139,13 +142,13 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co if (indices[0] >= config::numCCs) break; // Note: result value is not frame-exact - client.receive<'f'>(delay, path, impl.resources_.midiState.getCCValue(indices[0])); + client.receive<'f'>(delay, path, impl.resources_.getMidiState().getCCValue(indices[0])); } break; MATCH("/cc&/value", "f") { if (indices[0] >= config::numCCs) break; - impl.resources_.midiState.ccEvent(delay, indices[0], args[0].f); + impl.resources_.getMidiState().ccEvent(delay, indices[0], args[0].f); } break; MATCH("/cc&/label", "") { diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 10b699328..fea3f196b 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -26,6 +26,11 @@ #include "SfzHelpers.h" #include "SIMDHelpers.h" #include "Smoothers.h" +#include "FilePool.h" +#include "Wavetables.h" +#include "Tuning.h" +#include "BufferPool.h" +#include "SynthConfig.h" #include "utility/Macros.h" #include #include @@ -387,10 +392,11 @@ const ExtendedCCValues& Voice::getExtendedCCValues() const noexcept void Voice::Impl::updateExtendedCCValues() noexcept { - extendedCCValues_.unipolar = resources_.midiState.getCCValue(ExtendedCCs::unipolarRandom); - extendedCCValues_.bipolar = resources_.midiState.getCCValue(ExtendedCCs::bipolarRandom); - extendedCCValues_.alternate = resources_.midiState.getCCValue(ExtendedCCs::alternate); - extendedCCValues_.noteGate = resources_.midiState.getCCValue(ExtendedCCs::keyboardNoteGate); + MidiState& midiState = resources_.getMidiState(); + extendedCCValues_.unipolar = midiState.getCCValue(ExtendedCCs::unipolarRandom); + extendedCCValues_.bipolar = midiState.getCCValue(ExtendedCCs::bipolarRandom); + extendedCCValues_.alternate = midiState.getCCValue(ExtendedCCs::alternate); + extendedCCValues_.noteGate = midiState.getCCValue(ExtendedCCs::keyboardNoteGate); } bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexcept @@ -399,6 +405,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc ASSERT(event.value >= 0.0f && event.value <= 1.0f); Resources& resources = impl.resources_; + MidiState& midiState = resources.getMidiState(); const Region& region = layer->getRegion(); impl.region_ = ®ion; @@ -408,7 +415,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc impl.triggerEvent_.number = region.pitchKeycenter; if (region.velocityOverride == VelocityOverride::previous) - impl.triggerEvent_.value = resources.midiState.getVelocityOverride(); + impl.triggerEvent_.value = midiState.getVelocityOverride(); if (region.disabled()) { impl.switchState(State::cleanMeUp); @@ -424,26 +431,27 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc delay = 0; if (region.isOscillator()) { + WavetablePool& wavePool = resources.getWavePool(); const WavetableMulti* wave = nullptr; if (!region.isGenerator()) - wave = resources.wavePool.getFileWave(region.sampleId->filename()); + wave = wavePool.getFileWave(region.sampleId->filename()); else { switch (hash(region.sampleId->filename())) { default: case hash("*silence"): break; case hash("*sine"): - wave = resources.wavePool.getWaveSin(); + wave = wavePool.getWaveSin(); break; case hash("*triangle"): // fallthrough case hash("*tri"): - wave = resources.wavePool.getWaveTriangle(); + wave = wavePool.getWaveTriangle(); break; case hash("*square"): - wave = resources.wavePool.getWaveSquare(); + wave = wavePool.getWaveSquare(); break; case hash("*saw"): - wave = resources.wavePool.getWaveSaw(); + wave = wavePool.getWaveSaw(); break; } } @@ -457,27 +465,29 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc } impl.setupOscillatorUnison(); } else { - impl.currentPromise_ = resources.filePool.getFilePromise(region.sampleId); + FilePool& filePool = resources.getFilePool(); + impl.currentPromise_ = filePool.getFilePromise(region.sampleId); if (!impl.currentPromise_) { impl.switchState(State::cleanMeUp); return false; } impl.updateLoopInformation(); impl.speedRatio_ = static_cast(impl.currentPromise_->information.sampleRate / impl.sampleRate_); - impl.sourcePosition_ = region.getOffset(resources.midiState); + impl.sourcePosition_ = region.getOffset(midiState); } // do Scala retuning and reconvert the frequency into a 12TET key number - const float numberRetuned = resources.tuning.getKeyFractional12TET(impl.triggerEvent_.number); + Tuning& tuning = resources.getTuning(); + const float numberRetuned = tuning.getKeyFractional12TET(impl.triggerEvent_.number); impl.pitchRatio_ = region.getBasePitchVariation(numberRetuned, impl.triggerEvent_.value); // apply stretch tuning if set - if (resources.stretch) - impl.pitchRatio_ *= resources.stretch->getRatioForFractionalKey(numberRetuned); + if (absl::optional& stretch = resources.getStretch()) + impl.pitchRatio_ *= stretch->getRatioForFractionalKey(numberRetuned); impl.pitchKeycenter_ = region.pitchKeycenter; - impl.baseVolumedB_ = region.getBaseVolumedB(resources.midiState, impl.triggerEvent_.number); + impl.baseVolumedB_ = region.getBaseVolumedB(midiState, impl.triggerEvent_.number); impl.baseGain_ = region.getBaseGain(); if (impl.triggerEvent_.type != TriggerEventType::CC || region.velocityOverride == VelocityOverride::previous) impl.baseGain_ *= region.getNoteGain(impl.triggerEvent_.number, impl.triggerEvent_.value); @@ -494,26 +504,27 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc } impl.triggerDelay_ = delay; - impl.initialDelay_ = delay + static_cast(region.getDelay(resources.midiState) * impl.sampleRate_); - impl.baseFrequency_ = resources.tuning.getFrequencyOfKey(impl.triggerEvent_.number); - impl.sampleEnd_ = int(region.getSampleEnd(resources.midiState)); + impl.initialDelay_ = delay + static_cast(region.getDelay(midiState) * impl.sampleRate_); + impl.baseFrequency_ = tuning.getFrequencyOfKey(impl.triggerEvent_.number); + impl.sampleEnd_ = int(region.getSampleEnd(midiState)); impl.sampleSize_ = impl.sampleEnd_- impl.sourcePosition_ - 1; impl.bendSmoother_.setSmoothing(region.bendSmooth, impl.sampleRate_); - impl.bendSmoother_.reset(region.getBendInCents(resources.midiState.getPitchBend())); + impl.bendSmoother_.reset(region.getBendInCents(midiState.getPitchBend())); - resources.modMatrix.initVoice(impl.id_, region.getId(), impl.initialDelay_); + ModMatrix& modMatrix = resources.getModMatrix(); + modMatrix.initVoice(impl.id_, region.getId(), impl.initialDelay_); impl.saveModulationTargets(®ion); if (region.checkSustain) { const bool sustainPressed = - resources.midiState.getCCValue(region.sustainCC) >= region.sustainThreshold; + midiState.getCCValue(region.sustainCC) >= region.sustainThreshold; impl.sustainState_ = sustainPressed ? Impl::SustainState::Sustaining : Impl::SustainState::Up; } if (region.checkSostenuto) { const bool sostenutoPressed = - resources.midiState.getCCValue(region.sostenutoCC) >= region.sostenutoThreshold; + midiState.getCCValue(region.sostenutoCC) >= region.sostenutoThreshold; impl.sostenutoState_ = sostenutoPressed ? Impl::SostenutoState::PreviouslyDown : Impl::SostenutoState::Up; } @@ -524,7 +535,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc int Voice::Impl::getCurrentSampleQuality() const noexcept { return (region_ && region_->sampleQuality) ? - *region_->sampleQuality : resources_.synthConfig.currentSampleQuality(); + *region_->sampleQuality : resources_.getSynthConfig().currentSampleQuality(); } int Voice::getCurrentSampleQuality() const noexcept @@ -536,7 +547,7 @@ int Voice::getCurrentSampleQuality() const noexcept int Voice::Impl::getCurrentOscillatorQuality() const noexcept { return (region_ && region_->oscillatorQuality) ? - *region_->oscillatorQuality : resources_.synthConfig.currentOscillatorQuality(); + *region_->oscillatorQuality : resources_.getSynthConfig().currentOscillatorQuality(); } int Voice::getCurrentOscillatorQuality() const noexcept @@ -571,7 +582,8 @@ void Voice::Impl::release(int delay) noexcept switchState(State::cleanMeUp); } - resources_.modMatrix.releaseVoice(id_, region_->getId(), delay); + ModMatrix& modMatrix = resources_.getModMatrix(); + modMatrix.releaseVoice(id_, region_->getId(), delay); } void Voice::off(int delay, bool fast) noexcept @@ -805,13 +817,15 @@ void Voice::Impl::resetCrossfades() noexcept float xfadeValue { 1.0f }; const auto xfCurve = region_->crossfadeCCCurve; + MidiState& midiState = resources_.getMidiState(); + for (const auto& mod : region_->crossfadeCCInRange) { - const auto value = resources_.midiState.getCCValue(mod.cc); + const auto value = midiState.getCCValue(mod.cc); xfadeValue *= crossfadeIn(mod.data, value, xfCurve); } for (const auto& mod : region_->crossfadeCCOutRange) { - const auto value = resources_.midiState.getCCValue(mod.cc); + const auto value = midiState.getCCValue(mod.cc); xfadeValue *= crossfadeOut(mod.data, value, xfCurve); } @@ -823,8 +837,11 @@ void Voice::Impl::applyCrossfades(absl::Span modulationSpan) noexcept const auto numSamples = modulationSpan.size(); const auto xfCurve = region_->crossfadeCCCurve; - auto tempSpan = resources_.bufferPool.getBuffer(numSamples); - auto xfadeSpan = resources_.bufferPool.getBuffer(numSamples); + MidiState& midiState = resources_.getMidiState(); + BufferPool& bufferPool = resources_.getBufferPool(); + + auto tempSpan = bufferPool.getBuffer(numSamples); + auto xfadeSpan = bufferPool.getBuffer(numSamples); if (!tempSpan || !xfadeSpan) return; @@ -833,7 +850,7 @@ void Voice::Impl::applyCrossfades(absl::Span modulationSpan) noexcept bool canShortcut = true; for (const auto& mod : region_->crossfadeCCInRange) { - const auto& events = resources_.midiState.getCCEvents(mod.cc); + const auto& events = midiState.getCCEvents(mod.cc); canShortcut &= (events.size() == 1); linearEnvelope(events, *tempSpan, [&](float x) { return crossfadeIn(mod.data, x, xfCurve); @@ -842,7 +859,7 @@ void Voice::Impl::applyCrossfades(absl::Span modulationSpan) noexcept } for (const auto& mod : region_->crossfadeCCOutRange) { - const auto& events = resources_.midiState.getCCEvents(mod.cc); + const auto& events = midiState.getCCEvents(mod.cc); canShortcut &= (events.size() == 1); linearEnvelope(events, *tempSpan, [&](float x) { return crossfadeOut(mod.data, x, xfCurve); @@ -859,7 +876,7 @@ void Voice::Impl::amplitudeEnvelope(absl::Span modulationSpan) noexcept { const auto numSamples = modulationSpan.size(); - ModMatrix& mm = resources_.modMatrix; + ModMatrix& mm = resources_.getModMatrix(); // Amplitude EG absl::Span ampegOut(mm.getModulation(masterAmplitudeTarget_), numSamples); @@ -891,7 +908,9 @@ void Voice::Impl::ampStageMono(AudioSpan buffer) noexcept const auto numSamples = buffer.getNumFrames(); const auto leftBuffer = buffer.getSpan(0); - auto modulationSpan = resources_.bufferPool.getBuffer(numSamples); + BufferPool& bufferPool = resources_.getBufferPool(); + + auto modulationSpan = bufferPool.getBuffer(numSamples); if (!modulationSpan) return; @@ -904,8 +923,10 @@ void Voice::Impl::ampStageStereo(AudioSpan buffer) noexcept { ScopedTiming logger { amplitudeDuration_ }; + BufferPool& bufferPool = resources_.getBufferPool(); + const auto numSamples = buffer.getNumFrames(); - auto modulationSpan = resources_.bufferPool.getBuffer(numSamples); + auto modulationSpan = bufferPool.getBuffer(numSamples); if (!modulationSpan) return; @@ -922,11 +943,13 @@ void Voice::Impl::panStageMono(AudioSpan buffer) noexcept const auto leftBuffer = buffer.getSpan(0); const auto rightBuffer = buffer.getSpan(1); - auto modulationSpan = resources_.bufferPool.getBuffer(numSamples); + BufferPool& bufferPool = resources_.getBufferPool(); + + auto modulationSpan = bufferPool.getBuffer(numSamples); if (!modulationSpan) return; - ModMatrix& mm = resources_.modMatrix; + ModMatrix& mm = resources_.getModMatrix(); // Prepare for stereo output copy(leftBuffer, rightBuffer); @@ -947,11 +970,13 @@ void Voice::Impl::panStageStereo(AudioSpan buffer) noexcept const auto leftBuffer = buffer.getSpan(0); const auto rightBuffer = buffer.getSpan(1); - auto modulationSpan = resources_.bufferPool.getBuffer(numSamples); + BufferPool& bufferPool = resources_.getBufferPool(); + + auto modulationSpan = bufferPool.getBuffer(numSamples); if (!modulationSpan) return; - ModMatrix& mm = resources_.modMatrix; + ModMatrix& mm = resources_.getModMatrix(); // Apply panning fill(*modulationSpan, region_->pan); @@ -1029,15 +1054,18 @@ void Voice::Impl::fillWithData(AudioSpan buffer) noexcept auto source = currentPromise_->getData(); + BufferPool& bufferPool = resources_.getBufferPool(); + const CurveSet& curves = resources_.getCurves(); + // calculate interpolation data // indices: integral position in the source audio // coeffs: fractional position normalized 0-1 - auto coeffs = resources_.bufferPool.getBuffer(numSamples); - auto indices = resources_.bufferPool.getIndexBuffer(numSamples); + auto coeffs = bufferPool.getBuffer(numSamples); + auto indices = bufferPool.getIndexBuffer(numSamples); if (!indices || !coeffs) return; { - auto jumps = resources_.bufferPool.getBuffer(numSamples); + auto jumps = bufferPool.getBuffer(numSamples); if (!jumps) return; @@ -1091,7 +1119,7 @@ void Voice::Impl::fillWithData(AudioSpan buffer) noexcept SpanHolder> partitionBuffers[2]; if (shouldLoop) { for (auto& buf : partitionBuffers) { - buf = resources_.bufferPool.getIndexBuffer(numSamples); + buf = bufferPool.getIndexBuffer(numSamples); if (!buf) return; } @@ -1198,9 +1226,9 @@ void Voice::Impl::fillWithData(AudioSpan buffer) noexcept source, ptBuffer, ptIndices, ptCoeffs, {}, quality); if (ptType == kPartitionLoopXfade) { - auto xfTemp1 = resources_.bufferPool.getBuffer(numSamples); - auto xfTemp2 = resources_.bufferPool.getBuffer(numSamples); - auto xfIndicesTemp = resources_.bufferPool.getIndexBuffer(numSamples); + auto xfTemp1 = bufferPool.getBuffer(numSamples); + auto xfTemp2 = bufferPool.getBuffer(numSamples); + auto xfIndicesTemp = bufferPool.getIndexBuffer(numSamples); if (!xfTemp1 || !xfTemp2 || !xfIndicesTemp) return; @@ -1224,7 +1252,7 @@ void Voice::Impl::fillWithData(AudioSpan buffer) noexcept xfCurve[i] = xfIn.evalNormalized(1.0f - xfCurvePos[i]); } else IF_CONSTEXPR (config::loopXfadeCurve == 1) { - const Curve& xfOut = resources_.curves.getCurve(6); + const Curve& xfOut = curves.getCurve(6); for (unsigned i = 0; i < ptSize; ++i) xfCurve[i] = xfOut.evalNormalized(xfCurvePos[i]); } @@ -1274,7 +1302,7 @@ void Voice::Impl::fillWithData(AudioSpan buffer) noexcept xfCurve[i] = xfIn.evalNormalized(xfInCurvePos[i]); } else IF_CONSTEXPR (config::loopXfadeCurve == 1) { - const Curve& xfIn = resources_.curves.getCurve(5); + const Curve& xfIn = curves.getCurve(5); for (unsigned i = 0; i < applySize; ++i) xfCurve[i] = xfIn.evalNormalized(xfInCurvePos[i]); } @@ -1462,7 +1490,10 @@ void Voice::Impl::fillWithGenerator(AudioSpan buffer) noexcept } else { const size_t numFrames = buffer.getNumFrames(); - auto frequencies = resources_.bufferPool.getBuffer(numFrames); + BufferPool& bufferPool = resources_.getBufferPool(); + ModMatrix& modMatrix = resources_.getModMatrix(); + + auto frequencies = bufferPool.getBuffer(numFrames); if (!frequencies) return; @@ -1475,7 +1506,7 @@ void Voice::Impl::fillWithGenerator(AudioSpan buffer) noexcept for (size_t i = 0; i < numFrames; ++i) (*frequencies)[i] = baseRatio * centsFactor(pitch[i]); - auto detuneSpan = resources_.bufferPool.getBuffer(numFrames); + auto detuneSpan = bufferPool.getBuffer(numFrames); if (!detuneSpan) return; @@ -1485,7 +1516,7 @@ void Voice::Impl::fillWithGenerator(AudioSpan buffer) noexcept if (oscillatorMode <= 0 && oscillatorMulti < 2) { // single oscillator - auto tempSpan = resources_.bufferPool.getBuffer(numFrames); + auto tempSpan = bufferPool.getBuffer(numFrames); if (!tempSpan) return; @@ -1498,13 +1529,13 @@ void Voice::Impl::fillWithGenerator(AudioSpan buffer) noexcept } else if (oscillatorMode <= 0 && oscillatorMulti >= 3) { // unison oscillator - auto tempSpan = resources_.bufferPool.getBuffer(numFrames); - auto tempLeftSpan = resources_.bufferPool.getBuffer(numFrames); - auto tempRightSpan = resources_.bufferPool.getBuffer(numFrames); + auto tempSpan = bufferPool.getBuffer(numFrames); + auto tempLeftSpan = bufferPool.getBuffer(numFrames); + auto tempRightSpan = bufferPool.getBuffer(numFrames); if (!tempSpan || !tempLeftSpan || !tempRightSpan) return; - const float* detuneMod = resources_.modMatrix.getModulation(oscillatorDetuneTarget_); + const float* detuneMod = modMatrix.getModulation(oscillatorDetuneTarget_); for (unsigned u = 0, uSize = waveUnisonSize_; u < uSize; ++u) { WavetableOscillator& osc = waveOscillators_[u]; osc.setQuality(quality); @@ -1531,7 +1562,7 @@ void Voice::Impl::fillWithGenerator(AudioSpan buffer) noexcept } else { // modulated oscillator - auto tempSpan = resources_.bufferPool.getBuffer(numFrames); + auto tempSpan = bufferPool.getBuffer(numFrames); if (!tempSpan) return; @@ -1541,11 +1572,11 @@ void Voice::Impl::fillWithGenerator(AudioSpan buffer) noexcept oscMod.setQuality(quality); // compute the modulator - auto modulatorSpan = resources_.bufferPool.getBuffer(numFrames); + auto modulatorSpan = bufferPool.getBuffer(numFrames); if (!modulatorSpan) return; - const float* detuneMod = resources_.modMatrix.getModulation(oscillatorDetuneTarget_); + const float* detuneMod = modMatrix.getModulation(oscillatorDetuneTarget_); if (!detuneMod) fill(*detuneSpan, waveDetuneRatio_[1]); else { @@ -1560,7 +1591,7 @@ void Voice::Impl::fillWithGenerator(AudioSpan buffer) noexcept const float oscillatorModDepth = region_->oscillatorModDepth; if (oscillatorModDepth != 1.0f) applyGain1(oscillatorModDepth, *modulatorSpan); - const float* modDepthMod = resources_.modMatrix.getModulation(oscillatorModDepthTarget_); + const float* modDepthMod = modMatrix.getModulation(oscillatorModDepthTarget_); if (modDepthMod) applyGain(absl::MakeConstSpan(modDepthMod, numFrames), *modulatorSpan); @@ -1672,12 +1703,12 @@ void Voice::Impl::updateLoopInformation() noexcept if (!region_->shouldLoop()) return; - Resources& resources = resources_; + MidiState& midiState = resources_.getMidiState(); const FileInformation& info = currentPromise_->information; const double rate = info.sampleRate; - loop_.start = static_cast(region_->loopStart(resources.midiState)); - loop_.end = max(static_cast(region_->loopEnd(resources.midiState)), loop_.start); + loop_.start = static_cast(region_->loopStart(midiState)); + loop_.end = max(static_cast(region_->loopEnd(midiState)), loop_.start); loop_.size = loop_.end + 1 - loop_.start; loop_.xfSize = static_cast(lroundPositive(region_->loopCrossfade * rate)); // Clamp the crossfade to the part available before the loop starts @@ -1906,7 +1937,8 @@ void Voice::Impl::pitchEnvelope(absl::Span pitchSpan) noexcept { const size_t numFrames = pitchSpan.size(); - const EventVector& events = resources_.midiState.getPitchEvents(); + const MidiState& midiState = resources_.getMidiState(); + const EventVector& events = midiState.getPitchEvents(); const auto bendLambda = [this](float bend) { return region_->getBendInCents(bend); }; @@ -1917,7 +1949,7 @@ void Voice::Impl::pitchEnvelope(absl::Span pitchSpan) noexcept linearEnvelope(events, pitchSpan, bendLambda); bendSmoother_.process(pitchSpan, pitchSpan); - ModMatrix& mm = resources_.modMatrix; + ModMatrix& mm = resources_.getModMatrix(); if (float* mod = mm.getModulation(pitchTarget_)) add(absl::MakeSpan(mod, numFrames), pitchSpan); @@ -1931,7 +1963,7 @@ void Voice::Impl::resetSmoothers() noexcept void Voice::Impl::saveModulationTargets(const Region* region) noexcept { - ModMatrix& mm = resources_.modMatrix; + ModMatrix& mm = resources_.getModMatrix(); masterAmplitudeTarget_ = mm.findTarget(ModKey::createNXYZ(ModId::MasterAmplitude, region->getId())); amplitudeTarget_ = mm.findTarget(ModKey::createNXYZ(ModId::Amplitude, region->getId())); volumeTarget_ = mm.findTarget(ModKey::createNXYZ(ModId::Volume, region->getId())); diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 0643004f0..f8032a7d0 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -10,6 +10,7 @@ #include "Region.h" #include "Resources.h" #include "AudioSpan.h" +#include "Logger.h" #include "utility/NumericId.h" #include "utility/LeakDetector.h" #include diff --git a/src/sfizz/modulations/sources/Controller.cpp b/src/sfizz/modulations/sources/Controller.cpp index f6b16fb03..7d6caea9e 100644 --- a/src/sfizz/modulations/sources/Controller.cpp +++ b/src/sfizz/modulations/sources/Controller.cpp @@ -37,8 +37,8 @@ ControllerSource::~ControllerSource() float ControllerSource::Impl::getLastTransformedValue(uint16_t cc, uint8_t curveIndex) const noexcept { ASSERT(res_); - const Curve& curve = res_->curves.getCurve(curveIndex); - const auto lastCCValue = res_->midiState.getCCValue(cc); + const Curve& curve = res_->getCurves().getCurve(curveIndex); + const auto lastCCValue = res_->getMidiState().getCCValue(cc); return curve.evalNormalized(lastCCValue); } @@ -89,8 +89,8 @@ void ControllerSource::generate(const ModKey& sourceKey, NumericId voiceI { const ModKey::Parameters p = sourceKey.parameters(); const Resources& res = *impl_->res_; - const Curve& curve = res.curves.getCurve(p.curve); - const MidiState& ms = res.midiState; + const Curve& curve = res.getCurves().getCurve(p.curve); + const MidiState& ms = res.getMidiState(); bool canShortcut = false; auto transformValue = [p, &curve](float x) { @@ -110,7 +110,7 @@ void ControllerSource::generate(const ModKey& sourceKey, NumericId voiceI const auto voice = impl_->voiceManager_->getVoiceById(voiceId); const float fillValue = voice && voice->getTriggerEvent().type == TriggerEventType::NoteOn ? - impl_->res_->midiState.getPolyAftertouch(voice->getTriggerEvent().number) : 0.0f; + impl_->res_->getMidiState().getPolyAftertouch(voice->getTriggerEvent().number) : 0.0f; sfz::fill(buffer, quantize(fillValue)); canShortcut = true; diff --git a/src/sfizz/modulations/sources/Controller.h b/src/sfizz/modulations/sources/Controller.h index 2736bb5be..695b36df4 100644 --- a/src/sfizz/modulations/sources/Controller.h +++ b/src/sfizz/modulations/sources/Controller.h @@ -11,7 +11,7 @@ namespace sfz { -struct Resources; +class Resources; class ControllerSource : public ModGenerator { public: diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index c1d568b21..50c734147 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -421,7 +421,7 @@ TEST_CASE("[Files] Default path is ignored for generators") TEST_CASE("[Files] Set CC applies properly") { Synth synth; - const auto& midiState = synth.getResources().midiState; + const MidiState& midiState = synth.getResources().getMidiState(); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_cc.sfz"); REQUIRE(midiState.getCCValue(142) == 63_norm); REQUIRE(midiState.getCCValue(61) == 122_norm); @@ -430,7 +430,7 @@ TEST_CASE("[Files] Set CC applies properly") TEST_CASE("[Files] Set HDCC applies properly") { sfz::Synth synth; - const auto& midiState = synth.getResources().midiState; + const MidiState& midiState = synth.getResources().getMidiState(); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_hdcc.sfz"); REQUIRE(midiState.getCCValue(142) == Approx(0.5678)); REQUIRE(midiState.getCCValue(61) == Approx(0.1234)); @@ -439,7 +439,7 @@ TEST_CASE("[Files] Set HDCC applies properly") TEST_CASE("[Files] Set RealCC applies properly") { sfz::Synth synth; - const auto& midiState = synth.getResources().midiState; + const MidiState& midiState = synth.getResources().getMidiState(); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_realcc.sfz"); REQUIRE(midiState.getCCValue(142) == Approx(0.5678)); REQUIRE(midiState.getCCValue(61) == Approx(0.1234)); diff --git a/tests/FlexEGT.cpp b/tests/FlexEGT.cpp index a7a2e634e..76657eea4 100644 --- a/tests/FlexEGT.cpp +++ b/tests/FlexEGT.cpp @@ -8,6 +8,7 @@ #include "sfizz/Synth.h" #include "sfizz/AudioBuffer.h" #include "sfizz/FlexEnvelope.h" +#include "sfizz/modulations/ModMatrix.h" #include "catch2/catch.hpp" #include "TestHelpers.h" #include @@ -42,7 +43,7 @@ TEST_CASE("[FlexEG] Values") REQUIRE( egDescription.points[4].time == .4_a ); REQUIRE( egDescription.points[4].level == 1.0_a ); REQUIRE( egDescription.sustain == 3 ); - REQUIRE(synth.getResources().modMatrix.toDotGraph() == createDefaultGraph({ + REQUIRE(synth.getResources().getModMatrix().toDotGraph() == createDefaultGraph({ R"("EG 1 {0}" -> "Amplitude {0}")", })); } @@ -68,7 +69,7 @@ TEST_CASE("[FlexEG] Default values") REQUIRE( egDescription.points[1].level == 0.0_a ); REQUIRE( egDescription.points[2].time == .1_a ); REQUIRE( egDescription.points[2].level == .25_a ); - REQUIRE( synth.getResources().modMatrix.toDotGraph() == createDefaultGraph({}) ); + REQUIRE( synth.getResources().getModMatrix().toDotGraph() == createDefaultGraph({}) ); } TEST_CASE("[FlexEG] Connections") @@ -86,7 +87,7 @@ TEST_CASE("[FlexEG] Connections") REQUIRE(synth.getNumRegions() == 6); REQUIRE( synth.getRegionView(0)->flexEGs.size() == 1 ); REQUIRE( synth.getRegionView(0)->flexEGs[0].points.size() == 2 ); - REQUIRE( synth.getResources().modMatrix.toDotGraph() == createDefaultGraph({ + REQUIRE( synth.getResources().getModMatrix().toDotGraph() == createDefaultGraph({ R"("EG 1 {0}" -> "Amplitude {0}")", R"("EG 1 {1}" -> "Pan {1}")", R"("EG 1 {2}" -> "Width {2}")", diff --git a/tests/ModulationsT.cpp b/tests/ModulationsT.cpp index 48ede30b1..cc9adfcb4 100644 --- a/tests/ModulationsT.cpp +++ b/tests/ModulationsT.cpp @@ -4,6 +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/modulations/ModMatrix.h" #include "sfizz/modulations/ModId.h" #include "sfizz/modulations/ModKey.h" #include "sfizz/Synth.h" @@ -89,7 +90,7 @@ pan_oncc36=12.5 pan_stepcc36=0.5 width_oncc425=29 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("Controller 20 {curve=3, smooth=0, step=0}" -> "Amplitude {0}")", R"("Controller 42 {curve=0, smooth=32, step=0}" -> "Pitch {0}")", @@ -108,7 +109,7 @@ TEST_CASE("[Modulations] Filter CC connections") resonance3=-1 resonance3_oncc1=2 resonance3_smoothcc1=10 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("Controller 1 {curve=0, smooth=10, step=0}" -> "FilterResonance {0, N=3}")", R"("Controller 2 {curve=2, smooth=0, step=0}" -> "FilterCutoff {0, N=2}")", @@ -126,7 +127,7 @@ TEST_CASE("[Modulations] EQ CC connections") eq3_bw_oncc1=2 eq3_bw_smoothcc1=10 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("Controller 1 {curve=0, smooth=10, step=0}" -> "EqBandwidth {0, N=3}")", R"("Controller 2 {curve=0, smooth=0, step=0.1}" -> "EqGain {0, N=1}")", @@ -147,7 +148,7 @@ TEST_CASE("[Modulations] LFO Filter connections") lfo6_freq=3 lfo6_fil1gain=-1 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("LFO 1 {0}" -> "FilterCutoff {0, N=1}")", R"("LFO 2 {0}" -> "FilterCutoff {0, N=1}")", @@ -171,7 +172,7 @@ TEST_CASE("[Modulations] EG Filter connections") eg6_time1=3 eg6_fil1gain=-1 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("EG 1 {0}" -> "FilterCutoff {0, N=1}")", R"("EG 2 {0}" -> "FilterCutoff {0, N=1}")", @@ -195,7 +196,7 @@ TEST_CASE("[Modulations] LFO EQ connections") lfo6_freq=3 lfo6_eq1freq=-1 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("LFO 1 {0}" -> "EqBandwidth {0, N=1}")", R"("LFO 2 {0}" -> "EqFrequency {0, N=2}")", @@ -219,7 +220,7 @@ TEST_CASE("[Modulations] EG EQ connections") eg6_freq=3 eg6_eq1freq=-1 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("EG 1 {0}" -> "EqBandwidth {0, N=1}")", R"("EG 2 {0}" -> "EqFrequency {0, N=2}")", @@ -244,7 +245,7 @@ TEST_CASE("[Modulations] FlexEG Ampeg target") eg1_ampeg=1 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createModulationDotGraph({ R"("Controller 10 {curve=1, smooth=10, step=0}" -> "Pan {0}")", R"("Controller 11 {curve=4, smooth=10, step=0}" -> "Amplitude {0}")", @@ -269,7 +270,7 @@ TEST_CASE("[Modulations] FlexEG Ampeg target with 2 FlexEGs") eg2_ampeg=1 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createModulationDotGraph({ R"("Controller 10 {curve=1, smooth=10, step=0}" -> "Pan {0}")", R"("Controller 11 {curve=4, smooth=10, step=0}" -> "Amplitude {0}")", @@ -296,7 +297,7 @@ TEST_CASE("[Modulations] FlexEG Ampeg target with multiple EGs targeting ampeg") eg2_ampeg=1 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createModulationDotGraph({ R"("Controller 10 {curve=1, smooth=10, step=0}" -> "Pan {0}")", R"("Controller 11 {curve=4, smooth=10, step=0}" -> "Amplitude {0}")", @@ -313,7 +314,7 @@ TEST_CASE("[Modulations] Override the default volume controller") sample=*sine tune_oncc7=1200 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createModulationDotGraph({ R"("AmplitudeEG {0}" -> "MasterAmplitude {0}")", R"("Controller 10 {curve=1, smooth=10, step=0}" -> "Pan {0}")", @@ -330,7 +331,7 @@ TEST_CASE("[Modulations] Override the default pan controller") sample=*sine on_locc10=127 on_hicc10=127 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createModulationDotGraph({ R"("AmplitudeEG {0}" -> "MasterAmplitude {0}")", R"("Controller 11 {curve=4, smooth=10, step=0}" -> "Amplitude {0}")", @@ -346,7 +347,7 @@ TEST_CASE("[Modulations] Aftertouch connections") sample=*sine cutoff2_chanaft=1000 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("ChannelAftertouch" -> "FilterCutoff {0, N=1}")", R"("ChannelAftertouch" -> "FilterCutoff {1, N=2}")", @@ -362,7 +363,7 @@ TEST_CASE("[Modulations] LFO v1 connections") sample=*sine fillfo_freq=1.0 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("AmplitudeLFO {0}" -> "Volume {0}")", R"("PitchLFO {1}" -> "Pitch {1}")", @@ -379,7 +380,7 @@ TEST_CASE("[Modulations] LFO v1 CC connections") sample=*sine fillfo_depth_oncc3=-3600 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("Controller 1 {curve=0, smooth=0, step=0}" -> "AmplitudeLFODepth {0}")", R"("Controller 2 {curve=0, smooth=0, step=0}" -> "PitchLFODepth {1}")", @@ -399,7 +400,7 @@ TEST_CASE("[Modulations] LFO v1 CC frequency connections") sample=*sine fillfo_freqcc3=-3600 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("Controller 1 {curve=0, smooth=0, step=0}" -> "AmplitudeLFOFrequency {0}")", R"("Controller 2 {curve=0, smooth=0, step=0}" -> "PitchLFOFrequency {1}")", @@ -419,7 +420,7 @@ TEST_CASE("[Modulations] LFO v1 aftertouch connections") sample=*sine fillfo_depthchanaft=-3600 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("ChannelAftertouch" -> "AmplitudeLFODepth {0}")", R"("ChannelAftertouch" -> "PitchLFODepth {1}")", @@ -439,7 +440,7 @@ TEST_CASE("[Modulations] LFO v1 aftertouch frequency connections") sample=*sine fillfo_freqchanaft=-3600 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("ChannelAftertouch" -> "AmplitudeLFOFrequency {0}")", R"("ChannelAftertouch" -> "PitchLFOFrequency {1}")", @@ -459,7 +460,7 @@ TEST_CASE("[Modulations] LFO v1 poly aftertouch connections") sample=*sine fillfo_depthpolyaft=-3600 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("PolyAftertouch" -> "AmplitudeLFODepth {0}")", R"("PolyAftertouch" -> "PitchLFODepth {1}")", @@ -479,7 +480,7 @@ TEST_CASE("[Modulations] LFO v1 poly aftertouch frequency connections") sample=*sine fillfo_freqpolyaft=-3600 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("PolyAftertouch" -> "AmplitudeLFOFrequency {0}")", R"("PolyAftertouch" -> "PitchLFOFrequency {1}")", @@ -498,7 +499,7 @@ TEST_CASE("[Modulations] EG v1 CC connections") sample=*sine fileg_depth_oncc3=-3600 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("Controller 2 {curve=0, smooth=0, step=0}" -> "PitchEGDepth {0}")", R"("Controller 3 {curve=0, smooth=0, step=0}" -> "FilterEGDepth {1}")", @@ -523,7 +524,7 @@ TEST_CASE("[Modulations] LFO CC connections") pitch_oncc137=1200 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("Controller 128 {curve=0, smooth=0, step=0}" -> "Pitch {0}")", R"("Controller 129 {curve=0, smooth=0, step=0}" -> "Pitch {0}")", @@ -547,7 +548,7 @@ TEST_CASE("[Modulations] Extended CCs connections") lfo3_freq=0.1 lfo3_phase_cc1=2 lfo3_phase_smoothcc1=10 lfo3_phase_stepcc1=0.2 lfo3_phase_curvecc1=1 lfo3_amplitude=50 )"); - const std::string graph = synth.getResources().modMatrix.toDotGraph(); + const std::string graph = synth.getResources().getModMatrix().toDotGraph(); REQUIRE(graph == createDefaultGraph({ R"("LFO 1 {0}" -> "Volume {0}")", R"("LFO 2 {0}" -> "Pitch {0}")", diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 2e71c21f3..3ca5db3fb 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -141,7 +141,7 @@ TEST_CASE("[Synth] Reset all controllers") { sfz::Synth synth; sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; - const auto& midiState = synth.getResources().midiState; + const sfz::MidiState& midiState = synth.getResources().getMidiState(); synth.cc(0, 12, 64); synth.renderBlock(buffer); REQUIRE(midiState.getCCValue(12) == 64_norm); @@ -401,7 +401,7 @@ TEST_CASE("[Synth] Gain to mix") TEST_CASE("[Synth] Basic curves") { sfz::Synth synth; - const auto& curves = synth.getResources().curves; + const sfz::CurveSet& curves = synth.getResources().getCurves(); synth.loadSfzString(fs::current_path() / "tests/TestFiles/curves.sfz", R"( sample=*sine curve_index=18 v000=0 v095=0.5 v127=1 From 41ab126549806b4b456fa20e8437fa1ffc753652 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 25 Apr 2021 05:31:26 +0200 Subject: [PATCH 010/193] Remove header dependency Parser from Synth --- src/CMakeLists.txt | 1 + src/sfizz/Synth.cpp | 13 +++++++++++++ src/sfizz/Synth.h | 17 ++++++++++++++++- src/sfizz/SynthPrivate.h | 2 ++ src/sfizz/parser/Parser.cpp | 1 + src/sfizz/parser/Parser.h | 15 ++------------- src/sfizz/parser/ParserListener.h | 29 +++++++++++++++++++++++++++++ src/sfizz/sfizz.cpp | 4 ++-- src/sfizz/sfizz_wrapper.cpp | 4 ++-- tests/FilesT.cpp | 1 + tests/ParsingT.cpp | 1 + tests/TestHelpers.cpp | 1 + 12 files changed, 71 insertions(+), 18 deletions(-) create mode 100644 src/sfizz/parser/ParserListener.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 96eed54c7..1d4d1dff7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -202,6 +202,7 @@ set(SFIZZ_PARSER_HEADERS sfizz/Range.h sfizz/Opcode.h sfizz/parser/Parser.h + sfizz/parser/ParserListener.h sfizz/parser/ParserPrivate.h sfizz/parser/ParserPrivate.hpp sfizz/SfzHelpers.h) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index c992d2394..c76de6166 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -28,6 +28,7 @@ #include "utility/XmlHelpers.h" #include "Voice.h" #include "Interpolators.h" +#include "parser/Parser.h" #include #include #include @@ -1996,6 +1997,18 @@ void Synth::allSoundOff() noexcept effectBus->clear(); } +void Synth::addExternalDefinition(const std::string& id, const std::string& value) +{ + Impl& impl = *impl_; + impl.parser_.addExternalDefinition(id, value); +} + +void Synth::clearExternalDefinitions() +{ + Impl& impl = *impl_; + impl.parser_.clearExternalDefinitions(); +} + const BitArray& Synth::getUsedCCs() const noexcept { Impl& impl = *impl_; diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index 0722ecd46..3ffaedab3 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -10,7 +10,6 @@ #include "Messaging.h" #include "utility/NumericId.h" #include "utility/LeakDetector.h" -#include "parser/Parser.h" #include #include #include @@ -22,6 +21,7 @@ template class BitArray; namespace sfz { // Forward declarations for the introspection methods +class Parser; class RegionSet; class PolyphonyGroup; class EffectBus; @@ -29,6 +29,9 @@ struct Region; struct Layer; class Voice; +using CCNamePair = std::pair; +using NoteNamePair = std::pair; + /** * @brief This class is the core of the sfizz library. In C++ it is the main point * of entry and in C the interface basically maps the functions of the class into @@ -642,6 +645,18 @@ class Synth final { */ void allSoundOff() noexcept; + /** + * @brief Add external definitions prior to loading. + * + */ + void addExternalDefinition(const std::string& id, const std::string& value); + + /** + * @brief Clears external definitions for the next file loading. + * + */ + void clearExternalDefinitions(); + /** * @brief Get the parser. * diff --git a/src/sfizz/SynthPrivate.h b/src/sfizz/SynthPrivate.h index 157bba4b2..4b2446491 100644 --- a/src/sfizz/SynthPrivate.h +++ b/src/sfizz/SynthPrivate.h @@ -13,6 +13,8 @@ #include "modulations/sources/ChannelAftertouch.h" #include "modulations/sources/PolyAftertouch.h" #include "modulations/sources/LFO.h" +#include "parser/Parser.h" +#include "parser/ParserListener.h" namespace sfz { diff --git a/src/sfizz/parser/Parser.cpp b/src/sfizz/parser/Parser.cpp index 40306431c..5ae4aef81 100644 --- a/src/sfizz/parser/Parser.cpp +++ b/src/sfizz/parser/Parser.cpp @@ -5,6 +5,7 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "Parser.h" +#include "ParserListener.h" #include "ParserPrivate.h" #include "absl/memory/memory.h" #include diff --git a/src/sfizz/parser/Parser.h b/src/sfizz/parser/Parser.h index 1be513611..e83fc1ada 100644 --- a/src/sfizz/parser/Parser.h +++ b/src/sfizz/parser/Parser.h @@ -16,6 +16,7 @@ namespace sfz { class Reader; +class ParserListener; struct SourceLocation; struct SourceRange; @@ -50,19 +51,7 @@ class Parser { size_t getErrorCount() const noexcept { return _errorCount; } size_t getWarningCount() const noexcept { return _warningCount; } - class Listener { - public: - // low-level parsing - virtual void onParseBegin() {} - virtual void onParseEnd() {} - virtual void onParseHeader(const SourceRange& /*range*/, const std::string& /*header*/) {} - virtual void onParseOpcode(const SourceRange& /*rangeOpcode*/, const SourceRange& /*rangeValue*/, const std::string& /*name*/, const std::string& /*value*/) {} - virtual void onParseError(const SourceRange& /*range*/, const std::string& /*message*/) {} - virtual void onParseWarning(const SourceRange& /*range*/, const std::string& /*message*/) {} - - // high-level parsing - virtual void onParseFullBlock(const std::string& /*header*/, const std::vector& /*opcodes*/) {} - }; + using Listener = ParserListener; void setListener(Listener* listener) noexcept { _listener = listener; } diff --git a/src/sfizz/parser/ParserListener.h b/src/sfizz/parser/ParserListener.h new file mode 100644 index 000000000..fa208ff97 --- /dev/null +++ b/src/sfizz/parser/ParserListener.h @@ -0,0 +1,29 @@ +// 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 { +struct SourceRange; +struct Opcode; + +class ParserListener { +public: + // low-level parsing + virtual void onParseBegin() {} + virtual void onParseEnd() {} + virtual void onParseHeader(const SourceRange& /*range*/, const std::string& /*header*/) {} + virtual void onParseOpcode(const SourceRange& /*rangeOpcode*/, const SourceRange& /*rangeValue*/, const std::string& /*name*/, const std::string& /*value*/) {} + virtual void onParseError(const SourceRange& /*range*/, const std::string& /*message*/) {} + virtual void onParseWarning(const SourceRange& /*range*/, const std::string& /*message*/) {} + + // high-level parsing + virtual void onParseFullBlock(const std::string& /*header*/, const std::vector& /*opcodes*/) {} +}; + +} // namespace sfz diff --git a/src/sfizz/sfizz.cpp b/src/sfizz/sfizz.cpp index 2a1d2d9d8..2d7d58c0a 100644 --- a/src/sfizz/sfizz.cpp +++ b/src/sfizz/sfizz.cpp @@ -353,12 +353,12 @@ void sfz::Sfizz::allSoundOff() noexcept void sfz::Sfizz::addExternalDefinition(const std::string& id, const std::string& value) { - synth->synth.getParser().addExternalDefinition(id, value); + synth->synth.addExternalDefinition(id, value); } void sfz::Sfizz::clearExternalDefinitions() { - synth->synth.getParser().clearExternalDefinitions(); + synth->synth.clearExternalDefinitions(); } std::string sfz::Sfizz::exportMidnam(const std::string& model) const diff --git a/src/sfizz/sfizz_wrapper.cpp b/src/sfizz/sfizz_wrapper.cpp index 5166081d1..4b191ab50 100644 --- a/src/sfizz/sfizz_wrapper.cpp +++ b/src/sfizz/sfizz_wrapper.cpp @@ -335,12 +335,12 @@ void sfizz_all_sound_off(sfizz_synth_t* synth) void sfizz_add_external_definitions(sfizz_synth_t* synth, const char* id, const char* value) { - synth->synth.getParser().addExternalDefinition(id, value); + synth->synth.addExternalDefinition(id, value); } void sfizz_clear_external_definitions(sfizz_synth_t* synth) { - synth->synth.getParser().clearExternalDefinitions(); + synth->synth.clearExternalDefinitions(); } unsigned int sfizz_get_num_key_labels(sfizz_synth_t* synth) diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 50c734147..69f178f9e 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -8,6 +8,7 @@ #include "sfizz/Synth.h" #include "sfizz/Voice.h" #include "sfizz/SfzHelpers.h" +#include "sfizz/parser/Parser.h" #include "sfizz/modulations/ModId.h" #include "sfizz/modulations/ModKey.h" #include "catch2/catch.hpp" diff --git a/tests/ParsingT.cpp b/tests/ParsingT.cpp index cb1096ce5..3b6859cf4 100644 --- a/tests/ParsingT.cpp +++ b/tests/ParsingT.cpp @@ -6,6 +6,7 @@ #include "sfizz/SfzHelpers.h" #include "sfizz/parser/Parser.h" +#include "sfizz/parser/ParserListener.h" #include #include "catch2/catch.hpp" #include "absl/strings/string_view.h" diff --git a/tests/TestHelpers.cpp b/tests/TestHelpers.cpp index 3e1f77157..78e6cb854 100644 --- a/tests/TestHelpers.cpp +++ b/tests/TestHelpers.cpp @@ -6,6 +6,7 @@ #include "TestHelpers.h" #include "sfizz/modulations/ModId.h" +#include size_t RegionCCView::size() const { From cd1e4567f2b2373fa50afe1c0ebec84a684eb31e Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 25 Apr 2021 05:49:45 +0200 Subject: [PATCH 011/193] Add cmake option for -ftime-trace --- CMakeLists.txt | 1 + cmake/SfizzConfig.cmake | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index b45737c0c..d39543437 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ option_ex (SFIZZ_USE_SYSTEM_ABSEIL "Use Abseil libraries preinstalled on system" option_ex (SFIZZ_USE_SYSTEM_SIMDE "Use SIMDe libraries preinstalled on system" OFF) option_ex (SFIZZ_STATIC_DEPENDENCIES "Link dependencies statically" OFF) option_ex (SFIZZ_RELEASE_ASSERTS "Forced assertions in release builds" OFF) +option_ex (SFIZZ_PROFILE_BUILD "Profile the build time" OFF) # The fixed number of controller parameters set(SFIZZ_NUM_CCS 512) diff --git a/cmake/SfizzConfig.cmake b/cmake/SfizzConfig.cmake index 21d715852..40f248de1 100644 --- a/cmake/SfizzConfig.cmake +++ b/cmake/SfizzConfig.cmake @@ -1,4 +1,5 @@ include(CMakeDependentOption) +include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) include(GNUWarnings) @@ -22,6 +23,18 @@ elseif((SFIZZ_LV2_UI OR SFIZZ_VST OR SFIZZ_AU OR SFIZZ_VST2) AND CMAKE_CXX_STAND set(CMAKE_CXX_STANDARD 14) endif() +# Set build profiling options +if(SFIZZ_PROFILE_BUILD) + check_c_compiler_flag("-ftime-trace" SFIZZ_HAVE_CFLAG_FTIME_TRACE) + check_cxx_compiler_flag("-ftime-trace" SFIZZ_HAVE_CXXFLAG_FTIME_TRACE) + if(SFIZZ_HAVE_CFLAG_FTIME_TRACE) + add_compile_options("$<$:-ftime-trace>") + endif() + if(SFIZZ_HAVE_CXXFLAG_FTIME_TRACE) + add_compile_options("$<$:-ftime-trace>") + endif() +endif() + # Process sources as UTF-8 if(MSVC) add_compile_options("/utf-8") From 8a5e4f4fe05e36617cb1c3851319e64037f9f08a Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 25 Apr 2021 05:58:04 +0200 Subject: [PATCH 012/193] Try building with ninja generator --- .appveyor.yml | 4 ++-- .github/workflows/build.yml | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index a7a6cee27..cf383f9d3 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -42,9 +42,9 @@ for: - ${APPVEYOR_BUILD_FOLDER}/scripts/appveyor/before_build.sh build_script: - cd build - - make -j2 sfizz_tests + - cmake --build . -j2 --target sfizz_tests - tests/sfizz_tests - - make -j2 + - cmake --build . -j2 after_build: - chmod +x ${APPVEYOR_BUILD_FOLDER}/scripts/appveyor/after_build.sh - ${APPVEYOR_BUILD_FOLDER}/scripts/appveyor/after_build.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3e460e95e..9fbd501e8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,6 +44,7 @@ jobs: run: | sudo apt-get update && \ sudo apt-get install \ + ninja-build \ libjack-jackd2-dev \ libsndfile1-dev \ libcairo2-dev \ @@ -64,7 +65,8 @@ jobs: shell: bash working-directory: ${{runner.workspace}}/build run: | - cmake "$GITHUB_WORKSPACE" -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ + cmake "$GITHUB_WORKSPACE" -G Ninja \ + -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ -DSFIZZ_JACK=ON \ -DSFIZZ_VST=ON \ -DSFIZZ_LV2_UI=ON \ @@ -239,7 +241,7 @@ jobs: shell: bash run: | pacman -Sqyu --noconfirm - pacman -Sq --needed --noconfirm base-devel git wget mingw-w64-cmake mingw-w64-gcc mingw-w64-pkg-config mingw-w64-libsndfile + pacman -Sq --needed --noconfirm base-devel git wget ninja mingw-w64-cmake mingw-w64-gcc mingw-w64-pkg-config mingw-w64-libsndfile - uses: actions/checkout@v2 with: submodules: recursive @@ -263,7 +265,7 @@ jobs: shell: bash working-directory: ${{runner.workspace}}/build run: | - i686-w64-mingw32-cmake "$GITHUB_WORKSPACE" \ + i686-w64-mingw32-cmake "$GITHUB_WORKSPACE" -G Ninja \ -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ -DENABLE_LTO=OFF \ -DSFIZZ_JACK=OFF \ @@ -308,7 +310,7 @@ jobs: shell: bash run: | pacman -Sqyu --noconfirm - pacman -Sq --needed --noconfirm base-devel git wget mingw-w64-cmake mingw-w64-gcc mingw-w64-pkg-config mingw-w64-libsndfile + pacman -Sq --needed --noconfirm base-devel git wget ninja mingw-w64-cmake mingw-w64-gcc mingw-w64-pkg-config mingw-w64-libsndfile - uses: actions/checkout@v2 with: submodules: recursive @@ -332,7 +334,7 @@ jobs: shell: bash working-directory: ${{runner.workspace}}/build run: | - x86_64-w64-mingw32-cmake "$GITHUB_WORKSPACE" \ + x86_64-w64-mingw32-cmake "$GITHUB_WORKSPACE" -G Ninja \ -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ -DENABLE_LTO=OFF \ -DSFIZZ_JACK=OFF \ From 508dae298de8a1c79e1073432576ee00f71c79ea Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 25 Apr 2021 06:48:49 +0200 Subject: [PATCH 013/193] Compile ghc::filesystem implementations once only --- cmake/SfizzDeps.cmake | 16 ++++++++++++++-- src/sfizz/FileMetadata.cpp | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/cmake/SfizzDeps.cmake b/cmake/SfizzDeps.cmake index 1e4bd556e..fbf5f8e00 100644 --- a/cmake/SfizzDeps.cmake +++ b/cmake/SfizzDeps.cmake @@ -183,9 +183,21 @@ add_library(sfizz::atomic_queue ALIAS sfizz_atomic_queue) target_include_directories(sfizz_atomic_queue INTERFACE "external/atomic_queue/include") # The ghc::filesystem library -add_library(sfizz_filesystem INTERFACE) +if(FALSE) + # header-only + add_library(sfizz_filesystem INTERFACE) + target_include_directories(sfizz_filesystem INTERFACE "external/filesystem/include") +else() + # static library + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/fs_std_impl.cpp" "#include ") + add_library(sfizz_filesystem_impl STATIC "${CMAKE_CURRENT_BINARY_DIR}/fs_std_impl.cpp") + target_include_directories(sfizz_filesystem_impl PUBLIC "external/filesystem/include") + # + add_library(sfizz_filesystem INTERFACE) + target_compile_definitions(sfizz_filesystem INTERFACE "GHC_FILESYSTEM_FWD") + target_link_libraries(sfizz_filesystem INTERFACE sfizz_filesystem_impl) +endif() add_library(sfizz::filesystem ALIAS sfizz_filesystem) -target_include_directories(sfizz_filesystem INTERFACE "external/filesystem/include") # The atomic library add_library(sfizz_atomic INTERFACE) diff --git a/src/sfizz/FileMetadata.cpp b/src/sfizz/FileMetadata.cpp index 65ba9677c..541734a9b 100644 --- a/src/sfizz/FileMetadata.cpp +++ b/src/sfizz/FileMetadata.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include From 11abec50ba13e056a331f0d5c0a51f5e841e63f0 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 25 Apr 2021 08:11:42 +0200 Subject: [PATCH 014/193] Fix copy and move operations of Buffer --- src/sfizz/Buffer.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sfizz/Buffer.h b/src/sfizz/Buffer.h index 399e04ae5..e8a5135aa 100644 --- a/src/sfizz/Buffer.h +++ b/src/sfizz/Buffer.h @@ -230,7 +230,7 @@ class Buffer { * * @param other */ - Buffer(const Buffer& other) + Buffer(const Buffer& other) { resize(other.size()); std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type)); @@ -241,7 +241,7 @@ class Buffer { * * @param other */ - Buffer(Buffer&& other) noexcept + Buffer(Buffer&& other) noexcept : largerSize(other.largerSize), alignedSize(other.alignedSize), normalData(other.normalData), @@ -252,7 +252,7 @@ class Buffer { other._clear(); } - Buffer& operator=(const Buffer& other) + Buffer& operator=(const Buffer& other) { if (this != &other) { resize(other.size()); @@ -261,7 +261,7 @@ class Buffer { return *this; } - Buffer& operator=(Buffer&& other) noexcept + Buffer& operator=(Buffer&& other) noexcept { if (this != &other) { if (largerSize > 0) From 38b36522ef767d9b31dc73b068356260daa7818c Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 26 Apr 2021 10:45:06 +0200 Subject: [PATCH 015/193] Add min/max specifications for LV2 controllers --- cmake/LV2Config.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/LV2Config.cmake b/cmake/LV2Config.cmake index 5a6fb0440..a7ca3cd10 100644 --- a/cmake/LV2Config.cmake +++ b/cmake/LV2Config.cmake @@ -62,7 +62,9 @@ function(sfizz_lv2_generate_controllers_ttl FILE) sfizz:cc${_i} a lv2:Parameter ; rdfs:label \"Controller ${_i}\" ; - rdfs:range atom:Float") + rdfs:range atom:Float ; + lv2:minimum 0.0 ; + lv2:maximum 1.0") if(_i LESS 128 AND NOT SFIZZ_LV2_PSA) math(EXPR _digit1 "${_i}>>4") From e17b6e7d52cb9c458c54df9cfa425ba3d8dd8f96 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 26 Apr 2021 10:52:19 +0200 Subject: [PATCH 016/193] Put LV2 stereo outputs in a group --- plugins/lv2/sfizz.ttl.in | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/plugins/lv2/sfizz.ttl.in b/plugins/lv2/sfizz.ttl.in index 38868a564..f92b274fc 100644 --- a/plugins/lv2/sfizz.ttl.in +++ b/plugins/lv2/sfizz.ttl.in @@ -23,6 +23,13 @@ midnam:interface a lv2:ExtensionData . midnam:update a lv2:Feature . +<@LV2PLUGIN_URI@#stereo_output> + a pg:StereoGroup, pg:OutputGroup ; + lv2:symbol "stereo_output" ; + lv2:name "Stereo output", + "Sortie stéréo"@fr , + "Uscita stereo"@it . + <@LV2PLUGIN_URI@#config> a pg:Group ; lv2:symbol "config" ; @@ -125,14 +132,18 @@ midnam:update a lv2:Feature . lv2:symbol "out_left" ; lv2:name "Left Output", "Sortie gauche"@fr , - "Uscita Sinistra"@it + "Uscita Sinistra"@it ; + pg:group <@LV2PLUGIN_URI@#stereo_output> ; + lv2:designation pg:left ] , [ a lv2:AudioPort, lv2:OutputPort ; lv2:index 4 ; lv2:symbol "out_right" ; lv2:name "Right Output", "Sortie droite"@fr , - "Uscita Destra"@it + "Uscita Destra"@it ; + pg:group <@LV2PLUGIN_URI@#stereo_output> ; + lv2:designation pg:right ] , [ a lv2:InputPort, lv2:ControlPort ; lv2:index 5 ; From c046c630147792838b252e0ce4b0b3fb1e6f8be1 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 26 Apr 2021 10:53:04 +0200 Subject: [PATCH 017/193] Fix a typo --- plugins/lv2/sfizz.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lv2/sfizz.cpp b/plugins/lv2/sfizz.cpp index ec8e15186..6a001e721 100644 --- a/plugins/lv2/sfizz.cpp +++ b/plugins/lv2/sfizz.cpp @@ -478,7 +478,7 @@ instantiate(const LV2_Descriptor *descriptor, else { lv2_log_warning(&self->logger, - "No option array was given upon instantiation; will use default values\n."); + "No option array was given upon instantiation; will use default values.\n"); } // We need _some_ information on the block size From 9411bd458d28056bf758e57ec9bb35bdcfe3b7c2 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 26 Apr 2021 11:11:21 +0200 Subject: [PATCH 018/193] Specify the LV2 main output --- plugins/lv2/sfizz.ttl.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/lv2/sfizz.ttl.in b/plugins/lv2/sfizz.ttl.in index f92b274fc..83bddf760 100644 --- a/plugins/lv2/sfizz.ttl.in +++ b/plugins/lv2/sfizz.ttl.in @@ -97,6 +97,8 @@ midnam:update a lv2:Feature . patch:writable <@LV2PLUGIN_URI@:sfzfile> , <@LV2PLUGIN_URI@:tuningfile> ; + pg:mainOutput <@LV2PLUGIN_URI@#stereo_output> ; + lv2:port [ a lv2:InputPort, atom:AtomPort ; atom:bufferType atom:Sequence ; From 9952d28b97e26e92817d38c59f08b8b86d51185e Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 14 Apr 2021 06:36:17 +0200 Subject: [PATCH 019/193] Volume meter for plugin editors --- cmake/SfizzConfig.cmake | 5 + plugins/CMakeLists.txt | 3 +- plugins/common/plugin/RMSFollower.h | 73 ++++++ plugins/editor/layout/main.fl | 12 +- plugins/editor/src/editor/EditIds.h | 3 + plugins/editor/src/editor/Editor.cpp | 31 ++- plugins/editor/src/editor/GUIComponents.cpp | 69 ++++++ plugins/editor/src/editor/GUIComponents.h | 35 +++ plugins/editor/src/editor/layout/main.hpp | 232 ++++++++++---------- plugins/lv2/CMakeLists.txt | 6 + plugins/lv2/sfizz.cpp | 43 +++- plugins/lv2/sfizz_lv2.h | 6 + plugins/lv2/sfizz_lv2_common.cpp | 7 + plugins/lv2/sfizz_lv2_plugin.h | 10 + plugins/lv2/sfizz_ui.cpp | 26 +++ plugins/lv2/sfizz_ui.ttl.in | 2 +- plugins/vst/SfizzVstController.cpp | 16 ++ plugins/vst/SfizzVstEditor.cpp | 19 ++ plugins/vst/SfizzVstEditor.h | 5 +- plugins/vst/SfizzVstParameters.h | 9 + plugins/vst/SfizzVstProcessor.cpp | 28 ++- plugins/vst/SfizzVstProcessor.h | 5 + src/CMakeLists.txt | 3 - 23 files changed, 514 insertions(+), 134 deletions(-) create mode 100644 plugins/common/plugin/RMSFollower.h diff --git a/cmake/SfizzConfig.cmake b/cmake/SfizzConfig.cmake index 40f248de1..1cb4b0e6a 100644 --- a/cmake/SfizzConfig.cmake +++ b/cmake/SfizzConfig.cmake @@ -45,6 +45,11 @@ if(WIN32) add_compile_definitions(_WIN32_WINNT=0x601) endif() +# Define the math constants everywhere +if(WIN32) + add_compile_definitions(_USE_MATH_DEFINES) +endif() + # Set macOS compatibility level if(APPLE) set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9") diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index e9c211046..3201b27d7 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(plugins-common STATIC EXCLUDE_FROM_ALL + "common/plugin/RMSFollower.h" "common/plugin/MessageUtils.h" "common/plugin/MessageUtils.cpp" "common/plugin/InstrumentDescription.h" @@ -28,7 +29,7 @@ endif() target_include_directories(plugins-common PUBLIC "common") target_link_libraries(plugins-common PUBLIC sfizz::spin_mutex - PUBLIC sfizz::filesystem absl::strings + PUBLIC sfizz::simde sfizz::filesystem absl::strings PRIVATE sfizz::pugixml PRIVATE sfizz::internal sfizz::sfizz) add_library(sfizz::plugins-common ALIAS plugins-common) diff --git a/plugins/common/plugin/RMSFollower.h b/plugins/common/plugin/RMSFollower.h new file mode 100644 index 000000000..43cc7ac27 --- /dev/null +++ b/plugins/common/plugin/RMSFollower.h @@ -0,0 +1,73 @@ +// 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 +#include + +class RMSFollower { +public: + RMSFollower() + { + updatePole(); + } + + void clear() + { + mem_ = simde_mm_setzero_ps(); + } + + void init(float sampleRate) + { + sampleRate_ = sampleRate; + updatePole(); + clear(); + } + + void setT60(float t60) + { + t60_ = t60; + updatePole(); + } + + void process(const float* leftBlock, const float* rightBlock, size_t numFrames) + { + simde__m128 mem = mem_; + const simde__m128 pole = simde_mm_load1_ps(&pole_); + for (size_t i = 0; i < numFrames; ++i) { + float left = leftBlock[i]; + float right = rightBlock[i]; + simde__m128 input = simde_mm_setr_ps(left, right, 0.0f, 0.0f); + input = simde_mm_mul_ps(input, input); + simde__m128 output = simde_mm_add_ps(input, simde_mm_mul_ps(pole, simde_mm_sub_ps(mem, input))); + mem = output; + } + mem_ = mem; + } + + simde__m128 getMS() const + { + return mem_; + } + + simde__m128 getRMS() const + { + return simde_mm_sqrt_ps(mem_); + } + +private: + void updatePole() + { + pole_ = std::exp(float(-2.0 * M_PI) / (t60_ * sampleRate_)); + } + +private: + simde__m128 mem_ {}; + float pole_ {}; + float t60_ = 300e-3; + float sampleRate_ = 44100; +}; diff --git a/plugins/editor/layout/main.fl b/plugins/editor/layout/main.fl index c5ac97085..2ec8a0e4f 100644 --- a/plugins/editor/layout/main.fl +++ b/plugins/editor/layout/main.fl @@ -134,10 +134,6 @@ widget_class mainView {open xywh {610 70 60 5} labelsize 12 hide class ValueLabel } - Fl_Box {} { - xywh {745 20 35 55} box BORDER_BOX - class VMeter - } Fl_Box volumeCCKnob_ { label Volume comment {tag=kTagSetCCVolume} @@ -150,6 +146,14 @@ widget_class mainView {open xywh {655 10 70 90} box BORDER_BOX labelsize 12 align 17 class KnobCCBox } + Fl_Box leftMeter_ { + xywh {740 10 15 90} box BORDER_BOX + class VMeter + } + Fl_Box rightMeter_ {selected + xywh {760 10 15 90} box BORDER_BOX + class VMeter + } } } Fl_Group {subPanels_[kPanelGeneral]} { diff --git a/plugins/editor/src/editor/EditIds.h b/plugins/editor/src/editor/EditIds.h index 3f55d31b3..c0070ed6c 100644 --- a/plugins/editor/src/editor/EditIds.h +++ b/plugins/editor/src/editor/EditIds.h @@ -38,6 +38,9 @@ enum class EditId : int { CC_RANGE(ControllerDefault), CC_RANGE(ControllerLabel), // + LeftLevel, + RightLevel, + // UINumCurves, UINumMasters, UINumGroups, diff --git a/plugins/editor/src/editor/Editor.cpp b/plugins/editor/src/editor/Editor.cpp index 1e5850aaf..f98d58545 100644 --- a/plugins/editor/src/editor/Editor.cpp +++ b/plugins/editor/src/editor/Editor.cpp @@ -153,6 +153,9 @@ struct Editor::Impl : EditorController::Receiver, SKnobCCBox* volumeCCKnob_ = nullptr; SKnobCCBox* panCCKnob_ = nullptr; + SLevelMeter* leftMeter_ = nullptr; + SLevelMeter* rightMeter_ = nullptr; + SAboutDialog* aboutDialog_ = nullptr; SharedPointer backgroundBitmap_; @@ -504,6 +507,20 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v) updateBackgroundImage(value.c_str()); } break; + case EditId::LeftLevel: + { + const float value = v.to_float(); + if (SLevelMeter* meter = leftMeter_) + meter->setValue(value); + } + break; + case EditId::RightLevel: + { + const float value = v.to_float(); + if (SLevelMeter* meter = rightMeter_) + meter->setValue(value); + } + break; default: if (editIdIsKey(id)) { const int key = keyForEditId(id); @@ -703,11 +720,15 @@ void Editor::Impl::createFrameContents() lbl->setFont(font); return lbl; }; - auto createVMeter = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { - // TODO the volume meter... - CViewContainer* container = new CViewContainer(bounds); - container->setBackgroundColor(CColor(0x00, 0x00, 0x00, 0x00)); - return container; + auto createVMeter = [this, &palette](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { + SLevelMeter* meter = new SLevelMeter(bounds); + meter->setNormalFillColor(CColor(0x00, 0xaa, 0x11)); + meter->setDangerFillColor(CColor(0xaa, 0x00, 0x00)); + OnThemeChanged.push_back([meter, palette]() { + meter->setFrameColor(palette->inactiveText); + meter->setBackColor(palette->knobInactiveTrack); + }); + return meter; }; #if 0 auto createButton = [this](const CRect& bounds, int tag, const char* label, CHoriTxtAlign align, int fontsize) { diff --git a/plugins/editor/src/editor/GUIComponents.cpp b/plugins/editor/src/editor/GUIComponents.cpp index a70f70e2c..76d7f9a07 100644 --- a/plugins/editor/src/editor/GUIComponents.cpp +++ b/plugins/editor/src/editor/GUIComponents.cpp @@ -979,6 +979,75 @@ void SControlsPanel::ControlSlotListener::controlEndEdit(CControl* pControl) panel_->EndEditFunction(pControl->getTag()); } +/// +SLevelMeter::SLevelMeter(const CRect& size) + : CView(size) +{ +} + +void SLevelMeter::setValue(float value) +{ + if (value_ == value) + return; + + value_ = value; + invalid(); +} + +void SLevelMeter::draw(CDrawContext* dc) +{ + float dbValue = 20.0f * std::log10(value_); + float fill = (dbValue - dbMin_) / (dbMax_ - dbMin_); + fill = (fill < 0.0f) ? 0.0f : fill; + fill = (fill > 1.0f) ? 1.0f : fill; + + CRect largeBounds = getViewSize(); + CRect fillBounds = largeBounds; + fillBounds.top = largeBounds.bottom - fill * largeBounds.getHeight(); + + const CColor safeColor = safeFillColor_; + const CColor dangerColor = dangerFillColor_; + + CColor fillColor; + if (safeColor == dangerColor) { + fillColor = safeColor; + } + else { + float thres = dangerThreshold_; + float mix = (fill - thres) / (1.0f - thres); + mix = (mix < 0.0f) ? 0.0f : mix; + + CCoord safeH, safeS, safeV, safeA; + CCoord dangerH, dangerS, dangerV, dangerA; + safeColor.toHSV(safeH, safeS, safeV); + dangerColor.toHSV(dangerH, dangerS, dangerV); + safeA = safeColor.alpha / 255.0; + dangerA = dangerColor.alpha / 255.0; + + CCoord H, S, V, A; + H = safeH + mix * (dangerH - safeH); + S = safeS + mix * (dangerS - safeS); + V = safeV + mix * (dangerV - safeV); + A = safeA + mix * (dangerA - safeA); + + fillColor.fromHSV(H, S, V); + fillColor.alpha = static_cast(A * 255.0); + } + + dc->setDrawMode(kAliasing); + + if (backColor_.alpha > 0) { + dc->setFillColor(backColor_); + dc->drawRect(largeBounds, kDrawFilled); + } + + dc->setFrameColor(frameColor_); + dc->setFillColor(fillColor); + + dc->drawRect(fillBounds, kDrawFilled); + dc->drawRect(largeBounds); +} + /// SPlaceHolder::SPlaceHolder(const CRect& size, const CColor& color) : CView(size), color_(color) diff --git a/plugins/editor/src/editor/GUIComponents.h b/plugins/editor/src/editor/GUIComponents.h index b3cbc0569..99378f2af 100644 --- a/plugins/editor/src/editor/GUIComponents.h +++ b/plugins/editor/src/editor/GUIComponents.h @@ -365,6 +365,41 @@ class SControlsPanel : public CScrollView { SharedPointer relayoutTrigger_; }; +/// +class SLevelMeter : public CView { +public: + explicit SLevelMeter(const CRect& size); + + float getValue() const { return value_; } + void setValue(float value); + + float getDangerThreshold() const { return dangerThreshold_; } + void setDangerThreshold(float thres) { dangerThreshold_ = thres; invalid(); } + + CColor getFrameColor() const { return frameColor_; } + void setFrameColor(CColor color) { frameColor_ = color; invalid(); } + CColor getBackColor() const { return backColor_; } + void setBackColor(CColor color) { backColor_ = color; invalid(); } + + CColor getNormalFillColor() const { return safeFillColor_; } + void setNormalFillColor(CColor color) { safeFillColor_ = color; invalid(); } + CColor getDangerFillColor() const { return dangerFillColor_; } + void setDangerFillColor(CColor color) { dangerFillColor_ = color; invalid(); } + +protected: + void draw(CDrawContext* dc) override; + +private: + float value_ = 0; + float dangerThreshold_ = 0.5; + float dbMin_ = -40; + float dbMax_ = 0; + CColor frameColor_; + CColor safeFillColor_; + CColor dangerFillColor_; + CColor backColor_; +}; + /// class SPlaceHolder : public CView { public: diff --git a/plugins/editor/src/editor/layout/main.hpp b/plugins/editor/src/editor/layout/main.hpp index 7c0ebf314..26536478e 100644 --- a/plugins/editor/src/editor/layout/main.hpp +++ b/plugins/editor/src/editor/layout/main.hpp @@ -72,124 +72,128 @@ view__26->setVisible(false); auto* const view__27 = createValueLabel(CRect(40, 65, 100, 70), -1, "Center", kCenterText, 12); view__25->addView(view__27); view__27->setVisible(false); -auto* const view__28 = createVMeter(CRect(175, 15, 210, 70), -1, "", kCenterText, 14); +auto* const view__28 = createKnobCCBox(CRect(10, 5, 80, 95), kTagSetCCVolume, "Volume", kCenterText, 12); +volumeCCKnob_ = view__28; view__25->addView(view__28); -auto* const view__29 = createKnobCCBox(CRect(10, 5, 80, 95), kTagSetCCVolume, "Volume", kCenterText, 12); -volumeCCKnob_ = view__29; +auto* const view__29 = createKnobCCBox(CRect(85, 5, 155, 95), kTagSetCCPan, "Pan", kCenterText, 12); +panCCKnob_ = view__29; view__25->addView(view__29); -auto* const view__30 = createKnobCCBox(CRect(85, 5, 155, 95), kTagSetCCPan, "Pan", kCenterText, 12); -panCCKnob_ = view__30; +auto* const view__30 = createVMeter(CRect(170, 5, 185, 95), -1, "", kCenterText, 14); +leftMeter_ = view__30; view__25->addView(view__30); +auto* const view__31 = createVMeter(CRect(190, 5, 205, 95), -1, "", kCenterText, 14); +rightMeter_ = view__31; +view__25->addView(view__31); enterPalette(defaultPalette); -auto* const view__31 = createLogicalGroup(CRect(5, 110, 796, 395), -1, "", kCenterText, 14); -subPanels_[kPanelGeneral] = view__31; -view__0->addView(view__31); -view__31->setVisible(false); -auto* const view__32 = createRoundedGroup(CRect(0, 0, 175, 280), -1, "", kCenterText, 14); -view__31->addView(view__32); -auto* const view__33 = createLabel(CRect(15, 10, 75, 35), -1, "Curves:", kLeftText, 14); +auto* const view__32 = createLogicalGroup(CRect(5, 110, 796, 395), -1, "", kCenterText, 14); +subPanels_[kPanelGeneral] = view__32; +view__0->addView(view__32); +view__32->setVisible(false); +auto* const view__33 = createRoundedGroup(CRect(0, 0, 175, 280), -1, "", kCenterText, 14); view__32->addView(view__33); -auto* const view__34 = createLabel(CRect(15, 35, 75, 60), -1, "Masters:", kLeftText, 14); -view__32->addView(view__34); -auto* const view__35 = createLabel(CRect(15, 60, 75, 85), -1, "Groups:", kLeftText, 14); -view__32->addView(view__35); -auto* const view__36 = createLabel(CRect(15, 85, 75, 110), -1, "Regions:", kLeftText, 14); -view__32->addView(view__36); -auto* const view__37 = createLabel(CRect(15, 110, 75, 135), -1, "Samples:", kLeftText, 14); -view__32->addView(view__37); -auto* const view__38 = createLabel(CRect(115, 10, 155, 35), -1, "0", kCenterText, 14); -infoCurvesLabel_ = view__38; -view__32->addView(view__38); -auto* const view__39 = createLabel(CRect(115, 35, 155, 60), -1, "0", kCenterText, 14); -infoMastersLabel_ = view__39; -view__32->addView(view__39); -auto* const view__40 = createLabel(CRect(115, 60, 155, 85), -1, "0", kCenterText, 14); -infoGroupsLabel_ = view__40; -view__32->addView(view__40); -auto* const view__41 = createLabel(CRect(115, 85, 155, 110), -1, "0", kCenterText, 14); -infoRegionsLabel_ = view__41; -view__32->addView(view__41); -auto* const view__42 = createLabel(CRect(115, 110, 155, 135), -1, "0", kCenterText, 14); -infoSamplesLabel_ = view__42; -view__32->addView(view__42); -auto* const view__43 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14); -subPanels_[kPanelControls] = view__43; -view__0->addView(view__43); -view__43->setVisible(false); -auto* const view__44 = createRoundedGroup(CRect(0, 0, 790, 285), -1, "", kCenterText, 14); -view__43->addView(view__44); -auto* const view__45 = createControlsPanel(CRect(0, 0, 790, 285), -1, "", kCenterText, 12); -controlsPanel_ = view__45; +auto* const view__34 = createLabel(CRect(15, 10, 75, 35), -1, "Curves:", kLeftText, 14); +view__33->addView(view__34); +auto* const view__35 = createLabel(CRect(15, 35, 75, 60), -1, "Masters:", kLeftText, 14); +view__33->addView(view__35); +auto* const view__36 = createLabel(CRect(15, 60, 75, 85), -1, "Groups:", kLeftText, 14); +view__33->addView(view__36); +auto* const view__37 = createLabel(CRect(15, 85, 75, 110), -1, "Regions:", kLeftText, 14); +view__33->addView(view__37); +auto* const view__38 = createLabel(CRect(15, 110, 75, 135), -1, "Samples:", kLeftText, 14); +view__33->addView(view__38); +auto* const view__39 = createLabel(CRect(115, 10, 155, 35), -1, "0", kCenterText, 14); +infoCurvesLabel_ = view__39; +view__33->addView(view__39); +auto* const view__40 = createLabel(CRect(115, 35, 155, 60), -1, "0", kCenterText, 14); +infoMastersLabel_ = view__40; +view__33->addView(view__40); +auto* const view__41 = createLabel(CRect(115, 60, 155, 85), -1, "0", kCenterText, 14); +infoGroupsLabel_ = view__41; +view__33->addView(view__41); +auto* const view__42 = createLabel(CRect(115, 85, 155, 110), -1, "0", kCenterText, 14); +infoRegionsLabel_ = view__42; +view__33->addView(view__42); +auto* const view__43 = createLabel(CRect(115, 110, 155, 135), -1, "0", kCenterText, 14); +infoSamplesLabel_ = view__43; +view__33->addView(view__43); +auto* const view__44 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14); +subPanels_[kPanelControls] = view__44; +view__0->addView(view__44); +view__44->setVisible(false); +auto* const view__45 = createRoundedGroup(CRect(0, 0, 790, 285), -1, "", kCenterText, 14); view__44->addView(view__45); -auto* const view__46 = createLogicalGroup(CRect(5, 109, 795, 425), -1, "", kCenterText, 14); -subPanels_[kPanelSettings] = view__46; -view__0->addView(view__46); -auto* const view__47 = createTitleGroup(CRect(300, 26, 495, 126), -1, "Engine", kCenterText, 12); -view__46->addView(view__47); -auto* const view__48 = createValueMenu(CRect(25, 60, 85, 85), kTagSetOversampling, "", kCenterText, 12); -oversamplingSlider_ = view__48; +auto* const view__46 = createControlsPanel(CRect(0, 0, 790, 285), -1, "", kCenterText, 12); +controlsPanel_ = view__46; +view__45->addView(view__46); +auto* const view__47 = createLogicalGroup(CRect(5, 109, 795, 425), -1, "", kCenterText, 14); +subPanels_[kPanelSettings] = view__47; +view__0->addView(view__47); +auto* const view__48 = createTitleGroup(CRect(300, 26, 495, 126), -1, "Engine", kCenterText, 12); view__47->addView(view__48); -auto* const view__49 = createValueLabel(CRect(15, 20, 95, 45), -1, "Oversampling", kCenterText, 12); -view__47->addView(view__49); -auto* const view__50 = createValueLabel(CRect(100, 20, 180, 45), -1, "Preload size", kCenterText, 12); -view__47->addView(view__50); -auto* const view__51 = createValueMenu(CRect(110, 60, 170, 85), kTagSetPreloadSize, "", kCenterText, 12); -preloadSizeSlider_ = view__51; -view__47->addView(view__51); -auto* const view__52 = createTitleGroup(CRect(170, 161, 585, 261), -1, "Tuning", kCenterText, 12); -view__46->addView(view__52); -auto* const view__53 = createValueLabel(CRect(155, 20, 235, 45), -1, "Root key", kCenterText, 12); -view__52->addView(view__53); -auto* const view__54 = createValueMenu(CRect(250, 60, 310, 85), kTagSetTuningFrequency, "", kCenterText, 12); -tuningFrequencySlider_ = view__54; -view__52->addView(view__54); -auto* const view__55 = createValueLabel(CRect(240, 20, 320, 45), -1, "Frequency", kCenterText, 12); -view__52->addView(view__55); -auto* const view__56 = createStyledKnob(CRect(340, 45, 388, 93), kTagSetStretchedTuning, "", kCenterText, 14); -stretchedTuningSlider_ = view__56; -view__52->addView(view__56); -auto* const view__57 = createValueLabel(CRect(325, 20, 405, 45), -1, "Stretch", kCenterText, 12); -view__52->addView(view__57); -auto* const view__58 = createValueLabel(CRect(20, 20, 120, 45), -1, "Scala file", kCenterText, 12); -view__52->addView(view__58); -auto* const view__59 = createValueButton(CRect(20, 60, 120, 85), kTagLoadScalaFile, "DefaultScale", kCenterText, 12); -scalaFileButton_ = view__59; -view__52->addView(view__59); -auto* const view__60 = createValueMenu(CRect(165, 60, 200, 85), kTagSetScalaRootKey, "", kCenterText, 12); -scalaRootKeySlider_ = view__60; -view__52->addView(view__60); -auto* const view__61 = createValueMenu(CRect(200, 60, 230, 85), kTagSetScalaRootKey, "", kCenterText, 12); -scalaRootOctaveSlider_ = view__61; -view__52->addView(view__61); -auto* const view__62 = createResetSomethingButton(CRect(120, 60, 145, 85), kTagResetScalaFile, "", kCenterText, 12); -scalaResetButton_ = view__62; -view__52->addView(view__62); -auto* const view__63 = createTitleGroup(CRect(615, 161, 754, 261), -1, "Files", kCenterText, 12); -userFilesGroup_ = view__63; -view__46->addView(view__63); -auto* const view__64 = createValueLabel(CRect(20, 20, 120, 45), -1, "User SFZ folder", kCenterText, 12); -view__63->addView(view__64); -auto* const view__65 = createValueButton(CRect(20, 60, 120, 85), kTagChooseUserFilesDir, "DefaultPath", kCenterText, 12); -userFilesDirButton_ = view__65; -view__63->addView(view__65); -auto* const view__66 = createTitleGroup(CRect(525, 26, 720, 126), -1, "Quality", kCenterText, 12); -view__46->addView(view__66); -auto* const view__67 = createValueMenu(CRect(15, 60, 95, 85), kTagSetSampleQuality, "", kCenterText, 12); -sampleQualitySlider_ = view__67; -view__66->addView(view__67); -auto* const view__68 = createValueLabel(CRect(15, 20, 95, 45), -1, "Sample", kCenterText, 12); -view__66->addView(view__68); -auto* const view__69 = createValueLabel(CRect(100, 20, 180, 45), -1, "Oscillator", kCenterText, 12); -view__66->addView(view__69); -auto* const view__70 = createValueMenu(CRect(100, 60, 180, 85), kTagSetOscillatorQuality, "", kCenterText, 12); -oscillatorQualitySlider_ = view__70; -view__66->addView(view__70); -auto* const view__71 = createTitleGroup(CRect(35, 161, 140, 261), -1, "Theme", kCenterText, 12); -view__46->addView(view__71); -view__71->setVisible(false); -auto* const view__72 = createOptionMenu(CRect(20, 60, 85, 85), kTagThemeMenu, "", kCenterText, 12); -themeMenu_ = view__72; -view__71->addView(view__72); -auto* const view__73 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12); -piano_ = view__73; -view__0->addView(view__73); +auto* const view__49 = createValueMenu(CRect(25, 60, 85, 85), kTagSetOversampling, "", kCenterText, 12); +oversamplingSlider_ = view__49; +view__48->addView(view__49); +auto* const view__50 = createValueLabel(CRect(15, 20, 95, 45), -1, "Oversampling", kCenterText, 12); +view__48->addView(view__50); +auto* const view__51 = createValueLabel(CRect(100, 20, 180, 45), -1, "Preload size", kCenterText, 12); +view__48->addView(view__51); +auto* const view__52 = createValueMenu(CRect(110, 60, 170, 85), kTagSetPreloadSize, "", kCenterText, 12); +preloadSizeSlider_ = view__52; +view__48->addView(view__52); +auto* const view__53 = createTitleGroup(CRect(170, 161, 585, 261), -1, "Tuning", kCenterText, 12); +view__47->addView(view__53); +auto* const view__54 = createValueLabel(CRect(155, 20, 235, 45), -1, "Root key", kCenterText, 12); +view__53->addView(view__54); +auto* const view__55 = createValueMenu(CRect(250, 60, 310, 85), kTagSetTuningFrequency, "", kCenterText, 12); +tuningFrequencySlider_ = view__55; +view__53->addView(view__55); +auto* const view__56 = createValueLabel(CRect(240, 20, 320, 45), -1, "Frequency", kCenterText, 12); +view__53->addView(view__56); +auto* const view__57 = createStyledKnob(CRect(340, 45, 388, 93), kTagSetStretchedTuning, "", kCenterText, 14); +stretchedTuningSlider_ = view__57; +view__53->addView(view__57); +auto* const view__58 = createValueLabel(CRect(325, 20, 405, 45), -1, "Stretch", kCenterText, 12); +view__53->addView(view__58); +auto* const view__59 = createValueLabel(CRect(20, 20, 120, 45), -1, "Scala file", kCenterText, 12); +view__53->addView(view__59); +auto* const view__60 = createValueButton(CRect(20, 60, 120, 85), kTagLoadScalaFile, "DefaultScale", kCenterText, 12); +scalaFileButton_ = view__60; +view__53->addView(view__60); +auto* const view__61 = createValueMenu(CRect(165, 60, 200, 85), kTagSetScalaRootKey, "", kCenterText, 12); +scalaRootKeySlider_ = view__61; +view__53->addView(view__61); +auto* const view__62 = createValueMenu(CRect(200, 60, 230, 85), kTagSetScalaRootKey, "", kCenterText, 12); +scalaRootOctaveSlider_ = view__62; +view__53->addView(view__62); +auto* const view__63 = createResetSomethingButton(CRect(120, 60, 145, 85), kTagResetScalaFile, "", kCenterText, 12); +scalaResetButton_ = view__63; +view__53->addView(view__63); +auto* const view__64 = createTitleGroup(CRect(615, 161, 754, 261), -1, "Files", kCenterText, 12); +userFilesGroup_ = view__64; +view__47->addView(view__64); +auto* const view__65 = createValueLabel(CRect(20, 20, 120, 45), -1, "User SFZ folder", kCenterText, 12); +view__64->addView(view__65); +auto* const view__66 = createValueButton(CRect(20, 60, 120, 85), kTagChooseUserFilesDir, "DefaultPath", kCenterText, 12); +userFilesDirButton_ = view__66; +view__64->addView(view__66); +auto* const view__67 = createTitleGroup(CRect(525, 26, 720, 126), -1, "Quality", kCenterText, 12); +view__47->addView(view__67); +auto* const view__68 = createValueMenu(CRect(15, 60, 95, 85), kTagSetSampleQuality, "", kCenterText, 12); +sampleQualitySlider_ = view__68; +view__67->addView(view__68); +auto* const view__69 = createValueLabel(CRect(15, 20, 95, 45), -1, "Sample", kCenterText, 12); +view__67->addView(view__69); +auto* const view__70 = createValueLabel(CRect(100, 20, 180, 45), -1, "Oscillator", kCenterText, 12); +view__67->addView(view__70); +auto* const view__71 = createValueMenu(CRect(100, 60, 180, 85), kTagSetOscillatorQuality, "", kCenterText, 12); +oscillatorQualitySlider_ = view__71; +view__67->addView(view__71); +auto* const view__72 = createTitleGroup(CRect(35, 161, 140, 261), -1, "Theme", kCenterText, 12); +view__47->addView(view__72); +view__72->setVisible(false); +auto* const view__73 = createOptionMenu(CRect(20, 60, 85, 85), kTagThemeMenu, "", kCenterText, 12); +themeMenu_ = view__73; +view__72->addView(view__73); +auto* const view__74 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12); +piano_ = view__74; +view__0->addView(view__74); diff --git a/plugins/lv2/CMakeLists.txt b/plugins/lv2/CMakeLists.txt index 828be7384..18305a325 100644 --- a/plugins/lv2/CMakeLists.txt +++ b/plugins/lv2/CMakeLists.txt @@ -61,6 +61,12 @@ if(SFIZZ_LV2_UI) endif() endif() +# Define a preprocessor variable to indicate a build with UI enabled +if(SFIZZ_LV2_UI) + target_compile_definitions(${LV2PLUGIN_PRJ_NAME} PRIVATE "SFIZZ_LV2_UI=1") + target_compile_definitions(${LV2PLUGIN_PRJ_NAME}_ui PRIVATE "SFIZZ_LV2_UI=1") +endif() + # Remove the "lib" prefix, rename the target name and build it in the .lv build dir # /lv2/_lv2. to # /lv2/.lv2/. diff --git a/plugins/lv2/sfizz.cpp b/plugins/lv2/sfizz.cpp index 6a001e721..0c60824a9 100644 --- a/plugins/lv2/sfizz.cpp +++ b/plugins/lv2/sfizz.cpp @@ -121,9 +121,9 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self) self->sfizz_preload_size_uri = map->map(map->handle, SFIZZ__preloadSize); self->sfizz_oversampling_uri = map->map(map->handle, SFIZZ__oversampling); self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus); - self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus); self->sfizz_check_modification_uri = map->map(map->handle, SFIZZ__checkModification); self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob); + self->sfizz_audio_level_uri = map->map(map->handle, SFIZZ__AudioLevel); self->time_position_uri = map->map(map->handle, LV2_TIME__Position); self->time_bar_uri = map->map(map->handle, LV2_TIME__bar); self->time_bar_beat_uri = map->map(map->handle, LV2_TIME__barBeat); @@ -532,6 +532,9 @@ activate(LV2_Handle instance) sfizz_set_samples_per_block(self->synth, self->max_block_size); sfizz_set_sample_rate(self->synth, self->sample_rate); self->must_update_midnam.store(1); +#if defined(SFIZZ_LV2_UI) + self->rms_follower.init(self->sample_rate); +#endif } static void @@ -576,6 +579,30 @@ sfizz_lv2_send_controller(sfizz_plugin_t *self, LV2_Atom_Forge* forge, unsigned lv2_atom_forge_pop(forge, &frame); } +#if defined(SFIZZ_LV2_UI) +static void +sfizz_lv2_send_levels(sfizz_plugin_t *self, LV2_Atom_Forge* forge, float left, float right) +{ + const float levels[] = {left, right}; + uint32_t num_levels = sizeof(levels) / sizeof(levels[0]); + + bool write_ok = lv2_atom_forge_frame_time(forge, 0); + + LV2_Atom_Vector *vector = nullptr; + if (write_ok) { + LV2_Atom_Forge_Ref ref = lv2_atom_forge_vector(forge, sizeof(float), self->atom_float_uri, num_levels, levels); + write_ok = ref; + if (write_ok) + vector = (LV2_Atom_Vector *)lv2_atom_forge_deref(forge, ref); + } + + if (write_ok) + vector->atom.type = self->sfizz_audio_level_uri; + + (void)write_ok; +} +#endif + static void sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, int delay, const LV2_Atom_Object *obj) { @@ -1046,6 +1073,20 @@ run(LV2_Handle instance, uint32_t sample_count) spin_mutex_unlock(self->synth_mutex); +#if defined(SFIZZ_LV2_UI) + if (self->ui_active) + { + self->rms_follower.process(self->output_buffers[0], self->output_buffers[1], sample_count); + const simde__m128 rms = self->rms_follower.getRMS(); + const float *levels = (const float *)&rms; + sfizz_lv2_send_levels(self, &self->forge_notify, levels[0], levels[1]); + } + else + { + self->rms_follower.clear(); + } +#endif + lv2_atom_forge_pop(&self->forge_notify, ¬ify_frame); lv2_atom_forge_pop(&self->forge_automate, &automate_frame); } diff --git a/plugins/lv2/sfizz_lv2.h b/plugins/lv2/sfizz_lv2.h index 51a53896b..dfc3a1165 100644 --- a/plugins/lv2/sfizz_lv2.h +++ b/plugins/lv2/sfizz_lv2.h @@ -44,6 +44,8 @@ #define SFIZZ__checkModification SFIZZ_URI ":" "check_modification" // OSC atoms #define SFIZZ__OSCBlob SFIZZ_URI ":" "OSCBlob" +// Level atoms +#define SFIZZ__AudioLevel SFIZZ_URI ":" "AudioLevel" enum { @@ -89,6 +91,10 @@ bool sfizz_lv2_fetch_description( sfizz_plugin_t *self, const int *serial, uint8_t **descp, uint32_t *sizep, int *serialp); +#if defined(SFIZZ_LV2_UI) +void sfizz_lv2_set_ui_active(sfizz_plugin_t *self, bool ui_active); +#endif + // Mapping URID to CC and vice-versa struct sfizz_lv2_ccmap; sfizz_lv2_ccmap *sfizz_lv2_ccmap_create(LV2_URID_Map* map); diff --git a/plugins/lv2/sfizz_lv2_common.cpp b/plugins/lv2/sfizz_lv2_common.cpp index 7881db1d6..30bf4b63f 100644 --- a/plugins/lv2/sfizz_lv2_common.cpp +++ b/plugins/lv2/sfizz_lv2_common.cpp @@ -31,6 +31,13 @@ bool sfizz_lv2_fetch_description( return true; } +#if defined(SFIZZ_LV2_UI) +void sfizz_lv2_set_ui_active(sfizz_plugin_t *self, bool ui_active) +{ + self->ui_active = ui_active; +} +#endif + struct sfizz_lv2_ccmap { LV2_URID *cc_to_urid; int *urid_to_cc; diff --git a/plugins/lv2/sfizz_lv2_plugin.h b/plugins/lv2/sfizz_lv2_plugin.h index 2eed6d529..d9c4fe434 100644 --- a/plugins/lv2/sfizz_lv2_plugin.h +++ b/plugins/lv2/sfizz_lv2_plugin.h @@ -7,6 +7,9 @@ #pragma once #include "sfizz_lv2.h" +#if defined(SFIZZ_LV2_UI) +#include "plugin/RMSFollower.h" +#endif #include #include #include @@ -89,6 +92,7 @@ struct sfizz_plugin_t LV2_URID sfizz_check_modification_uri {}; LV2_URID sfizz_active_voices_uri {}; LV2_URID sfizz_osc_blob_uri {}; + LV2_URID sfizz_audio_level_uri {}; LV2_URID time_position_uri {}; LV2_URID time_bar_uri {}; LV2_URID time_bar_beat_uri {}; @@ -145,4 +149,10 @@ struct sfizz_plugin_t // OSC uint8_t osc_temp[OSC_TEMP_SIZE] {}; + +#if defined(SFIZZ_LV2_UI) + // UI + volatile bool ui_active = false; + RMSFollower rms_follower; +#endif }; diff --git a/plugins/lv2/sfizz_ui.cpp b/plugins/lv2/sfizz_ui.cpp index 93a90813b..bd9438339 100644 --- a/plugins/lv2/sfizz_ui.cpp +++ b/plugins/lv2/sfizz_ui.cpp @@ -116,6 +116,7 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface { LV2_URID sfizz_sfz_file_uri; LV2_URID sfizz_scala_file_uri; LV2_URID sfizz_osc_blob_uri; + LV2_URID sfizz_audio_level_uri; std::unique_ptr ccmap; uint8_t osc_temp[OSC_TEMP_SIZE]; @@ -217,6 +218,7 @@ instantiate(const LV2UI_Descriptor *descriptor, self->sfizz_sfz_file_uri = map->map(map->handle, SFIZZ__sfzFile); self->sfizz_scala_file_uri = map->map(map->handle, SFIZZ__tuningfile); self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob); + self->sfizz_audio_level_uri = map->map(map->handle, SFIZZ__AudioLevel); self->ccmap.reset(sfizz_lv2_ccmap_create(map)); // set up the resource path @@ -287,6 +289,7 @@ static void cleanup(LV2UI_Handle ui) { sfizz_ui_t *self = (sfizz_ui_t *)ui; + sfizz_lv2_set_ui_active(self->plugin, false); delete self; } @@ -391,6 +394,27 @@ port_event(LV2UI_Handle ui, if (sfizz_extract_message(LV2_ATOM_BODY_CONST(atom), atom->size, buffer, sizeof(buffer), &path, &sig, &args) > 0) self->uiReceiveMessage(path, sig, args); } + else if (atom->type == self->sfizz_audio_level_uri) { + const LV2_Atom_Vector *vector = reinterpret_cast(atom); + + if (vector->body.child_type == self->atom_float_uri && + vector->body.child_size == sizeof(float)) + { + const uint8_t *vec_body = reinterpret_cast( + LV2_ATOM_CONTENTS_CONST(LV2_Atom_Vector, vector)); + const uint8_t *vec_end = reinterpret_cast( + LV2_ATOM_BODY_CONST(&vector->atom)) + vector->atom.size; + + const float *levels = reinterpret_cast(vec_body); + uint32_t count = static_cast((vec_end - vec_body) / sizeof(float)); + + float left = (count > 0) ? levels[0] : 0.0f; + float right = (count > 1) ? levels[1] : 0.0f; + + self->uiReceiveValue(EditId::LeftLevel, left); + self->uiReceiveValue(EditId::RightLevel, right); + } + } } (void)buffer_size; @@ -466,6 +490,8 @@ idle(LV2UI_Handle ui) (void)self; #endif + sfizz_lv2_set_ui_active(self->plugin, self->uiFrame->isVisible()); + return 0; } diff --git a/plugins/lv2/sfizz_ui.ttl.in b/plugins/lv2/sfizz_ui.ttl.in index 06ee2e319..a0150653b 100644 --- a/plugins/lv2/sfizz_ui.ttl.in +++ b/plugins/lv2/sfizz_ui.ttl.in @@ -12,4 +12,4 @@ lv2:optionalFeature ui:touch ; lv2:requiredFeature urid:map ; lv2:requiredFeature urid:unmap ; - lv2:requiredFeature . + lv2:requiredFeature . diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index 4e92f8b43..17c3098ba 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -92,6 +92,22 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) Vst::kRootUnitId, shortTitle)); } + // Volume levels + parameters.addParameter( + SfizzRange::getForParameter(kPidLeftLevel).createParameter( + Steinberg::String("Left level"), pid++, nullptr, + 0, Vst::ParameterInfo::kIsReadOnly|Vst::ParameterInfo::kIsHidden, Vst::kRootUnitId)); + parameters.addParameter( + SfizzRange::getForParameter(kPidRightLevel).createParameter( + Steinberg::String("Right level"), pid++, nullptr, + 0, Vst::ParameterInfo::kIsReadOnly|Vst::ParameterInfo::kIsHidden, Vst::kRootUnitId)); + + // Editor status + parameters.addParameter( + SfizzRange::getForParameter(kPidEditorOpen).createParameter( + Steinberg::String("Editor open"), pid++, nullptr, + 0, Vst::ParameterInfo::kIsReadOnly|Vst::ParameterInfo::kIsHidden, Vst::kRootUnitId)); + // Initial MIDI mapping for (int32 i = 0; i < Vst::kCountCtrlNumber; ++i) { Vst::ParamID id = Vst::kNoParamId; diff --git a/plugins/vst/SfizzVstEditor.cpp b/plugins/vst/SfizzVstEditor.cpp index 8a95afa74..6c85bfd36 100644 --- a/plugins/vst/SfizzVstEditor.cpp +++ b/plugins/vst/SfizzVstEditor.cpp @@ -122,6 +122,8 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p uiReceiveValue(EditId::UserFilesDir, userFilesDir.value_or(fs::path()).u8string()); uiReceiveValue(EditId::FallbackFilesDir, SfizzPaths::getSfzFallbackDefaultPath().u8string()); + updateEditorIsOpenParameter(); + return true; } @@ -153,6 +155,16 @@ void PLUGIN_API SfizzVstEditor::close() std::lock_guard lock(noteEventQueueMutex_); noteEventQueue_.reset(); } + + updateEditorIsOpenParameter(); +} + +void SfizzVstEditor::updateEditorIsOpenParameter() +{ + SfizzVstController* ctrl = getController(); + bool editorIsOpen = frame && frame->isVisible(); + ctrl->setParamNormalized(kPidEditorOpen, editorIsOpen); + ctrl->performEdit(kPidEditorOpen, editorIsOpen); } /// @@ -180,6 +192,7 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message) if (message == CVSTGUITimer::kMsgTimer) { processOscQueue(); processNoteEventQueue(); + updateEditorIsOpenParameter(); // Note(jpc) for Reaper, it can fail at open time } return result; @@ -296,6 +309,12 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message) case kPidOscillatorQuality: uiReceiveValue(EditId::OscillatorQuality, range.denormalize(value)); break; + case kPidLeftLevel: + uiReceiveValue(EditId::LeftLevel, range.denormalize(value)); + break; + case kPidRightLevel: + uiReceiveValue(EditId::RightLevel, range.denormalize(value)); + break; default: if (id >= kPidCC0 && id <= kPidCCLast) { int cc = int(id - kPidCC0); diff --git a/plugins/vst/SfizzVstEditor.h b/plugins/vst/SfizzVstEditor.h index 780e2b2fd..d254dbd83 100644 --- a/plugins/vst/SfizzVstEditor.h +++ b/plugins/vst/SfizzVstEditor.h @@ -37,15 +37,14 @@ class SfizzVstEditor : public Vst::VSTGUIEditor, return static_cast(Vst::VSTGUIEditor::getController()); } + void updateEditorIsOpenParameter(); + // VSTGUIEditor CMessageResult notify(CBaseObject* sender, const char* message) override; // FObject void PLUGIN_API update(FUnknown* changedUnknown, int32 message) override; // - void updateState(const SfizzVstState& state); - void updatePlayState(const SfizzPlayState& playState); - private: void processOscQueue(); void processNoteEventQueue(); diff --git a/plugins/vst/SfizzVstParameters.h b/plugins/vst/SfizzVstParameters.h index f1791653a..cfe1c4737 100644 --- a/plugins/vst/SfizzVstParameters.h +++ b/plugins/vst/SfizzVstParameters.h @@ -26,6 +26,9 @@ enum { kPidPitchBend, kPidCC0, kPidCCLast = kPidCC0 + sfz::config::numCCs - 1, + kPidLeftLevel, + kPidRightLevel, + kPidEditorOpen, /* Reserved */ kNumParameters, }; @@ -78,6 +81,12 @@ struct SfizzRange { return {0.0, 0.0, 1.0}; case kPidPitchBend: return {0.0, -1.0, 1.0}; + case kPidLeftLevel: + return {0.0, 0.0, 1.0}; + case kPidRightLevel: + return {0.0, 0.0, 1.0}; + case kPidEditorOpen: + return {0.0, 0.0, 1.0}; default: if (id >= kPidCC0 && id <= kPidCCLast) return {0.0, 0.0, 1.0}; diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp index 7ae97bc3e..5dca65d14 100644 --- a/plugins/vst/SfizzVstProcessor.cpp +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -37,7 +37,7 @@ static const char* kMsgIdSetPreloadSize = "SetPreloadSize"; static const char* kMsgIdReceiveMessage = "ReceiveMessage"; static const char* kMsgIdNoteEvents = "NoteEvents"; -static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 50 }; +static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 20 }; SfizzVstProcessor::SfizzVstProcessor() : _oscTemp(new uint8_t[kOscTempSize]), @@ -106,6 +106,8 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context) _noteEventsCurrentCycle.fill(-1.0f); + _editorIsOpen = false; + return result; } @@ -221,6 +223,7 @@ tresult PLUGIN_API SfizzVstProcessor::setActive(TBool state) if (state) { synth->setSampleRate(processSetup.sampleRate); synth->setSamplesPerBlock(processSetup.maxSamplesPerBlock); + _rmsFollower.init(processSetup.sampleRate); initializeEventProcessor(processSetup, kNumParameters); startBackgroundWork(); } else { @@ -286,6 +289,24 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data) synth.renderBlock(outputs, numFrames, numChannels); + // Update levels, if editor is open, otherwise skip + RMSFollower& rmsFollower = _rmsFollower; + if (_editorIsOpen) { + rmsFollower.process(outputs[0], outputs[1], numFrames); + simde__m128 rms = _rmsFollower.getRMS(); + float left = reinterpret_cast(&rms)[0]; + float right = reinterpret_cast(&rms)[1]; + if (Vst::IParameterChanges* pcs = data.outputParameterChanges) { + int32 index; + if (Vst::IParamValueQueue* vq = pcs->addParameterData(kPidLeftLevel, index)) + vq->addPoint(0, left, index); + if (Vst::IParamValueQueue* vq = pcs->addParameterData(kPidRightLevel, index)) + vq->addPoint(0, right, index); + } + } + else + rmsFollower.clear(); + // Request OSC updates sfz::Client& client = *_client; synth.sendMessage(client, 0, "/sw/last/current", "", nullptr); @@ -388,6 +409,9 @@ void SfizzVstProcessor::playOrderedParameter(int32 sampleOffset, Vst::ParamID id case kPidPitchBend: synth.hdPitchWheel(sampleOffset, range.denormalize(value)); break; + case kPidEditorOpen: + _editorIsOpen = value != 0; + break; default: if (id >= kPidCC0 && id <= kPidCCLast) { int32 ccNumber = static_cast(id - kPidCC0); @@ -713,7 +737,7 @@ void SfizzVstProcessor::doBackgroundIdle(size_t idleCounter) sendMessage(notification); } - if (idleCounter % 10 == 0) { + if (idleCounter % 25 == 0) { if (_synth->shouldReloadFile()) { fprintf(stderr, "[Sfizz] sfz file has changed, reloading\n"); std::lock_guard lock(_processMutex); diff --git a/plugins/vst/SfizzVstProcessor.h b/plugins/vst/SfizzVstProcessor.h index e686b86ef..1ab35a9d3 100644 --- a/plugins/vst/SfizzVstProcessor.h +++ b/plugins/vst/SfizzVstProcessor.h @@ -7,6 +7,7 @@ #pragma once #include "SfizzVstState.h" #include "OrderedEventProcessor.h" +#include "plugin/RMSFollower.h" #include "sfizz/RTSemaphore.h" #include "ring_buffer/ring_buffer.h" #include "public.sdk/source/vst/vstaudioeffect.h" @@ -63,6 +64,10 @@ class SfizzVstProcessor : public Vst::AudioEffect, // whether allowed to perform events (owns the processing lock) bool _canPerformEventsAndParameters {}; + // level meters + RMSFollower _rmsFollower; + bool _editorIsOpen = false; + // client sfz::ClientPtr _client; std::unique_ptr _oscTemp; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1d4d1dff7..ea627f7db 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -296,9 +296,6 @@ if(SFIZZ_USE_SNDFILE) target_compile_definitions(sfizz_internal PUBLIC "SFIZZ_USE_SNDFILE=1") target_link_libraries(sfizz_internal PUBLIC st_audiofile) endif() -if(WIN32) - target_compile_definitions(sfizz_internal PRIVATE _USE_MATH_DEFINES) -endif() if(SFIZZ_RELEASE_ASSERTS) target_compile_definitions(sfizz_internal PUBLIC "SFIZZ_ENABLE_RELEASE_ASSERT=1") endif() From 4f416f9970a62a83851344d5ed921b367cd647e7 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 26 Apr 2021 16:04:11 +0200 Subject: [PATCH 020/193] Add note regarding Reaper with VST [ci skip] --- plugins/vst/SfizzVstController.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index 17c3098ba..cdd9f0b3d 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -324,6 +324,7 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) pos += sizeof(uint32); float value = *reinterpret_cast(pos); pos += sizeof(float); + #pragma message("setParam() on non-UI thread is dangerous on Reaper, make it deferred instead") setParam(pid, value); } } From 2f863bc458a9a3ffb5e4ca5680d7849bd5ffdcdb Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 27 Apr 2021 10:15:55 +0200 Subject: [PATCH 021/193] UI: Dark theme --- plugins/editor/CMakeLists.txt | 1 + plugins/editor/layout/main.fl | 2 +- .../editor/resources/Themes/Dark/theme.xml | 40 +++++++++++++++++++ plugins/editor/src/editor/layout/main.hpp | 1 - 4 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 plugins/editor/resources/Themes/Dark/theme.xml diff --git a/plugins/editor/CMakeLists.txt b/plugins/editor/CMakeLists.txt index 0b6ee3bd7..a87d58bfa 100644 --- a/plugins/editor/CMakeLists.txt +++ b/plugins/editor/CMakeLists.txt @@ -21,6 +21,7 @@ set(EDITOR_RESOURCES Fonts/sfizz-misc-icons.ttf Fonts/Roboto-Regular.ttf Themes/Default/theme.xml + Themes/Dark/theme.xml PARENT_SCOPE) function(copy_editor_resources TARGET SOURCE_DIR DESTINATION_DIR) diff --git a/plugins/editor/layout/main.fl b/plugins/editor/layout/main.fl index 2ec8a0e4f..2f94bbef1 100644 --- a/plugins/editor/layout/main.fl +++ b/plugins/editor/layout/main.fl @@ -362,7 +362,7 @@ widget_class mainView {open } Fl_Group {} { label Theme open - xywh {40 270 105 100} box ROUNDED_BOX labelsize 12 align 17 hide + xywh {40 270 105 100} box ROUNDED_BOX labelsize 12 align 17 class TitleGroup } { Fl_Spinner themeMenu_ { diff --git a/plugins/editor/resources/Themes/Dark/theme.xml b/plugins/editor/resources/Themes/Dark/theme.xml new file mode 100644 index 000000000..b5e9152f6 --- /dev/null +++ b/plugins/editor/resources/Themes/Dark/theme.xml @@ -0,0 +1,40 @@ + + + #121212 + + #1d1d1d + #e3e3e3 + #a2a2a2 + #e9e9e9 + #e3e3e3 + #2d2d2d + #e3e3e3 + #121212 + #e3e3e3 + #ff6000 + #006b0b + #6f6f6f + #ffffff + #ffffff + #ffffff + #006b0b + + + #2d2d2d + #9e9e9e + #a2a2a2 + #e9e9e9 + #9e9e9e + #2d2d2d + #9e9e9e + #121212 + #9e9e9e + #ff6000 + #006b0b + #6f6f6f + #ffffff + #ffffff + #ffffff + #006b0b + + diff --git a/plugins/editor/src/editor/layout/main.hpp b/plugins/editor/src/editor/layout/main.hpp index 26536478e..4c0f99103 100644 --- a/plugins/editor/src/editor/layout/main.hpp +++ b/plugins/editor/src/editor/layout/main.hpp @@ -190,7 +190,6 @@ oscillatorQualitySlider_ = view__71; view__67->addView(view__71); auto* const view__72 = createTitleGroup(CRect(35, 161, 140, 261), -1, "Theme", kCenterText, 12); view__47->addView(view__72); -view__72->setVisible(false); auto* const view__73 = createOptionMenu(CRect(20, 60, 85, 85), kTagThemeMenu, "", kCenterText, 12); themeMenu_ = view__73; view__72->addView(view__73); From 1d76cc23291b1e2fa4f23ca4577692cca7745be8 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 27 Apr 2021 10:33:30 +0200 Subject: [PATCH 022/193] Make the pragma message NOLINT --- plugins/vst/SfizzVstController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index cdd9f0b3d..dc2501624 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -324,7 +324,7 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) pos += sizeof(uint32); float value = *reinterpret_cast(pos); pos += sizeof(float); - #pragma message("setParam() on non-UI thread is dangerous on Reaper, make it deferred instead") + #pragma message("setParam() on non-UI thread is dangerous on Reaper, make it deferred instead") //NOLINT setParam(pid, value); } } From b07699efdd3e4a951b845d6f428f3967f28ba914 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 27 Apr 2021 14:15:36 +0200 Subject: [PATCH 023/193] Style update for the volume meter --- plugins/editor/layout/main.fl | 6 ++--- plugins/editor/src/editor/Editor.cpp | 2 +- plugins/editor/src/editor/GUIComponents.cpp | 28 ++++++++++++++++++--- plugins/editor/src/editor/GUIComponents.h | 4 +++ plugins/editor/src/editor/layout/main.hpp | 4 +-- 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/plugins/editor/layout/main.fl b/plugins/editor/layout/main.fl index 2f94bbef1..9baf34ec6 100644 --- a/plugins/editor/layout/main.fl +++ b/plugins/editor/layout/main.fl @@ -20,7 +20,7 @@ widget_class mainView {open class RoundedGroup } { Fl_Box {} { - comment {tag=kTagAbout} selected + comment {tag=kTagAbout} image {../resources/logo_text_shaded.png} xywh {32 9 120 60} class AboutButton } @@ -147,11 +147,11 @@ widget_class mainView {open class KnobCCBox } Fl_Box leftMeter_ { - xywh {740 10 15 90} box BORDER_BOX + xywh {740 15 15 85} box BORDER_BOX class VMeter } Fl_Box rightMeter_ {selected - xywh {760 10 15 90} box BORDER_BOX + xywh {760 15 15 85} box BORDER_BOX class VMeter } } diff --git a/plugins/editor/src/editor/Editor.cpp b/plugins/editor/src/editor/Editor.cpp index f98d58545..6c3d72e37 100644 --- a/plugins/editor/src/editor/Editor.cpp +++ b/plugins/editor/src/editor/Editor.cpp @@ -722,10 +722,10 @@ void Editor::Impl::createFrameContents() }; auto createVMeter = [this, &palette](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { SLevelMeter* meter = new SLevelMeter(bounds); + meter->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00)); meter->setNormalFillColor(CColor(0x00, 0xaa, 0x11)); meter->setDangerFillColor(CColor(0xaa, 0x00, 0x00)); OnThemeChanged.push_back([meter, palette]() { - meter->setFrameColor(palette->inactiveText); meter->setBackColor(palette->knobInactiveTrack); }); return meter; diff --git a/plugins/editor/src/editor/GUIComponents.cpp b/plugins/editor/src/editor/GUIComponents.cpp index 76d7f9a07..5af200314 100644 --- a/plugins/editor/src/editor/GUIComponents.cpp +++ b/plugins/editor/src/editor/GUIComponents.cpp @@ -1036,16 +1036,38 @@ void SLevelMeter::draw(CDrawContext* dc) dc->setDrawMode(kAliasing); + CCoord radius = radius_; + bool isRounded = radius > 0.0; + + SharedPointer largeRoundRect; + SharedPointer fillRoundRect; + + if (isRounded) { + largeRoundRect = owned(dc->createRoundRectGraphicsPath(largeBounds, radius)); + fillRoundRect = owned(dc->createRoundRectGraphicsPath(fillBounds, radius)); + } + if (backColor_.alpha > 0) { dc->setFillColor(backColor_); - dc->drawRect(largeBounds, kDrawFilled); + if (!isRounded) + dc->drawRect(largeBounds, kDrawFilled); + else + dc->drawGraphicsPath(largeRoundRect, CDrawContext::kPathFilled); } dc->setFrameColor(frameColor_); dc->setFillColor(fillColor); - dc->drawRect(fillBounds, kDrawFilled); - dc->drawRect(largeBounds); + if (!isRounded) { + if (fill > 0) + dc->drawRect(fillBounds, kDrawFilled); + dc->drawRect(largeBounds); + } + else { + if (fill > 0 && fillBounds.getHeight() >= radius) + dc->drawGraphicsPath(fillRoundRect, CDrawContext::kPathFilled); + dc->drawGraphicsPath(largeRoundRect, CDrawContext::kPathStroked); + } } /// diff --git a/plugins/editor/src/editor/GUIComponents.h b/plugins/editor/src/editor/GUIComponents.h index 99378f2af..dfbd0be44 100644 --- a/plugins/editor/src/editor/GUIComponents.h +++ b/plugins/editor/src/editor/GUIComponents.h @@ -386,6 +386,9 @@ class SLevelMeter : public CView { CColor getDangerFillColor() const { return dangerFillColor_; } void setDangerFillColor(CColor color) { dangerFillColor_ = color; invalid(); } + CCoord getRoundRectRadius() const { return radius_; } + void setRoundRectRadius(CCoord radius) { radius_ = radius; invalid(); } + protected: void draw(CDrawContext* dc) override; @@ -398,6 +401,7 @@ class SLevelMeter : public CView { CColor safeFillColor_; CColor dangerFillColor_; CColor backColor_; + CCoord radius_ = 5.0; }; /// diff --git a/plugins/editor/src/editor/layout/main.hpp b/plugins/editor/src/editor/layout/main.hpp index 4c0f99103..936db5da1 100644 --- a/plugins/editor/src/editor/layout/main.hpp +++ b/plugins/editor/src/editor/layout/main.hpp @@ -78,10 +78,10 @@ view__25->addView(view__28); auto* const view__29 = createKnobCCBox(CRect(85, 5, 155, 95), kTagSetCCPan, "Pan", kCenterText, 12); panCCKnob_ = view__29; view__25->addView(view__29); -auto* const view__30 = createVMeter(CRect(170, 5, 185, 95), -1, "", kCenterText, 14); +auto* const view__30 = createVMeter(CRect(170, 10, 185, 95), -1, "", kCenterText, 14); leftMeter_ = view__30; view__25->addView(view__30); -auto* const view__31 = createVMeter(CRect(190, 5, 205, 95), -1, "", kCenterText, 14); +auto* const view__31 = createVMeter(CRect(190, 10, 205, 95), -1, "", kCenterText, 14); rightMeter_ = view__31; view__25->addView(view__31); enterPalette(defaultPalette); From 8b38a7633b977ba434e850f1faf7e328c03c3a89 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 27 Apr 2021 14:23:14 +0200 Subject: [PATCH 024/193] Anti-aliasing for the rounded volume meter --- plugins/editor/src/editor/GUIComponents.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/editor/src/editor/GUIComponents.cpp b/plugins/editor/src/editor/GUIComponents.cpp index 5af200314..88e486092 100644 --- a/plugins/editor/src/editor/GUIComponents.cpp +++ b/plugins/editor/src/editor/GUIComponents.cpp @@ -1034,11 +1034,11 @@ void SLevelMeter::draw(CDrawContext* dc) fillColor.alpha = static_cast(A * 255.0); } - dc->setDrawMode(kAliasing); - CCoord radius = radius_; bool isRounded = radius > 0.0; + dc->setDrawMode(isRounded ? kAntiAliasing : kAliasing); + SharedPointer largeRoundRect; SharedPointer fillRoundRect; From 4df1d7a2d78641bc87e9d22d024952303e18c953 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 27 Apr 2021 14:35:10 +0200 Subject: [PATCH 025/193] Update layout around the theme selector --- plugins/editor/layout/main.fl | 9 +++++++-- plugins/editor/src/editor/layout/main.hpp | 10 ++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/plugins/editor/layout/main.fl b/plugins/editor/layout/main.fl index 9baf34ec6..dd9081c49 100644 --- a/plugins/editor/layout/main.fl +++ b/plugins/editor/layout/main.fl @@ -150,7 +150,7 @@ widget_class mainView {open xywh {740 15 15 85} box BORDER_BOX class VMeter } - Fl_Box rightMeter_ {selected + Fl_Box rightMeter_ { xywh {760 15 15 85} box BORDER_BOX class VMeter } @@ -361,7 +361,7 @@ widget_class mainView {open } } Fl_Group {} { - label Theme open + label Appearance open selected xywh {40 270 105 100} box ROUNDED_BOX labelsize 12 align 17 class TitleGroup } { @@ -370,6 +370,11 @@ widget_class mainView {open xywh {60 330 65 25} labelsize 12 textsize 12 class OptionMenu } + Fl_Box {} { + label Theme + xywh {50 295 80 25} labelsize 12 + class ValueLabel + } } } Fl_Box piano_ { diff --git a/plugins/editor/src/editor/layout/main.hpp b/plugins/editor/src/editor/layout/main.hpp index 936db5da1..7d49bb7c3 100644 --- a/plugins/editor/src/editor/layout/main.hpp +++ b/plugins/editor/src/editor/layout/main.hpp @@ -188,11 +188,13 @@ view__67->addView(view__70); auto* const view__71 = createValueMenu(CRect(100, 60, 180, 85), kTagSetOscillatorQuality, "", kCenterText, 12); oscillatorQualitySlider_ = view__71; view__67->addView(view__71); -auto* const view__72 = createTitleGroup(CRect(35, 161, 140, 261), -1, "Theme", kCenterText, 12); +auto* const view__72 = createTitleGroup(CRect(35, 161, 140, 261), -1, "Appearance", kCenterText, 12); view__47->addView(view__72); auto* const view__73 = createOptionMenu(CRect(20, 60, 85, 85), kTagThemeMenu, "", kCenterText, 12); themeMenu_ = view__73; view__72->addView(view__73); -auto* const view__74 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12); -piano_ = view__74; -view__0->addView(view__74); +auto* const view__74 = createValueLabel(CRect(10, 25, 90, 50), -1, "Theme", kCenterText, 12); +view__72->addView(view__74); +auto* const view__75 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12); +piano_ = view__75; +view__0->addView(view__75); From 7ca9c15a28bc7559a5b6f6e179e8f27ba4a737d5 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 27 Apr 2021 14:46:27 +0200 Subject: [PATCH 026/193] Update layout --- plugins/editor/layout/main.fl | 42 +++++++++++------------ plugins/editor/src/editor/layout/main.hpp | 36 +++++++++---------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/plugins/editor/layout/main.fl b/plugins/editor/layout/main.fl index dd9081c49..394d9334b 100644 --- a/plugins/editor/layout/main.fl +++ b/plugins/editor/layout/main.fl @@ -41,27 +41,27 @@ widget_class mainView {open } } Fl_Group {} {open - xywh {185 5 380 100} box ROUNDED_BOX + xywh {185 5 395 100} box ROUNDED_BOX class RoundedGroup } { Fl_Box {} { label {Separator 1} - xywh {195 41 360 5} box BORDER_BOX labeltype NO_LABEL + xywh {195 41 375 5} box BORDER_BOX labeltype NO_LABEL class HLine } Fl_Box {} { label {Separator 2} - xywh {195 73 360 5} box BORDER_BOX labeltype NO_LABEL + xywh {195 73 375 5} box BORDER_BOX labeltype NO_LABEL class HLine } Fl_Box sfzFileLabel_ { label {DefaultInstrument.sfz} comment {tag=kTagLoadSfzFile} - xywh {195 13 250 30} labelsize 20 align 20 + xywh {195 13 295 30} labelsize 20 align 20 class ClickableLabel } Fl_Box keyswitchLabel_ { - xywh {265 45 290 30} labelsize 20 align 20 + xywh {265 45 305 30} labelsize 20 align 20 class Label } Fl_Box keyswitchBadge_ { @@ -80,17 +80,17 @@ widget_class mainView {open } Fl_Button {} { comment {tag=kTagPreviousSfzFile} - xywh {480 18 25 25} labelsize 24 + xywh {495 18 25 25} labelsize 24 class PreviousFileButton } Fl_Button {} { comment {tag=kTagNextSfzFile} - xywh {505 18 25 25} labelsize 24 + xywh {520 18 25 25} labelsize 24 class NextFileButton } Fl_Button fileOperationsMenu_ { comment {tag=kTagFileOperations} - xywh {530 18 25 25} labelsize 24 + xywh {545 18 25 25} labelsize 24 class ChevronDropDown } Fl_Box infoVoicesLabel_ { @@ -99,30 +99,30 @@ widget_class mainView {open } Fl_Box {} { label {Max:} - xywh {315 78 40 25} labelsize 12 align 24 + xywh {325 78 40 25} labelsize 12 align 24 class Label } Fl_Box numVoicesLabel_ { - xywh {360 78 35 25} labelsize 12 align 16 + xywh {370 78 35 25} labelsize 12 align 16 class Label } Fl_Box {} { - label {Memory:} - xywh {425 78 60 25} labelsize 12 align 24 + label {Memory:} selected + xywh {440 78 60 25} labelsize 12 align 24 class Label } - Fl_Box memoryLabel_ { - xywh {490 78 60 25} labelsize 12 align 16 + Fl_Box memoryLabel_ {selected + xywh {505 78 60 25} labelsize 12 align 16 class Label } Fl_Button numVoicesSlider_ { comment {tag=kTagSetNumVoices} - xywh {395 82 20 20} labelsize 16 + xywh {405 82 20 20} labelsize 16 class ChevronValueDropDown } } Fl_Group {} {open - xywh {570 5 225 100} box ROUNDED_BOX + xywh {585 5 210 100} box ROUNDED_BOX class RoundedGroup } { Fl_Dial {} { @@ -137,21 +137,21 @@ widget_class mainView {open Fl_Box volumeCCKnob_ { label Volume comment {tag=kTagSetCCVolume} - xywh {580 10 70 90} box BORDER_BOX labelsize 12 align 17 + xywh {595 10 70 90} box BORDER_BOX labelsize 12 align 17 class KnobCCBox } Fl_Box panCCKnob_ { label Pan comment {tag=kTagSetCCPan} - xywh {655 10 70 90} box BORDER_BOX labelsize 12 align 17 + xywh {670 10 70 90} box BORDER_BOX labelsize 12 align 17 class KnobCCBox } Fl_Box leftMeter_ { - xywh {740 15 15 85} box BORDER_BOX + xywh {750 15 15 85} box BORDER_BOX class VMeter } Fl_Box rightMeter_ { - xywh {760 15 15 85} box BORDER_BOX + xywh {770 15 15 85} box BORDER_BOX class VMeter } } @@ -361,7 +361,7 @@ widget_class mainView {open } } Fl_Group {} { - label Appearance open selected + label Appearance open xywh {40 270 105 100} box ROUNDED_BOX labelsize 12 align 17 class TitleGroup } { diff --git a/plugins/editor/src/editor/layout/main.hpp b/plugins/editor/src/editor/layout/main.hpp index 7d49bb7c3..6e296644e 100644 --- a/plugins/editor/src/editor/layout/main.hpp +++ b/plugins/editor/src/editor/layout/main.hpp @@ -20,16 +20,16 @@ view__3->addView(view__6); auto* const view__7 = createSettingsButton(CRect(111, 69, 143, 101), kTagFirstChangePanel+kPanelSettings, "", kCenterText, 30); panelButtons_[kPanelSettings] = view__7; view__3->addView(view__7); -auto* const view__8 = createRoundedGroup(CRect(185, 5, 565, 105), -1, "", kCenterText, 14); +auto* const view__8 = createRoundedGroup(CRect(185, 5, 580, 105), -1, "", kCenterText, 14); view__2->addView(view__8); -auto* const view__9 = createHLine(CRect(10, 36, 370, 41), -1, "", kCenterText, 14); +auto* const view__9 = createHLine(CRect(10, 36, 385, 41), -1, "", kCenterText, 14); view__8->addView(view__9); -auto* const view__10 = createHLine(CRect(10, 68, 370, 73), -1, "", kCenterText, 14); +auto* const view__10 = createHLine(CRect(10, 68, 385, 73), -1, "", kCenterText, 14); view__8->addView(view__10); -auto* const view__11 = createClickableLabel(CRect(10, 8, 260, 38), kTagLoadSfzFile, "DefaultInstrument.sfz", kLeftText, 20); +auto* const view__11 = createClickableLabel(CRect(10, 8, 305, 38), kTagLoadSfzFile, "DefaultInstrument.sfz", kLeftText, 20); sfzFileLabel_ = view__11; view__8->addView(view__11); -auto* const view__12 = createLabel(CRect(80, 40, 370, 70), -1, "", kLeftText, 20); +auto* const view__12 = createLabel(CRect(80, 40, 385, 70), -1, "", kLeftText, 20); keyswitchLabel_ = view__12; view__8->addView(view__12); auto* const view__13 = createBadge(CRect(10, 42, 70, 68), -1, "", kCenterText, 20); @@ -41,35 +41,35 @@ view__8->addView(view__14); view__14->setVisible(false); auto* const view__15 = createLabel(CRect(10, 73, 70, 98), -1, "Voices:", kRightText, 12); view__8->addView(view__15); -auto* const view__16 = createPreviousFileButton(CRect(295, 13, 320, 38), kTagPreviousSfzFile, "", kCenterText, 24); +auto* const view__16 = createPreviousFileButton(CRect(310, 13, 335, 38), kTagPreviousSfzFile, "", kCenterText, 24); view__8->addView(view__16); -auto* const view__17 = createNextFileButton(CRect(320, 13, 345, 38), kTagNextSfzFile, "", kCenterText, 24); +auto* const view__17 = createNextFileButton(CRect(335, 13, 360, 38), kTagNextSfzFile, "", kCenterText, 24); view__8->addView(view__17); -auto* const view__18 = createChevronDropDown(CRect(345, 13, 370, 38), kTagFileOperations, "", kCenterText, 24); +auto* const view__18 = createChevronDropDown(CRect(360, 13, 385, 38), kTagFileOperations, "", kCenterText, 24); fileOperationsMenu_ = view__18; view__8->addView(view__18); auto* const view__19 = createLabel(CRect(75, 73, 115, 98), -1, "", kCenterText, 12); infoVoicesLabel_ = view__19; view__8->addView(view__19); -auto* const view__20 = createLabel(CRect(130, 73, 170, 98), -1, "Max:", kRightText, 12); +auto* const view__20 = createLabel(CRect(140, 73, 180, 98), -1, "Max:", kRightText, 12); view__8->addView(view__20); -auto* const view__21 = createLabel(CRect(175, 73, 210, 98), -1, "", kCenterText, 12); +auto* const view__21 = createLabel(CRect(185, 73, 220, 98), -1, "", kCenterText, 12); numVoicesLabel_ = view__21; view__8->addView(view__21); -auto* const view__22 = createLabel(CRect(240, 73, 300, 98), -1, "Memory:", kRightText, 12); +auto* const view__22 = createLabel(CRect(255, 73, 315, 98), -1, "Memory:", kRightText, 12); view__8->addView(view__22); -auto* const view__23 = createLabel(CRect(305, 73, 365, 98), -1, "", kCenterText, 12); +auto* const view__23 = createLabel(CRect(320, 73, 380, 98), -1, "", kCenterText, 12); memoryLabel_ = view__23; view__8->addView(view__23); -auto* const view__24 = createChevronValueDropDown(CRect(210, 77, 230, 97), kTagSetNumVoices, "", kCenterText, 16); +auto* const view__24 = createChevronValueDropDown(CRect(220, 77, 240, 97), kTagSetNumVoices, "", kCenterText, 16); numVoicesSlider_ = view__24; view__8->addView(view__24); -auto* const view__25 = createRoundedGroup(CRect(570, 5, 795, 105), -1, "", kCenterText, 14); +auto* const view__25 = createRoundedGroup(CRect(585, 5, 795, 105), -1, "", kCenterText, 14); view__2->addView(view__25); -auto* const view__26 = createKnob48(CRect(45, 15, 93, 63), -1, "", kCenterText, 14); +auto* const view__26 = createKnob48(CRect(30, 15, 78, 63), -1, "", kCenterText, 14); view__25->addView(view__26); view__26->setVisible(false); -auto* const view__27 = createValueLabel(CRect(40, 65, 100, 70), -1, "Center", kCenterText, 12); +auto* const view__27 = createValueLabel(CRect(25, 65, 85, 70), -1, "Center", kCenterText, 12); view__25->addView(view__27); view__27->setVisible(false); auto* const view__28 = createKnobCCBox(CRect(10, 5, 80, 95), kTagSetCCVolume, "Volume", kCenterText, 12); @@ -78,10 +78,10 @@ view__25->addView(view__28); auto* const view__29 = createKnobCCBox(CRect(85, 5, 155, 95), kTagSetCCPan, "Pan", kCenterText, 12); panCCKnob_ = view__29; view__25->addView(view__29); -auto* const view__30 = createVMeter(CRect(170, 10, 185, 95), -1, "", kCenterText, 14); +auto* const view__30 = createVMeter(CRect(165, 10, 180, 95), -1, "", kCenterText, 14); leftMeter_ = view__30; view__25->addView(view__30); -auto* const view__31 = createVMeter(CRect(190, 10, 205, 95), -1, "", kCenterText, 14); +auto* const view__31 = createVMeter(CRect(185, 10, 200, 95), -1, "", kCenterText, 14); rightMeter_ = view__31; view__25->addView(view__31); enterPalette(defaultPalette); From 9580ca84d58af925f11d5443670e75675e96c73e Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 27 Apr 2021 17:38:48 +0200 Subject: [PATCH 027/193] Thread-safety for receiving parameters --- plugins/vst/SfizzVstEditor.cpp | 128 ++++++++++++++++++++++----------- plugins/vst/SfizzVstEditor.h | 12 ++++ 2 files changed, 97 insertions(+), 43 deletions(-) diff --git a/plugins/vst/SfizzVstEditor.cpp b/plugins/vst/SfizzVstEditor.cpp index 6c85bfd36..4c9f2565d 100644 --- a/plugins/vst/SfizzVstEditor.cpp +++ b/plugins/vst/SfizzVstEditor.cpp @@ -18,6 +18,7 @@ #include "X11RunLoop.h" #endif #include +#include using namespace VSTGUI; @@ -93,6 +94,10 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p for (FObject* update : triggerUpdates_) update->addDependent(this); + threadChecker_ = Vst::ThreadChecker::create(); + + parametersToUpdate_.clear(); + Steinberg::IdleUpdateHandler::start(); for (FObject* update : continuousUpdates_) @@ -192,6 +197,7 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message) if (message == CVSTGUITimer::kMsgTimer) { processOscQueue(); processNoteEventQueue(); + processParameterUpdates(); updateEditorIsOpenParameter(); // Note(jpc) for Reaper, it can fail at open time } @@ -278,49 +284,18 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message) } if (Vst::RangeParameter* param = Steinberg::FCast(changedUnknown)) { - const Vst::ParamValue value = param->getNormalized(); - const Vst::ParamID id = param->getInfo().id; - const SfizzRange range = SfizzRange::getForParameter(id); - switch (id) { - case kPidVolume: - uiReceiveValue(EditId::Volume, range.denormalize(value)); - break; - case kPidNumVoices: - uiReceiveValue(EditId::Polyphony, range.denormalize(value)); - break; - case kPidOversampling: - uiReceiveValue(EditId::Oversampling, float(1u << (int32)range.denormalize(value))); - break; - case kPidPreloadSize: - uiReceiveValue(EditId::PreloadSize, range.denormalize(value)); - break; - case kPidScalaRootKey: - uiReceiveValue(EditId::ScalaRootKey, range.denormalize(value)); - break; - case kPidTuningFrequency: - uiReceiveValue(EditId::TuningFrequency, range.denormalize(value)); - break; - case kPidStretchedTuning: - uiReceiveValue(EditId::StretchTuning, range.denormalize(value)); - break; - case kPidSampleQuality: - uiReceiveValue(EditId::SampleQuality, range.denormalize(value)); - break; - case kPidOscillatorQuality: - uiReceiveValue(EditId::OscillatorQuality, range.denormalize(value)); - break; - case kPidLeftLevel: - uiReceiveValue(EditId::LeftLevel, range.denormalize(value)); - break; - case kPidRightLevel: - uiReceiveValue(EditId::RightLevel, range.denormalize(value)); - break; - default: - if (id >= kPidCC0 && id <= kPidCCLast) { - int cc = int(id - kPidCC0); - uiReceiveValue(editIdForCC(cc), range.denormalize(value)); - } - break; + // Note(jpc) some hosts send us the parameters in the wrong thread... + // store these parameters thread-safely and let the idle + // callback process them later + if (threadChecker_->test()) + updateParameter(param); + else { + static std::atomic_bool warn_once_flag { false }; + if (!warn_once_flag.exchange(true)) + fprintf(stderr, "[sfizz] using a thread-safety workaround for parameter updates\n"); + const Vst::ParamID id = param->getInfo().id; + std::lock_guard lock(parametersToUpdateMutex_); + parametersToUpdate_.insert(id); } return; } @@ -368,6 +343,73 @@ void SfizzVstEditor::processNoteEventQueue() queue->clear(); } +void SfizzVstEditor::processParameterUpdates() +{ + auto extractNextParamID = [this]() -> Vst::ParamID { + Vst::ParamID id = Vst::kNoParamId; + std::lock_guard lock(parametersToUpdateMutex_); + auto it = parametersToUpdate_.begin(); + if (it != parametersToUpdate_.end()) { + id = *it; + parametersToUpdate_.erase(it); + } + return id; + }; + + for (Vst::ParamID id; (id = extractNextParamID()) != Vst::kNoParamId; ) + updateParameter(getController()->getParameterObject(id)); +} + +void SfizzVstEditor::updateParameter(Vst::Parameter* parameterToUpdate) +{ + if (Vst::RangeParameter* param = FCast(parameterToUpdate)) { + const Vst::ParamID id = param->getInfo().id; + const Vst::ParamValue value = param->getNormalized(); + const SfizzRange range = SfizzRange::getForParameter(id); + switch (id) { + case kPidVolume: + uiReceiveValue(EditId::Volume, range.denormalize(value)); + break; + case kPidNumVoices: + uiReceiveValue(EditId::Polyphony, range.denormalize(value)); + break; + case kPidOversampling: + uiReceiveValue(EditId::Oversampling, float(1u << (int32)range.denormalize(value))); + break; + case kPidPreloadSize: + uiReceiveValue(EditId::PreloadSize, range.denormalize(value)); + break; + case kPidScalaRootKey: + uiReceiveValue(EditId::ScalaRootKey, range.denormalize(value)); + break; + case kPidTuningFrequency: + uiReceiveValue(EditId::TuningFrequency, range.denormalize(value)); + break; + case kPidStretchedTuning: + uiReceiveValue(EditId::StretchTuning, range.denormalize(value)); + break; + case kPidSampleQuality: + uiReceiveValue(EditId::SampleQuality, range.denormalize(value)); + break; + case kPidOscillatorQuality: + uiReceiveValue(EditId::OscillatorQuality, range.denormalize(value)); + break; + case kPidLeftLevel: + uiReceiveValue(EditId::LeftLevel, range.denormalize(value)); + break; + case kPidRightLevel: + uiReceiveValue(EditId::RightLevel, range.denormalize(value)); + break; + default: + if (id >= kPidCC0 && id <= kPidCCLast) { + int cc = int(id - kPidCC0); + uiReceiveValue(editIdForCC(cc), range.denormalize(value)); + } + break; + } + } +} + /// void SfizzVstEditor::uiSendValue(EditId id, const EditValue& v) { diff --git a/plugins/vst/SfizzVstEditor.h b/plugins/vst/SfizzVstEditor.h index d254dbd83..7d3b48c8b 100644 --- a/plugins/vst/SfizzVstEditor.h +++ b/plugins/vst/SfizzVstEditor.h @@ -8,8 +8,10 @@ #include "SfizzVstController.h" #include "editor/EditorController.h" #include "public.sdk/source/vst/vstguieditor.h" +#include "public.sdk/source/common/threadchecker.h" #include #include +#include class Editor; #if !defined(__APPLE__) && !defined(_WIN32) namespace VSTGUI { class RunLoop; } @@ -48,6 +50,8 @@ class SfizzVstEditor : public Vst::VSTGUIEditor, private: void processOscQueue(); void processNoteEventQueue(); + void processParameterUpdates(); + void updateParameter(Vst::Parameter* parameterToUpdate); protected: // EditorController @@ -84,4 +88,12 @@ class SfizzVstEditor : public Vst::VSTGUIEditor, // subscribed updates std::vector> continuousUpdates_; std::vector> triggerUpdates_; + + // thread safety + std::unique_ptr threadChecker_; + + // parameters to process, whose values have received changes + // Note(jpc) it's because hosts send us parameter updates in the wrong thread.. + std::set parametersToUpdate_; + std::mutex parametersToUpdateMutex_; }; From 20219b83a5203c00af5858551be2df20b3f05e7d Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 27 Apr 2021 18:26:35 +0200 Subject: [PATCH 028/193] Add ThreadChecker for macOS --- plugins/vst/cmake/Vst3.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/vst/cmake/Vst3.cmake b/plugins/vst/cmake/Vst3.cmake index 644ce82bc..c05706ddc 100644 --- a/plugins/vst/cmake/Vst3.cmake +++ b/plugins/vst/cmake/Vst3.cmake @@ -35,6 +35,8 @@ if(WIN32) target_sources(vst3sdk PRIVATE "${VST3SDK_BASEDIR}/public.sdk/source/common/threadchecker_win32.cpp") elseif(APPLE) + target_sources(vst3sdk PRIVATE + "${VST3SDK_BASEDIR}/public.sdk/source/common/threadchecker_mac.mm") else() target_sources(vst3sdk PRIVATE "${VST3SDK_BASEDIR}/public.sdk/source/common/threadchecker_linux.cpp") From ac8ba20ed527ddd9dcd480c35d4c782185d88328 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 28 Apr 2021 17:19:42 +0200 Subject: [PATCH 029/193] Make VST updates defered --- plugins/vst/SfizzVstController.cpp | 38 ++++---- plugins/vst/SfizzVstController.h | 3 +- plugins/vst/SfizzVstEditor.cpp | 143 +++++++++-------------------- plugins/vst/SfizzVstEditor.h | 20 +--- plugins/vst/SfizzVstUpdates.cpp | 70 ++++++-------- plugins/vst/SfizzVstUpdates.h | 60 ++++++------ 6 files changed, 126 insertions(+), 208 deletions(-) diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index dc2501624..eb6581371 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -21,8 +21,7 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) Steinberg::UpdateHandler::instance(); // create update objects - oscUpdate_ = Steinberg::owned(new OSCUpdate); - noteUpdate_ = Steinberg::owned(new NoteUpdate); + queuedUpdates_ = Steinberg::owned(new QueuedUpdates); sfzUpdate_ = Steinberg::owned(new SfzUpdate); sfzDescriptionUpdate_ = Steinberg::owned(new SfzDescriptionUpdate); scalaUpdate_ = Steinberg::owned(new ScalaUpdate); @@ -289,10 +288,10 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) if (result != kResultTrue) return result; - // this is a synchronous send, because the update object gets reused - oscUpdate_->setMessage(data, size, false); - oscUpdate_->changed(); - oscUpdate_->clear(); + IPtr update = Steinberg::owned( + new OSCUpdate(reinterpret_cast(data), size)); + queuedUpdates_->enqueue(update); + queuedUpdates_->deferUpdate(); } else if (!strcmp(id, "NoteEvents")) { const void* data = nullptr; @@ -303,10 +302,10 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) const std::pair*>(data); uint32 numEvents = size / sizeof(events[0]); - // this is a synchronous send, because the update object gets reused - noteUpdate_->setEvents(events, numEvents, false); - noteUpdate_->changed(); - noteUpdate_->clear(); + IPtr update = Steinberg::owned( + new NoteUpdate(events, numEvents)); + queuedUpdates_->enqueue(update); + queuedUpdates_->deferUpdate(); } else if (!strcmp(id, "Automate")) { const void* data = nullptr; @@ -343,20 +342,17 @@ IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name) if (name != Vst::ViewType::kEditor) return nullptr; - std::vector continuousUpdates; - continuousUpdates.push_back(sfzUpdate_); - continuousUpdates.push_back(sfzDescriptionUpdate_); - continuousUpdates.push_back(scalaUpdate_); - continuousUpdates.push_back(playStateUpdate_); + std::vector updates; + updates.push_back(queuedUpdates_); + updates.push_back(sfzUpdate_); + updates.push_back(sfzDescriptionUpdate_); + updates.push_back(scalaUpdate_); + updates.push_back(playStateUpdate_); for (uint32 i = 0, n = parameters.getParameterCount(); i < n; ++i) - continuousUpdates.push_back(parameters.getParameterByIndex(i)); - - std::vector triggerUpdates; - triggerUpdates.push_back(oscUpdate_); - triggerUpdates.push_back(noteUpdate_); + updates.push_back(parameters.getParameterByIndex(i)); IPtr editor = Steinberg::owned( - new SfizzVstEditor(this, absl::MakeSpan(continuousUpdates), absl::MakeSpan(triggerUpdates))); + new SfizzVstEditor(this, absl::MakeSpan(updates))); editor->remember(); return editor; diff --git a/plugins/vst/SfizzVstController.h b/plugins/vst/SfizzVstController.h index b8d11cd8c..db52179fb 100644 --- a/plugins/vst/SfizzVstController.h +++ b/plugins/vst/SfizzVstController.h @@ -44,8 +44,7 @@ class SfizzVstControllerNoUi : public Vst::EditController, REFCOUNT_METHODS(Vst::EditController) protected: - Steinberg::IPtr oscUpdate_; - Steinberg::IPtr noteUpdate_; + Steinberg::IPtr queuedUpdates_; Steinberg::IPtr sfzUpdate_; Steinberg::IPtr sfzDescriptionUpdate_; Steinberg::IPtr scalaUpdate_; diff --git a/plugins/vst/SfizzVstEditor.cpp b/plugins/vst/SfizzVstEditor.cpp index 4c9f2565d..9f9e614c6 100644 --- a/plugins/vst/SfizzVstEditor.cpp +++ b/plugins/vst/SfizzVstEditor.cpp @@ -30,14 +30,10 @@ enum { kNoteEventQueueSize = 8192, }; -SfizzVstEditor::SfizzVstEditor( - SfizzVstController* controller, - absl::Span continuousUpdates, - absl::Span triggerUpdates) +SfizzVstEditor::SfizzVstEditor(SfizzVstController* controller, absl::Span updates) : VSTGUIEditor(controller, &sfizzUiViewRect), oscTemp_(new uint8_t[kOscTempSize]), - continuousUpdates_(continuousUpdates.begin(), continuousUpdates.end()), - triggerUpdates_(triggerUpdates.begin(), triggerUpdates.end()) + updates_(updates.begin(), updates.end()) { } @@ -69,19 +65,6 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p editor_.reset(editor); } - { - std::lock_guard lock(oscQueueMutex_); - OscByteVec* queue = new OscByteVec; - oscQueue_.reset(queue); - queue->reserve(kOscQueueSize); - } - { - std::lock_guard lock(noteEventQueueMutex_); - NoteEventsVec* queue = new NoteEventsVec; - noteEventQueue_.reset(queue); - queue->reserve(kNoteEventQueueSize); - } - if (!frame->open(parent, platformType, config)) { fprintf(stderr, "[sfizz] error opening frame\n"); return false; @@ -89,9 +72,7 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p editor->open(*frame); - for (FObject* update : continuousUpdates_) - update->addDependent(this); - for (FObject* update : triggerUpdates_) + for (FObject* update : updates_) update->addDependent(this); threadChecker_ = Vst::ThreadChecker::create(); @@ -100,7 +81,7 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p Steinberg::IdleUpdateHandler::start(); - for (FObject* update : continuousUpdates_) + for (FObject* update : updates_) update->deferUpdate(); // let the editor know about plugin format @@ -138,9 +119,7 @@ void PLUGIN_API SfizzVstEditor::close() if (frame) { Steinberg::IdleUpdateHandler::stop(); - for (FObject* update : continuousUpdates_) - update->removeDependent(this); - for (FObject* update : triggerUpdates_) + for (FObject* update : updates_) update->removeDependent(this); if (editor_) @@ -152,15 +131,6 @@ void PLUGIN_API SfizzVstEditor::close() this->frame = nullptr; } - { - std::lock_guard lock(oscQueueMutex_); - oscQueue_.reset(); - } - { - std::lock_guard lock(noteEventQueueMutex_); - noteEventQueue_.reset(); - } - updateEditorIsOpenParameter(); } @@ -195,8 +165,6 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message) #endif if (message == CVSTGUITimer::kMsgTimer) { - processOscQueue(); - processNoteEventQueue(); processParameterUpdates(); updateEditorIsOpenParameter(); // Note(jpc) for Reaper, it can fail at open time } @@ -206,34 +174,51 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message) void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message) { + if (processUpdate(changedUnknown, message)) + return; + + Vst::VSTGUIEditor::update(changedUnknown, message); +} + +bool SfizzVstEditor::processUpdate(FUnknown* changedUnknown, int32 message) +{ + if (QueuedUpdates* update = FCast(changedUnknown)) { + for (FObject* queuedUpdate : update->getUpdates(this)) + processUpdate(queuedUpdate, message); + return true; + } + if (OSCUpdate* update = FCast(changedUnknown)) { - // this update is synchronous: may happen from non-UI thread - uint32 size = update->size(); - if (size > 0) { - const uint8_t* bytes = reinterpret_cast(update->data()); - std::lock_guard lock(oscQueueMutex_); - if (OscByteVec* queue = oscQueue_.get()) - std::copy(bytes, bytes + size, std::back_inserter(*queue)); + const uint8* oscData = update->data(); + uint32 oscSize = update->size(); + + const char* path; + const char* sig; + const sfizz_arg_t* args; + uint8_t buffer[1024]; + + uint32_t msgSize; + while ((msgSize = sfizz_extract_message(oscData, oscSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) { + uiReceiveMessage(path, sig, args); + oscData += msgSize; + oscSize -= msgSize; } - return; + + return true; } if (NoteUpdate* update = FCast(changedUnknown)) { - // this update is synchronous: may happen from non-UI thread + const NoteUpdate::Item* events = update->events(); uint32 count = update->count(); - if (count > 0) { - const auto* events = update->events(); - std::lock_guard lock(noteEventQueueMutex_); - if (NoteEventsVec* queue = noteEventQueue_.get()) - std::copy(events, events + count, std::back_inserter(*queue)); - } - return; + for (uint32 i = 0; i < count; ++i) + uiReceiveValue(editIdForKey(events[i].first), events[i].second); + return true; } if (SfzUpdate* update = FCast(changedUnknown)) { const std::string path = update->getPath(); uiReceiveValue(EditId::SfzFile, path); - return; + return true; } if (SfzDescriptionUpdate* update = FCast(changedUnknown)) { @@ -268,19 +253,19 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message) uiReceiveValue(editIdForCCLabel(int(cc)), desc.ccLabel[cc]); } } - return; + return true; } if (ScalaUpdate* update = FCast(changedUnknown)) { const std::string path = update->getPath(); uiReceiveValue(EditId::ScalaFile, path); - return; + return true; } if (PlayStateUpdate* update = FCast(changedUnknown)) { const SfizzPlayState playState = update->getState(); uiReceiveValue(EditId::UINumActiveVoices, playState.activeVoices); - return; + return true; } if (Vst::RangeParameter* param = Steinberg::FCast(changedUnknown)) { @@ -297,50 +282,10 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message) std::lock_guard lock(parametersToUpdateMutex_); parametersToUpdate_.insert(id); } - return; + return true; } - Vst::VSTGUIEditor::update(changedUnknown, message); -} - -void SfizzVstEditor::processOscQueue() -{ - std::lock_guard lock(oscQueueMutex_); - - OscByteVec* queue = oscQueue_.get(); - if (!queue) - return; - - const uint8_t* oscData = queue->data(); - size_t oscSize = queue->size(); - - const char* path; - const char* sig; - const sfizz_arg_t* args; - uint8_t buffer[1024]; - - uint32_t msgSize; - while ((msgSize = sfizz_extract_message(oscData, oscSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) { - uiReceiveMessage(path, sig, args); - oscData += msgSize; - oscSize -= msgSize; - } - - queue->clear(); -} - -void SfizzVstEditor::processNoteEventQueue() -{ - std::lock_guard lock(noteEventQueueMutex_); - - NoteEventsVec* queue = noteEventQueue_.get(); - if (!queue) - return; - - for (std::pair event : *queue) - uiReceiveValue(editIdForKey(event.first), event.second); - - queue->clear(); + return false; } void SfizzVstEditor::processParameterUpdates() diff --git a/plugins/vst/SfizzVstEditor.h b/plugins/vst/SfizzVstEditor.h index 7d3b48c8b..9ced76591 100644 --- a/plugins/vst/SfizzVstEditor.h +++ b/plugins/vst/SfizzVstEditor.h @@ -25,10 +25,7 @@ class SfizzVstEditor : public Vst::VSTGUIEditor, public: using Self = SfizzVstEditor; - SfizzVstEditor( - SfizzVstController* controller, - absl::Span continuousUpdates, - absl::Span triggerUpdates); + SfizzVstEditor(SfizzVstController* controller, absl::Span updates); ~SfizzVstEditor(); bool PLUGIN_API open(void* parent, const VSTGUI::PlatformType& platformType) override; @@ -48,8 +45,7 @@ class SfizzVstEditor : public Vst::VSTGUIEditor, // private: - void processOscQueue(); - void processNoteEventQueue(); + bool processUpdate(FUnknown* changedUnknown, int32 message); void processParameterUpdates(); void updateParameter(Vst::Parameter* parameterToUpdate); @@ -76,18 +72,8 @@ class SfizzVstEditor : public Vst::VSTGUIEditor, // messaging std::unique_ptr oscTemp_; - // editor state - // note: might be updated from a non-UI thread - typedef std::vector OscByteVec; - std::unique_ptr oscQueue_; - std::mutex oscQueueMutex_; - typedef std::vector> NoteEventsVec; - std::unique_ptr noteEventQueue_; - std::mutex noteEventQueueMutex_; - // subscribed updates - std::vector> continuousUpdates_; - std::vector> triggerUpdates_; + std::vector> updates_; // thread safety std::unique_ptr threadChecker_; diff --git a/plugins/vst/SfizzVstUpdates.cpp b/plugins/vst/SfizzVstUpdates.cpp index 20e7c1e41..6ffcbea53 100644 --- a/plugins/vst/SfizzVstUpdates.cpp +++ b/plugins/vst/SfizzVstUpdates.cpp @@ -8,61 +8,51 @@ #include #include -OSCUpdate::~OSCUpdate() +void QueuedUpdates::enqueue(IPtr update) { - clear(); + std::lock_guard lock(mutex_); + for (std::pair& item : updates_) + item.second.push_back(update); } -void OSCUpdate::clear() +auto QueuedUpdates::getUpdates(IDependent* dep) -> List { - if (allocated_) - delete[] reinterpret_cast(data_); - data_ = nullptr; - size_ = 0; - allocated_ = false; + std::lock_guard lock(mutex_); + List list; + auto it = updates_.find(dep); + if (it != updates_.end()) + std::swap(list, it->second); + return list; } -void OSCUpdate::setMessage(const void* data, uint32_t size, bool copy) +void QueuedUpdates::addDependent(IDependent* dep) { - clear(); - - if (copy) { - uint8_t *buffer = new uint8_t[size]; - std::memcpy(buffer, data, size); - data = buffer; - } - - data_ = data; - size_ = size; - allocated_ = copy; + std::lock_guard lock(mutex_); + FObject::addDependent(dep); + updates_.emplace(dep, List()); } -/// -NoteUpdate::~NoteUpdate() +void QueuedUpdates::removeDependent(IDependent* dep) { - clear(); + std::lock_guard lock(mutex_); + FObject::removeDependent(dep); + updates_.erase(dep); } -void NoteUpdate::clear() +/// +OSCUpdate::OSCUpdate(const uint8* data, uint32 size) { - if (allocated_) - delete[] events_; - events_ = nullptr; - count_ = 0; - allocated_ = false; + uint8* copy = new uint8[size]; + std::copy_n(data, size, copy); + data_.reset(copy); + size_ = size; } -void NoteUpdate::setEvents(const std::pair* events, uint32_t count, bool copy) +/// +NoteUpdate::NoteUpdate(const Item* items, uint32 count) { - clear(); - - if (copy) { - auto *buffer = new std::pair[count]; - std::copy_n(events, count, buffer); - events = buffer; - } - - events_ = events; + Item* copy = new Item[count]; + std::copy_n(items, count, copy); + events_.reset(copy); count_ = count; - allocated_ = copy; } diff --git a/plugins/vst/SfizzVstUpdates.h b/plugins/vst/SfizzVstUpdates.h index add4107ed..d39edcfd3 100644 --- a/plugins/vst/SfizzVstUpdates.h +++ b/plugins/vst/SfizzVstUpdates.h @@ -8,62 +8,64 @@ #include "SfizzVstState.h" #include #include +#include #include +#include #include #include /** - * @brief Update which notifies a single OSC message - * Is is supposed to be used synchronously. - * (ie. FObject::changed or UpdateHandler::triggerUpdates) + * @brief Update which notifies a FIFO queue of one-time updates */ -class OSCUpdate : public Steinberg::FObject { +class QueuedUpdates : public Steinberg::FObject { public: - OSCUpdate() = default; - ~OSCUpdate(); - void clear(); - void setMessage(const void* data, uint32_t size, bool copy); + using List = std::vector>; - const void* data() const noexcept { return data_; } - const uint32_t size() const noexcept { return size_; } + void enqueue(IPtr update); + List getUpdates(IDependent* dep); - OBJ_METHODS(OSCUpdate, FObject) + void addDependent(IDependent* dep) override; + void removeDependent(IDependent* dep) override; + + OBJ_METHODS(QueuedUpdates, FObject) private: - const void* data_ = nullptr; - uint32_t size_ = 0; - bool allocated_ = false; + std::mutex mutex_; + std::map updates_; +}; + +/** + * @brief Update which notifies a single OSC message + */ +class OSCUpdate : public Steinberg::FObject { +public: + OSCUpdate(const uint8* data, uint32 size); + const uint8* data() const noexcept { return data_.get(); } + uint32_t size() const noexcept { return size_; } + + OBJ_METHODS(OSCUpdate, FObject) private: - OSCUpdate(const OSCUpdate&) = delete; - OSCUpdate& operator=(const OSCUpdate&) = delete; + std::unique_ptr data_; + uint32 size_ = 0; }; /** * @brief Update which notifies one or more note on/off events - * Is is supposed to be used synchronously. - * (ie. FObject::changed or UpdateHandler::triggerUpdates) */ class NoteUpdate : public Steinberg::FObject { public: - NoteUpdate() = default; - ~NoteUpdate(); - void clear(); - void setEvents(const std::pair* events, uint32_t count, bool copy); + using Item = std::pair; - const std::pair* events() const noexcept { return events_; } + NoteUpdate(const Item* items, uint32 count); + const Item* events() const noexcept { return events_.get(); } const uint32_t count() const noexcept { return count_; } OBJ_METHODS(NoteUpdate, FObject) private: - const std::pair* events_ = nullptr; + std::unique_ptr events_; uint32_t count_ = 0; - bool allocated_ = false; - -private: - NoteUpdate(const NoteUpdate&) = delete; - NoteUpdate& operator=(const NoteUpdate&) = delete; }; /** From 9023209dfd6973f08588a1963d383eefde385afc Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 28 Apr 2021 17:59:37 +0200 Subject: [PATCH 030/193] Add thread checker for the VST controller --- plugins/vst/SfizzVstController.cpp | 10 ++++++++++ plugins/vst/SfizzVstController.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index eb6581371..9f7b92463 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -10,6 +10,7 @@ #include "SfizzVstIDs.h" #include "base/source/fstreamer.h" #include "base/source/updatehandler.h" +#include tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) { @@ -20,6 +21,9 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) // initialize the update handler Steinberg::UpdateHandler::instance(); + // initialize the thread checker + threadChecker_ = Vst::ThreadChecker::create(); + // create update objects queuedUpdates_ = Steinberg::owned(new QueuedUpdates); sfzUpdate_ = Steinberg::owned(new SfzUpdate); @@ -228,6 +232,12 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) if (result != kResultFalse) return result; + if (!threadChecker_->test()) { + static std::atomic_bool warn_once_flag { false }; + if (!warn_once_flag.exchange(true)) + fprintf(stderr, "[sfizz] controller notification arrives from the wrong thread\n"); + } + const char* id = message->getMessageID(); Vst::IAttributeList* attr = message->getAttributes(); diff --git a/plugins/vst/SfizzVstController.h b/plugins/vst/SfizzVstController.h index db52179fb..153bcd85c 100644 --- a/plugins/vst/SfizzVstController.h +++ b/plugins/vst/SfizzVstController.h @@ -9,6 +9,7 @@ #include "SfizzVstUpdates.h" #include "public.sdk/source/vst/vsteditcontroller.h" #include "public.sdk/source/vst/vstparameters.h" +#include "public.sdk/source/common/threadchecker.h" #include "pluginterfaces/vst/ivstmidicontrollers.h" #include "vstgui/plugin-bindings/vst3editor.h" #include @@ -44,6 +45,7 @@ class SfizzVstControllerNoUi : public Vst::EditController, REFCOUNT_METHODS(Vst::EditController) protected: + std::unique_ptr threadChecker_; Steinberg::IPtr queuedUpdates_; Steinberg::IPtr sfzUpdate_; Steinberg::IPtr sfzDescriptionUpdate_; From 356b14fc677e6bbd1e5ed6212d0893b9c15fed37 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 28 Apr 2021 18:03:09 +0200 Subject: [PATCH 031/193] Update processing stub for VST processor --- plugins/vst/SfizzVstProcessor.cpp | 18 ++++++++++++++++++ plugins/vst/SfizzVstProcessor.h | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp index 5dca65d14..536e16eae 100644 --- a/plugins/vst/SfizzVstProcessor.cpp +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -13,6 +13,7 @@ #include "plugin/SfizzFileScan.h" #include "plugin/InstrumentDescription.h" #include "base/source/fstreamer.h" +#include "base/source/updatehandler.h" #include "pluginterfaces/vst/ivstevents.h" #include "pluginterfaces/vst/ivstparameterchanges.h" #include @@ -74,6 +75,9 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context) if (result != kResultTrue) return result; + // initialize the update handler + Steinberg::UpdateHandler::instance(); + addAudioOutput(STR16("Audio Output"), Vst::SpeakerArr::kStereo); addEventInput(STR16("Event Input"), 1); @@ -581,6 +585,20 @@ tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message) return result; } +void PLUGIN_API SfizzVstProcessor::update(FUnknown* changedUnknown, int32 message) +{ + if (processUpdate(changedUnknown, message)) + return; + + AudioEffect::update(changedUnknown, message); +} + +bool SfizzVstProcessor::processUpdate(FUnknown* changedUnknown, int32 message) +{ + // TODO + return false; +} + void SfizzVstProcessor::receiveMessage(int delay, const char* path, const char* sig, const sfizz_arg_t* args) { uint8_t* oscTemp = _oscTemp.get(); diff --git a/plugins/vst/SfizzVstProcessor.h b/plugins/vst/SfizzVstProcessor.h index 1ab35a9d3..16fc47f4d 100644 --- a/plugins/vst/SfizzVstProcessor.h +++ b/plugins/vst/SfizzVstProcessor.h @@ -46,6 +46,7 @@ class SfizzVstProcessor : public Vst::AudioEffect, void processMessagesFromUi(); tresult PLUGIN_API notify(Vst::IMessage* message) override; + void PLUGIN_API update(FUnknown* changedUnknown, int32 message) override; static FUnknown* createInstance(void*); @@ -68,6 +69,9 @@ class SfizzVstProcessor : public Vst::AudioEffect, RMSFollower _rmsFollower; bool _editorIsOpen = false; + // updates + bool processUpdate(FUnknown* changedUnknown, int32 message); + // client sfz::ClientPtr _client; std::unique_ptr _oscTemp; From 2da580894c80105c88db12a7e593570f67f3b794 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Thu, 29 Apr 2021 10:01:27 +0200 Subject: [PATCH 032/193] VST stub for queued updates --- plugins/vst/SfizzVstProcessor.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp index 536e16eae..0e7f73d09 100644 --- a/plugins/vst/SfizzVstProcessor.cpp +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -595,6 +595,12 @@ void PLUGIN_API SfizzVstProcessor::update(FUnknown* changedUnknown, int32 messag bool SfizzVstProcessor::processUpdate(FUnknown* changedUnknown, int32 message) { + if (QueuedUpdates* update = FCast(changedUnknown)) { + for (FObject* queuedUpdate : update->getUpdates(this)) + processUpdate(queuedUpdate, message); + return true; + } + // TODO return false; } From 3986bc782b2144fc6cdf207584be4c58dc221a05 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Thu, 29 Apr 2021 10:05:28 +0200 Subject: [PATCH 033/193] VST renaming to lift some ambiguity --- plugins/vst/SfizzVstProcessor.cpp | 10 +++++----- plugins/vst/SfizzVstProcessor.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp index 0e7f73d09..a2d8573e1 100644 --- a/plugins/vst/SfizzVstProcessor.cpp +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -35,7 +35,7 @@ static const char* kRingIdOsc = "Osc"; static const char* kMsgIdSetNumVoices = "SetNumVoices"; static const char* kMsgIdSetOversampling = "SetOversampling"; static const char* kMsgIdSetPreloadSize = "SetPreloadSize"; -static const char* kMsgIdReceiveMessage = "ReceiveMessage"; +static const char* kMsgIdReceiveOSC = "ReceiveOSC"; static const char* kMsgIdNoteEvents = "NoteEvents"; static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 20 }; @@ -92,7 +92,7 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context) auto onMessage = +[](void* data, int delay, const char* path, const char* sig, const sfizz_arg_t* args) { auto *self = reinterpret_cast(data); - self->receiveMessage(delay, path, sig, args); + self->receiveOSC(delay, path, sig, args); }; _client = _synth->createClient(this); _synth->setReceiveCallback(*_client, onMessage); @@ -605,12 +605,12 @@ bool SfizzVstProcessor::processUpdate(FUnknown* changedUnknown, int32 message) return false; } -void SfizzVstProcessor::receiveMessage(int delay, const char* path, const char* sig, const sfizz_arg_t* args) +void SfizzVstProcessor::receiveOSC(int delay, const char* path, const char* sig, const sfizz_arg_t* args) { uint8_t* oscTemp = _oscTemp.get(); uint32 oscSize = sfizz_prepare_message(oscTemp, kOscTempSize, path, sig, args); if (oscSize <= kOscTempSize) { - if (writeWorkerMessage(kMsgIdReceiveMessage, oscTemp, oscSize)) + if (writeWorkerMessage(kMsgIdReceiveOSC, oscTemp, oscSize)) _semaToWorker.post(); } } @@ -728,7 +728,7 @@ void SfizzVstProcessor::doBackgroundWork() std::lock_guard lock(_processMutex); _synth->setPreloadSize(value); } - else if (id == kMsgIdReceiveMessage) { + else if (id == kMsgIdReceiveOSC) { Steinberg::OPtr notification { allocateMessage() }; notification->setMessageID("ReceivedMessage"); notification->getAttributes()->setBinary("Message", msg->payload(), msg->size); diff --git a/plugins/vst/SfizzVstProcessor.h b/plugins/vst/SfizzVstProcessor.h index 16fc47f4d..89ff18509 100644 --- a/plugins/vst/SfizzVstProcessor.h +++ b/plugins/vst/SfizzVstProcessor.h @@ -75,7 +75,7 @@ class SfizzVstProcessor : public Vst::AudioEffect, // client sfz::ClientPtr _client; std::unique_ptr _oscTemp; - void receiveMessage(int delay, const char* path, const char* sig, const sfizz_arg_t* args); + void receiveOSC(int delay, const char* path, const char* sig, const sfizz_arg_t* args); // misc void loadSfzFileOrDefault(const std::string& filePath, bool initParametersFromState); From 0d63c7ee5f5a709a2a94505e0089e01913b67ee8 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Thu, 29 Apr 2021 15:38:43 +0200 Subject: [PATCH 034/193] Add vst support for message<->update conversion --- plugins/vst/CMakeLists.txt | 1 + plugins/vst/SfizzVstUpdates.cpp | 18 ++++++++++++----- plugins/vst/SfizzVstUpdates.h | 36 +++++++++++++++++++++++++++------ plugins/vst/SfizzVstUpdates.hpp | 30 +++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 11 deletions(-) create mode 100644 plugins/vst/SfizzVstUpdates.hpp diff --git a/plugins/vst/CMakeLists.txt b/plugins/vst/CMakeLists.txt index a92125ad3..894975f45 100644 --- a/plugins/vst/CMakeLists.txt +++ b/plugins/vst/CMakeLists.txt @@ -33,6 +33,7 @@ add_library(sfizz-vst3-core STATIC EXCLUDE_FROM_ALL SfizzVstState.h SfizzVstParameters.h SfizzVstUpdates.h + SfizzVstUpdates.hpp SfizzVstUpdates.cpp SfizzVstIDs.h OrderedEventProcessor.h diff --git a/plugins/vst/SfizzVstUpdates.cpp b/plugins/vst/SfizzVstUpdates.cpp index 6ffcbea53..eac7a6856 100644 --- a/plugins/vst/SfizzVstUpdates.cpp +++ b/plugins/vst/SfizzVstUpdates.cpp @@ -40,12 +40,20 @@ void QueuedUpdates::removeDependent(IDependent* dep) } /// -OSCUpdate::OSCUpdate(const uint8* data, uint32 size) +bool OSCUpdate::saveToAttributes(Vst::IAttributeList* attrs) const { - uint8* copy = new uint8[size]; - std::copy_n(data, size, copy); - data_.reset(copy); - size_ = size; + return attrs->setBinary("Data", data(), size()) == kResultTrue; +} + +bool OSCUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + const void* data; + uint32 size; + if (attrs->getBinary("Data", data, size) != kResultTrue) + return false; + const uint8* data8 = reinterpret_cast(data); + data_.assign(data8, data8 + size); + return true; } /// diff --git a/plugins/vst/SfizzVstUpdates.h b/plugins/vst/SfizzVstUpdates.h index d39edcfd3..23f8a529d 100644 --- a/plugins/vst/SfizzVstUpdates.h +++ b/plugins/vst/SfizzVstUpdates.h @@ -6,14 +6,32 @@ #pragma once #include "SfizzVstState.h" +#include +#include #include #include #include #include #include #include +#include #include +/** + * @brief Update which is convertible with Vst::IMessage back and forth. + */ +template class IConvertibleToMessage { +public: + virtual ~IConvertibleToMessage() {} + + IPtr convertToMessage(Vst::ComponentBase* sender) const; + static IPtr convertFromMessage(Vst::IMessage& message); + +protected: + virtual bool saveToAttributes(Vst::IAttributeList* attrs) const = 0; + virtual bool loadFromAttributes(Vst::IAttributeList* attrs) = 0; +}; + /** * @brief Update which notifies a FIFO queue of one-time updates */ @@ -37,17 +55,21 @@ class QueuedUpdates : public Steinberg::FObject { /** * @brief Update which notifies a single OSC message */ -class OSCUpdate : public Steinberg::FObject { +class OSCUpdate : public Steinberg::FObject, + public IConvertibleToMessage { public: - OSCUpdate(const uint8* data, uint32 size); - const uint8* data() const noexcept { return data_.get(); } - uint32_t size() const noexcept { return size_; } + OSCUpdate() = default; + OSCUpdate(const uint8* data, uint32 size) : data_(data, data + size) {} + const uint8* data() const noexcept { return data_.data(); } + uint32_t size() const noexcept { return static_cast(data_.size()); } + + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; OBJ_METHODS(OSCUpdate, FObject) private: - std::unique_ptr data_; - uint32 size_ = 0; + std::vector data_; }; /** @@ -163,3 +185,5 @@ class PlayStateUpdate : public Steinberg::FObject { SfizzPlayState state_ {}; mutable std::mutex mutex_; }; + +#include "SfizzVstUpdates.hpp" diff --git a/plugins/vst/SfizzVstUpdates.hpp b/plugins/vst/SfizzVstUpdates.hpp new file mode 100644 index 000000000..92f7557bd --- /dev/null +++ b/plugins/vst/SfizzVstUpdates.hpp @@ -0,0 +1,30 @@ +// 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 "SfizzVstUpdates.h" + +template +IPtr IConvertibleToMessage::convertToMessage(Vst::ComponentBase* sender) const +{ + IPtr message = owned(sender->allocateMessage()); + message->setMessageID(static_cast(this)->isA()); + if (!saveToAttributes(message->getAttributes())) + return nullptr; + return message; +} + +template +IPtr IConvertibleToMessage::convertFromMessage(Vst::IMessage& message) +{ + IPtr object; + if (!strcmp(T::getFClassID(), message.getMessageID())) { + object = owned(new T); + if (!object->loadFromAttributes(message.getAttributes())) + object = nullptr; + } + return object; +} From e93e760f4f5ca343aac60ab5318fab6a9fd42181 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Thu, 29 Apr 2021 15:51:53 +0200 Subject: [PATCH 035/193] Add a little safety check --- plugins/vst/SfizzVstUpdates.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/vst/SfizzVstUpdates.hpp b/plugins/vst/SfizzVstUpdates.hpp index 92f7557bd..48f9d32a2 100644 --- a/plugins/vst/SfizzVstUpdates.hpp +++ b/plugins/vst/SfizzVstUpdates.hpp @@ -11,6 +11,8 @@ template IPtr IConvertibleToMessage::convertToMessage(Vst::ComponentBase* sender) const { IPtr message = owned(sender->allocateMessage()); + if (!message) + return nullptr; message->setMessageID(static_cast(this)->isA()); if (!saveToAttributes(message->getAttributes())) return nullptr; From eb22706fcd2330885a413d6256ddb1ecfaa2ed41 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 28 Apr 2021 18:06:01 +0200 Subject: [PATCH 036/193] Defered message sending from VST processor --- plugins/vst/SfizzVstController.cpp | 124 +++++++++----------------- plugins/vst/SfizzVstProcessor.cpp | 137 ++++++++++++++++++++--------- plugins/vst/SfizzVstProcessor.h | 10 ++- plugins/vst/SfizzVstUpdates.cpp | 118 +++++++++++++++++++++++-- plugins/vst/SfizzVstUpdates.h | 108 +++++++++++++++++------ plugins/vst/SfizzVstUpdates.hpp | 15 +++- 6 files changed, 348 insertions(+), 164 deletions(-) diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index 9f7b92463..4cd7abfc4 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -11,6 +11,7 @@ #include "base/source/fstreamer.h" #include "base/source/updatehandler.h" #include +#include tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) { @@ -226,7 +227,7 @@ tresult PLUGIN_API SfizzVstControllerNoUi::setComponentState(IBStream* stream) tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) { - // Note: may be called from any thread (Reaper) + // Note: is expected to be called from the controller thread only tresult result = EditController::notify(message); if (result != kResultFalse) @@ -239,103 +240,62 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) } const char* id = message->getMessageID(); - Vst::IAttributeList* attr = message->getAttributes(); /// - auto stringFromBinaryAttribute = [attr](const char* id, absl::string_view& string) -> tresult { - const void* data = nullptr; - uint32 size = 0; - tresult result = attr->getBinary(id, data, size); - if (result == kResultTrue) - string = absl::string_view(reinterpret_cast(data), size); - return result; - }; - - /// - if (!strcmp(id, "LoadedSfz")) { - absl::string_view sfzFile; - absl::string_view sfzDescriptionBlob; - - result = stringFromBinaryAttribute("File", sfzFile); - if (result != kResultTrue) - return result; - - result = stringFromBinaryAttribute("Description", sfzDescriptionBlob); - if (result != kResultTrue) - return result; - - sfzUpdate_->setPath(std::string(sfzFile)); + if (!strcmp(id, SfzUpdate::getFClassID())) { + if (!sfzUpdate_->convertFromMessage(*message)) { + assert(false); + return kResultFalse; + } sfzUpdate_->deferUpdate(); - sfzDescriptionUpdate_->setDescription(std::string(sfzDescriptionBlob)); + } + else if (!strcmp(id, SfzDescriptionUpdate::getFClassID())) { + if (!sfzDescriptionUpdate_->convertFromMessage(*message)) { + assert(false); + return kResultFalse; + } sfzDescriptionUpdate_->deferUpdate(); } - else if (!strcmp(id, "LoadedScala")) { - absl::string_view scalaFile; - - result = stringFromBinaryAttribute("File", scalaFile); - if (result != kResultTrue) - return result; - - scalaUpdate_->setPath(std::string(scalaFile)); + else if (!strcmp(id, ScalaUpdate::getFClassID())) { + if (!scalaUpdate_->convertFromMessage(*message)) { + assert(false); + return kResultFalse; + } scalaUpdate_->deferUpdate(); } - else if (!strcmp(id, "NotifiedPlayState")) { - const void* data = nullptr; - uint32 size = 0; - result = attr->getBinary("PlayState", data, size); - - if (result != kResultTrue) - return result; - - playStateUpdate_->setState(*static_cast(data)); + else if (!strcmp(id, PlayStateUpdate::getFClassID())) { + if (!playStateUpdate_->convertFromMessage(*message)) { + assert(false); + return kResultFalse; + } playStateUpdate_->deferUpdate(); } - else if (!strcmp(id, "ReceivedMessage")) { - const void* data = nullptr; - uint32 size = 0; - result = attr->getBinary("Message", data, size); - - if (result != kResultTrue) - return result; - - IPtr update = Steinberg::owned( - new OSCUpdate(reinterpret_cast(data), size)); + else if (!strcmp(id, OSCUpdate::getFClassID())) { + IPtr update = OSCUpdate::createFromMessage(*message); + if (!update) { + assert(false); + return kResultFalse; + } queuedUpdates_->enqueue(update); queuedUpdates_->deferUpdate(); } - else if (!strcmp(id, "NoteEvents")) { - const void* data = nullptr; - uint32 size = 0; - result = attr->getBinary("Events", data, size); - - const auto* events = reinterpret_cast< - const std::pair*>(data); - uint32 numEvents = size / sizeof(events[0]); - - IPtr update = Steinberg::owned( - new NoteUpdate(events, numEvents)); + else if (!strcmp(id, NoteUpdate::getFClassID())) { + IPtr update = NoteUpdate::createFromMessage(*message); + if (!update) { + assert(false); + return kResultFalse; + } queuedUpdates_->enqueue(update); queuedUpdates_->deferUpdate(); } - else if (!strcmp(id, "Automate")) { - const void* data = nullptr; - uint32 size = 0; - result = attr->getBinary("Data", data, size); - - if (result != kResultTrue) - return result; - - const uint8* pos = reinterpret_cast(data); - const uint8* end = pos + size; - - while (static_cast(end - pos) >= sizeof(uint32) + sizeof(float)) { - Vst::ParamID pid = *reinterpret_cast(pos); - pos += sizeof(uint32); - float value = *reinterpret_cast(pos); - pos += sizeof(float); - #pragma message("setParam() on non-UI thread is dangerous on Reaper, make it deferred instead") //NOLINT - setParam(pid, value); + else if (!strcmp(id, AutomationUpdate::getFClassID())) { + IPtr update = AutomationUpdate::createFromMessage(*message); + if (!update) { + assert(false); + return kResultFalse; } + for (AutomationUpdate::Item item : update->getItems()) + setParam(item.first, item.second); } return result; diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp index a2d8573e1..0614803be 100644 --- a/plugins/vst/SfizzVstProcessor.cpp +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -78,6 +78,20 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context) // initialize the update handler Steinberg::UpdateHandler::instance(); + _queuedMessages = Steinberg::owned(new QueuedUpdates); + _playStateUpdate = Steinberg::owned(new PlayStateUpdate); + _sfzUpdate = Steinberg::owned(new SfzUpdate); + _sfzDescriptionUpdate = Steinberg::owned(new SfzDescriptionUpdate); + _scalaUpdate = Steinberg::owned(new ScalaUpdate); + _automationUpdate = Steinberg::owned(new AutomationUpdate); + + _queuedMessages->addDependent(this); + _playStateUpdate->addDependent(this); + _sfzUpdate->addDependent(this); + _sfzDescriptionUpdate->addDependent(this); + _scalaUpdate->addDependent(this); + _automationUpdate->addDependent(this); + addAudioOutput(STR16("Audio Output"), Vst::SpeakerArr::kStereo); addEventInput(STR16("Event Input"), 1); @@ -115,6 +129,18 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context) return result; } +tresult PLUGIN_API SfizzVstProcessor::terminate() +{ + _queuedMessages->removeDependent(this); + _playStateUpdate->removeDependent(this); + _sfzUpdate->removeDependent(this); + _sfzDescriptionUpdate->removeDependent(this); + _scalaUpdate->removeDependent(this); + _automationUpdate->removeDependent(this); + + return AudioEffect::terminate(); +} + tresult PLUGIN_API SfizzVstProcessor::setBusArrangements(Vst::SpeakerArrangement* inputs, int32 numIns, Vst::SpeakerArrangement* outputs, int32 numOuts) { bool isStereo = numIns == 0 && numOuts == 1 && outputs[0] == Vst::SpeakerArr::kStereo; @@ -132,10 +158,7 @@ tresult PLUGIN_API SfizzVstProcessor::connect(IConnectionPoint* other) return result; // when controller connects, send these messages that we couldn't earlier - if (_loadedSfzMessage) - sendMessage(_loadedSfzMessage); - if (_automateMessage) - sendMessage(_automateMessage); + _queuedMessages->deferUpdate(); return kResultTrue; } @@ -316,7 +339,7 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data) synth.sendMessage(client, 0, "/sw/last/current", "", nullptr); // - std::pair noteEvents[128]; + NoteUpdate::Item noteEvents[128]; size_t numNoteEvents = 0; for (uint32 key = 0; key < 128; ++key) { float value = _noteEventsCurrentCycle[key]; @@ -528,7 +551,7 @@ void SfizzVstProcessor::processMessagesFromUi() tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message) { - // Note(jpc) this notification is not necessarily handled by the RT thread + // Note(jpc) this notification is not handled by the RT thread tresult result = AudioEffect::notify(message); if (result != kResultFalse) @@ -563,10 +586,8 @@ tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message) _synth->loadScalaFile(_state.scalaFile); lock.unlock(); - Steinberg::OPtr reply { allocateMessage() }; - reply->setMessageID("LoadedScala"); - reply->getAttributes()->setBinary("File", _state.scalaFile.data(), _state.scalaFile.size()); - sendMessage(reply); + _scalaUpdate->setPath(_state.scalaFile); + _scalaUpdate->deferUpdate(); } else if (!std::strcmp(id, "MidiMessage")) { const void* data = nullptr; @@ -601,7 +622,48 @@ bool SfizzVstProcessor::processUpdate(FUnknown* changedUnknown, int32 message) return true; } - // TODO + if (OSCUpdate* update = FCast(changedUnknown)) { + if (IPtr message = update->convertToMessage(this)) + sendMessage(message); + return true; + } + + if (PlayStateUpdate* update = FCast(changedUnknown)) { + if (IPtr message = update->convertToMessage(this)) + sendMessage(message); + return true; + } + + if (NoteUpdate* update = FCast(changedUnknown)) { + if (IPtr message = update->convertToMessage(this)) + sendMessage(message); + return true; + } + + if (SfzUpdate* update = FCast(changedUnknown)) { + if (IPtr message = update->convertToMessage(this)) + sendMessage(message); + return true; + } + + if (SfzDescriptionUpdate* update = FCast(changedUnknown)) { + if (IPtr message = update->convertToMessage(this)) + sendMessage(message); + return true; + } + + if (ScalaUpdate* update = FCast(changedUnknown)) { + if (IPtr message = update->convertToMessage(this)) + sendMessage(message); + return true; + } + + if (AutomationUpdate* update = FCast(changedUnknown)) { + if (IPtr message = update->convertToMessage(this)) + sendMessage(message); + return true; + } + return false; } @@ -637,12 +699,6 @@ void SfizzVstProcessor::loadSfzFileOrDefault(const std::string& filePath, bool i const std::string descBlob = getDescriptionBlob(synth.handle()); - Steinberg::OPtr loadMessage { allocateMessage() }; - loadMessage->setMessageID("LoadedSfz"); - Vst::IAttributeList* loadAttrs = loadMessage->getAttributes(); - loadAttrs->setBinary("File", filePath.data(), filePath.size()); - loadAttrs->setBinary("Description", descBlob.data(), descBlob.size()); - { std::vector> newControllers(sfz::config::numCCs); const std::vector> oldControllers = std::move(_state.controllers); @@ -665,26 +721,21 @@ void SfizzVstProcessor::loadSfzFileOrDefault(const std::string& filePath, bool i } // create a message which requests the controller to automate initial parameters - Steinberg::OPtr automateMessage { allocateMessage() }; - automateMessage->setMessageID("Automate"); - Vst::IAttributeList* automateAttrs = automateMessage->getAttributes(); - std::string automateBlob; - automateBlob.reserve(sfz::config::numCCs * (sizeof(uint32) + sizeof(float))); + std::vector automationItems; + automationItems.reserve(sfz::config::numCCs); for (uint32 cc = 0; cc < sfz::config::numCCs; ++cc) { - uint32 pid = kPidCC0 + cc; + Vst::ParamID pid = kPidCC0 + cc; float value = _state.controllers[cc].value_or(0.0f); - automateBlob.append(reinterpret_cast(&pid), sizeof(uint32)); - automateBlob.append(reinterpret_cast(&value), sizeof(float)); + automationItems.emplace_back(pid, value); } - automateAttrs->setBinary("Data", automateBlob.data(), uint32(automateBlob.size())); - - // sending can fail if controller is not connected yet, so keep it around - _loadedSfzMessage = loadMessage; - _automateMessage = automateMessage; // send message - sendMessage(loadMessage); - sendMessage(automateMessage); + _sfzUpdate->setPath(filePath); + _sfzUpdate->deferUpdate(); + _sfzDescriptionUpdate->setDescription(descBlob); + _sfzDescriptionUpdate->deferUpdate(); + _automationUpdate->setItems(std::move(automationItems)); + _automationUpdate->deferUpdate(); } void SfizzVstProcessor::doBackgroundWork() @@ -729,16 +780,16 @@ void SfizzVstProcessor::doBackgroundWork() _synth->setPreloadSize(value); } else if (id == kMsgIdReceiveOSC) { - Steinberg::OPtr notification { allocateMessage() }; - notification->setMessageID("ReceivedMessage"); - notification->getAttributes()->setBinary("Message", msg->payload(), msg->size); - sendMessage(notification); + IPtr update = Steinberg::owned( + new OSCUpdate(msg->payload(), msg->size)); + _queuedMessages->enqueue(update); + _queuedMessages->deferUpdate(); } else if (id == kMsgIdNoteEvents) { - Steinberg::OPtr notification { allocateMessage() }; - notification->setMessageID("NoteEvents"); - notification->getAttributes()->setBinary("Events", msg->payload(), msg->size); - sendMessage(notification); + IPtr update = Steinberg::owned( + new NoteUpdate(msg->payload(), msg->size / sizeof(NoteUpdate::Item))); + _queuedMessages->enqueue(update); + _queuedMessages->deferUpdate(); } Clock::time_point currentTime = Clock::now(); @@ -755,10 +806,8 @@ void SfizzVstProcessor::doBackgroundIdle(size_t idleCounter) { SfizzPlayState ps; ps.activeVoices = _synth->getNumActiveVoices(); - Steinberg::OPtr notification { allocateMessage() }; - notification->setMessageID("NotifiedPlayState"); - notification->getAttributes()->setBinary("PlayState", &ps, sizeof(ps)); - sendMessage(notification); + _playStateUpdate->setState(ps); + _playStateUpdate->deferUpdate(); } if (idleCounter % 25 == 0) { diff --git a/plugins/vst/SfizzVstProcessor.h b/plugins/vst/SfizzVstProcessor.h index 89ff18509..a41016e88 100644 --- a/plugins/vst/SfizzVstProcessor.h +++ b/plugins/vst/SfizzVstProcessor.h @@ -6,6 +6,7 @@ #pragma once #include "SfizzVstState.h" +#include "SfizzVstUpdates.h" #include "OrderedEventProcessor.h" #include "plugin/RMSFollower.h" #include "sfizz/RTSemaphore.h" @@ -27,6 +28,7 @@ class SfizzVstProcessor : public Vst::AudioEffect, ~SfizzVstProcessor(); tresult PLUGIN_API initialize(FUnknown* context) override; + tresult PLUGIN_API terminate() override; tresult PLUGIN_API setBusArrangements(Vst::SpeakerArrangement* inputs, int32 numIns, Vst::SpeakerArrangement* outputs, int32 numOuts) override; tresult PLUGIN_API connect(IConnectionPoint* other) override; @@ -56,8 +58,6 @@ class SfizzVstProcessor : public Vst::AudioEffect, private: // synth state. acquire processMutex before accessing std::unique_ptr _synth; - Steinberg::IPtr _loadedSfzMessage; - Steinberg::IPtr _automateMessage; bool _isActive = false; SfizzVstState _state; float _currentStretchedTuning = 0; @@ -70,6 +70,12 @@ class SfizzVstProcessor : public Vst::AudioEffect, bool _editorIsOpen = false; // updates + IPtr _queuedMessages; + IPtr _playStateUpdate; + IPtr _sfzUpdate; + IPtr _sfzDescriptionUpdate; + IPtr _scalaUpdate; + IPtr _automationUpdate; bool processUpdate(FUnknown* changedUnknown, int32 message); // client diff --git a/plugins/vst/SfizzVstUpdates.cpp b/plugins/vst/SfizzVstUpdates.cpp index eac7a6856..5dfcfd3a6 100644 --- a/plugins/vst/SfizzVstUpdates.cpp +++ b/plugins/vst/SfizzVstUpdates.cpp @@ -57,10 +57,118 @@ bool OSCUpdate::loadFromAttributes(Vst::IAttributeList* attrs) } /// -NoteUpdate::NoteUpdate(const Item* items, uint32 count) +bool NoteUpdate::saveToAttributes(Vst::IAttributeList* attrs) const { - Item* copy = new Item[count]; - std::copy_n(items, count, copy); - events_.reset(copy); - count_ = count; + return attrs->setBinary("Events", events_.data(), events_.size() * sizeof(Item)) == kResultTrue; +} + +bool NoteUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + const void* binData = nullptr; + uint32 binSize = 0; + if (attrs->getBinary("Events", binData, binSize) != kResultTrue) + return false; + + const Item* events = reinterpret_cast(binData); + uint32 numEvents = binSize / sizeof(Item); + + events_.assign(events, events + numEvents); + return true; +} + +/// +bool FilePathUpdate::saveFilePathAttributes_(Vst::IAttributeList* attrs) const +{ + std::lock_guard lock(mutex_); + return attrs->setBinary("Path", path_.data(), path_.size()) == kResultTrue; +} + +bool FilePathUpdate::loadFilePathAttributes_(Vst::IAttributeList* attrs) +{ + const void* binData = nullptr; + uint32 binSize = 0; + if (attrs->getBinary("Path", binData, binSize) != kResultTrue) + return false; + std::lock_guard lock(mutex_); + path_.assign(reinterpret_cast(binData), binSize); + return true; +} + +/// +bool SfzUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + return saveFilePathAttributes_(attrs); +} + +bool SfzUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + return loadFilePathAttributes_(attrs); +} + +/// +bool ScalaUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + return saveFilePathAttributes_(attrs); +} + +bool ScalaUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + return loadFilePathAttributes_(attrs); +} + +/// +bool SfzDescriptionUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + std::lock_guard lock(mutex_); + return attrs->setBinary("Blob", description_.data(), description_.size()) == kResultTrue; +} + +bool SfzDescriptionUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + const void* binData = nullptr; + uint32 binSize = 0; + if (attrs->getBinary("Blob", binData, binSize) != kResultTrue) + return false; + std::lock_guard lock(mutex_); + description_.assign(reinterpret_cast(binData), binSize); + return true; +} + +/// +bool PlayStateUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + std::lock_guard lock(mutex_); + return attrs->setInt("ActiveVoices", state_.activeVoices) == kResultTrue; +} + +bool PlayStateUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + int64 activeVoices; + if (attrs->getInt("ActiveVoices", activeVoices) != kResultTrue) + return false; + std::lock_guard lock(mutex_); + state_.activeVoices = static_cast(activeVoices); + return true; +} + +/// +bool AutomationUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + std::lock_guard lock(mutex_); + return attrs->setBinary("Items", items_.data(), items_.size() * sizeof(Item)) == kResultTrue; +} + +bool AutomationUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + const void* binData = nullptr; + uint32 binSize = 0; + if (attrs->getBinary("Items", binData, binSize) != kResultTrue) + return false; + + const Item* events = reinterpret_cast(binData); + uint32 numEvents = binSize / sizeof(Item); + + std::lock_guard lock(mutex_); + items_.assign(events, events + numEvents); + return true; } diff --git a/plugins/vst/SfizzVstUpdates.h b/plugins/vst/SfizzVstUpdates.h index 23f8a529d..76357d5f3 100644 --- a/plugins/vst/SfizzVstUpdates.h +++ b/plugins/vst/SfizzVstUpdates.h @@ -25,7 +25,8 @@ template class IConvertibleToMessage { virtual ~IConvertibleToMessage() {} IPtr convertToMessage(Vst::ComponentBase* sender) const; - static IPtr convertFromMessage(Vst::IMessage& message); + bool convertFromMessage(Vst::IMessage& message); + static IPtr createFromMessage(Vst::IMessage& message); protected: virtual bool saveToAttributes(Vst::IAttributeList* attrs) const = 0; @@ -75,25 +76,32 @@ class OSCUpdate : public Steinberg::FObject, /** * @brief Update which notifies one or more note on/off events */ -class NoteUpdate : public Steinberg::FObject { +class NoteUpdate : public Steinberg::FObject, + public IConvertibleToMessage { public: - using Item = std::pair; + using Item = std::pair; - NoteUpdate(const Item* items, uint32 count); - const Item* events() const noexcept { return events_.get(); } - const uint32_t count() const noexcept { return count_; } + NoteUpdate() = default; + NoteUpdate(const Item* items, uint32 count) : events_(items, items + count) {} + const Item* events() const noexcept { return events_.data(); } + const uint32 count() const noexcept { return static_cast(events_.size()); } + + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; OBJ_METHODS(NoteUpdate, FObject) private: - std::unique_ptr events_; - uint32_t count_ = 0; + std::vector events_; }; /** - * @brief Update which notifies a change of SFZ file. + * @brief Abstract update which notifies change of a certain file path. */ -class SfzUpdate : public Steinberg::FObject { +class FilePathUpdate : public Steinberg::FObject { +protected: + FilePathUpdate() = default; + public: void setPath(std::string newPath) { @@ -107,17 +115,46 @@ class SfzUpdate : public Steinberg::FObject { return path_; } - OBJ_METHODS(SfzUpdate, FObject) + OBJ_METHODS(FilePathUpdate, FObject) + +protected: + bool saveFilePathAttributes_(Vst::IAttributeList* attrs) const; + bool loadFilePathAttributes_(Vst::IAttributeList* attrs); private: std::string path_; mutable std::mutex mutex_; }; +/** + * @brief Update which notifies a change of SFZ file. + */ +class SfzUpdate : public FilePathUpdate, + public IConvertibleToMessage { +public: + OBJ_METHODS(SfzUpdate, FilePathUpdate) + + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; +}; + +/** + * @brief Update which notifies a change of scala file. + */ +class ScalaUpdate : public FilePathUpdate, + public IConvertibleToMessage { +public: + OBJ_METHODS(ScalaUpdate, FilePathUpdate) + + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; +}; + /** * @brief Update which notifies a change of SFZ description. */ -class SfzDescriptionUpdate : public Steinberg::FObject { +class SfzDescriptionUpdate : public Steinberg::FObject, + public IConvertibleToMessage { public: void setDescription(std::string newDescription) { @@ -131,6 +168,9 @@ class SfzDescriptionUpdate : public Steinberg::FObject { return description_; } + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; + OBJ_METHODS(SfzDescriptionUpdate, FObject) private: @@ -139,50 +179,62 @@ class SfzDescriptionUpdate : public Steinberg::FObject { }; /** - * @brief Update which notifies a change of scala file. + * @brief Update which indicates the playing SFZ status. */ -class ScalaUpdate : public Steinberg::FObject { +class PlayStateUpdate : public Steinberg::FObject, + public IConvertibleToMessage { public: - void setPath(std::string newPath) + void setState(SfizzPlayState newState) { std::lock_guard lock(mutex_); - path_ = std::move(newPath); + state_ = std::move(newState); } - std::string getPath() const + SfizzPlayState getState() const { std::lock_guard lock(mutex_); - return path_; + return state_; } - OBJ_METHODS(ScalaUpdate, FObject) + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + virtual bool loadFromAttributes(Vst::IAttributeList* attrs) override; + + OBJ_METHODS(PlayStateUpdate, FObject) private: - std::string path_; + SfizzPlayState state_ {}; mutable std::mutex mutex_; }; /** - * @brief Update which indicates the playing SFZ status. + * @brief Update which automates a pack of parameters */ -class PlayStateUpdate : public Steinberg::FObject { +class AutomationUpdate : public Steinberg::FObject, + public IConvertibleToMessage { public: - void setState(SfizzPlayState newState) + using Item = std::pair; + + AutomationUpdate() = default; + + void setItems(std::vector newItems) { std::lock_guard lock(mutex_); - state_ = std::move(newState); + items_ = std::move(newItems); } - SfizzPlayState getState() const + std::vector getItems() const { std::lock_guard lock(mutex_); - return state_; + return items_; } - OBJ_METHODS(PlayStateUpdate, FObject) + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; + + OBJ_METHODS(AutomationUpdate, FObject) private: - SfizzPlayState state_ {}; + std::vector items_; mutable std::mutex mutex_; }; diff --git a/plugins/vst/SfizzVstUpdates.hpp b/plugins/vst/SfizzVstUpdates.hpp index 48f9d32a2..5b1ce1f93 100644 --- a/plugins/vst/SfizzVstUpdates.hpp +++ b/plugins/vst/SfizzVstUpdates.hpp @@ -10,7 +10,7 @@ template IPtr IConvertibleToMessage::convertToMessage(Vst::ComponentBase* sender) const { - IPtr message = owned(sender->allocateMessage()); + IPtr message = Steinberg::owned(sender->allocateMessage()); if (!message) return nullptr; message->setMessageID(static_cast(this)->isA()); @@ -20,13 +20,22 @@ IPtr IConvertibleToMessage::convertToMessage(Vst::ComponentBas } template -IPtr IConvertibleToMessage::convertFromMessage(Vst::IMessage& message) +IPtr IConvertibleToMessage::createFromMessage(Vst::IMessage& message) { IPtr object; if (!strcmp(T::getFClassID(), message.getMessageID())) { - object = owned(new T); + object = Steinberg::owned(new T); if (!object->loadFromAttributes(message.getAttributes())) object = nullptr; } return object; } + +template +bool IConvertibleToMessage::convertFromMessage(Vst::IMessage& message) +{ + bool success = false; + if (!strcmp(T::getFClassID(), message.getMessageID())) + success = loadFromAttributes(message.getAttributes()); + return success; +} From 4ef382420916f9c10f5c27307d04579a14b23d86 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Thu, 29 Apr 2021 19:28:29 +0200 Subject: [PATCH 037/193] External for puredata --- CMakeLists.txt | 1 + cmake/PuredataConfig.cmake | 15 ++ plugins/CMakeLists.txt | 4 + plugins/puredata/CMakeLists.txt | 38 +++++ plugins/puredata/example.sfz | 22 +++ plugins/puredata/sfizz_puredata.c | 269 ++++++++++++++++++++++++++++++ plugins/puredata/sfizz~-help.pd | 46 +++++ 7 files changed, 395 insertions(+) create mode 100644 cmake/PuredataConfig.cmake create mode 100644 plugins/puredata/CMakeLists.txt create mode 100644 plugins/puredata/example.sfz create mode 100644 plugins/puredata/sfizz_puredata.c create mode 100644 plugins/puredata/sfizz~-help.pd diff --git a/CMakeLists.txt b/CMakeLists.txt index d39543437..6805f67dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ option_ex (SFIZZ_LV2_UI "Enable LV2 plug-in user interface" ON) option_ex (SFIZZ_VST "Enable VST plug-in build" ON) option_ex (SFIZZ_AU "Enable AU plug-in build" APPLE) option_ex (SFIZZ_VST2 "Enable VST2 plug-in build (unsupported)" OFF) +option_ex (SFIZZ_PUREDATA "Enable Puredata plug-in build" OFF) option_ex (SFIZZ_BENCHMARKS "Enable benchmarks build" OFF) option_ex (SFIZZ_TESTS "Enable tests build" OFF) option_ex (SFIZZ_DEMOS "Enable feature demos build" OFF) diff --git a/cmake/PuredataConfig.cmake b/cmake/PuredataConfig.cmake new file mode 100644 index 000000000..2cb764ffc --- /dev/null +++ b/cmake/PuredataConfig.cmake @@ -0,0 +1,15 @@ +find_path(PUREDATA_INCLUDE_DIR "m_pd.h" PATH_SUFFIXES "pd") + +if(NOT PUREDATA_INCLUDE_DIR) + message(FATAL_ERROR "Cannot find Puredata headers") +endif() + +message(STATUS "Puredata headers: ${PUREDATA_INCLUDE_DIR}") + +if(WIN32) + set(PUREDATA_SUFFIX ".dll") +elseif(APPLE) + set(PUREDATA_SUFFIX ".pd_darwin") +else() + set(PUREDATA_SUFFIX ".pd_linux") +endif() diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 3201b27d7..77f133748 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -58,3 +58,7 @@ endif() if(SFIZZ_VST OR SFIZZ_AU OR SFIZZ_VST2) add_subdirectory(vst) endif() + +if(SFIZZ_PUREDATA) + add_subdirectory(puredata) +endif() diff --git a/plugins/puredata/CMakeLists.txt b/plugins/puredata/CMakeLists.txt new file mode 100644 index 000000000..7372c863f --- /dev/null +++ b/plugins/puredata/CMakeLists.txt @@ -0,0 +1,38 @@ +# Puredata plugin specific settings +include(PuredataConfig) + +set(PUREDATA_BINARY_DIR "${PROJECT_BINARY_DIR}/pd/sfizz") + +set(PUREDATA_RESOURCES + "sfizz~-help.pd" + "example.sfz") + +function(copy_puredata_resources TARGET SOURCE_DIR DESTINATION_DIR) + set(_deps) + foreach(res ${PUREDATA_RESOURCES}) + get_filename_component(_dir "${res}" DIRECTORY) + file(MAKE_DIRECTORY "${DESTINATION_DIR}/${_dir}") + add_custom_command( + OUTPUT "${DESTINATION_DIR}/${res}" + COMMAND "${CMAKE_COMMAND}" "-E" "copy" + "${SOURCE_DIR}/${res}" "${DESTINATION_DIR}/${res}" + DEPENDS "${SOURCE_DIR}/${res}") + list(APPEND _deps "${DESTINATION_DIR}/${res}") + endforeach() + add_custom_target("${TARGET}_puredata_resources" DEPENDS ${_deps}) + add_dependencies("${TARGET}" "${TARGET}_puredata_resources") +endfunction() + +add_library(sfizz_puredata MODULE sfizz_puredata.c) +target_include_directories(sfizz_puredata PRIVATE "${PUREDATA_INCLUDE_DIR}") +target_link_libraries(sfizz_puredata PRIVATE sfizz::sfizz sfizz::spin_mutex) + +set_target_properties(sfizz_puredata PROPERTIES + PREFIX "" + SUFFIX "${PUREDATA_SUFFIX}" + OUTPUT_NAME "sfizz" + LIBRARY_OUTPUT_DIRECTORY "${PUREDATA_BINARY_DIR}/$<0:>") + +copy_puredata_resources(sfizz_puredata + "${CMAKE_CURRENT_SOURCE_DIR}" + "${PUREDATA_BINARY_DIR}") diff --git a/plugins/puredata/example.sfz b/plugins/puredata/example.sfz new file mode 100644 index 000000000..1ab726998 --- /dev/null +++ b/plugins/puredata/example.sfz @@ -0,0 +1,22 @@ + +ampeg_attack=0.25 +ampeg_decay=5 +ampeg_sustain=50 +ampeg_release=2 +fil_type=lpf_4p +resonance=3 +cutoff=2000 +oscillator_phase=-1 +cutoff_oncc300=3600 +cutoff_curvecc300=1 + + +sample=*saw +oscillator_multi=3 +oscillator_detune=10 +lfo1_freq=5 +lfo1_pitch=30 + + +sample=*triangle +transpose=-12 diff --git a/plugins/puredata/sfizz_puredata.c b/plugins/puredata/sfizz_puredata.c new file mode 100644 index 000000000..f1aafe69c --- /dev/null +++ b/plugins/puredata/sfizz_puredata.c @@ -0,0 +1,269 @@ +// 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 +#include +#include +#include +#include +#include +#include + +static t_class* cls_sfizz_tilde; + +typedef struct _sfizz_tilde { + t_object obj; + t_outlet* outputs[2]; + sfizz_synth_t* synth; + spin_mutex_t* mutex; + int midi[3]; + int midinum; + t_symbol* dir; + char* filepath; +} t_sfizz_tilde; + +static void sfizz_tilde_set_file(t_sfizz_tilde* self, const char* file) +{ + const char* dir = self->dir->s_name; + char* filepath; + if (file[0] != '\0') { + filepath = malloc(strlen(dir) + 1 + strlen(file) + 1); + sprintf(filepath, "%s/%s", dir, file); + } + else { + filepath = malloc(1); + *filepath = '\0'; + } + free(self->filepath); + self->filepath = filepath; +} + +static bool sfizz_tilde_do_load(t_sfizz_tilde* self) +{ + bool loaded; + spin_mutex_lock(self->mutex); + if (self->filepath[0] != '\0') + loaded = sfizz_load_file(self->synth, self->filepath); + else + loaded = sfizz_load_string(self->synth, "default.sfz", "sample=*sine"); + spin_mutex_unlock(self->mutex); + return loaded; +} + +static void* sfizz_tilde_new(t_symbol* sym, int argc, t_atom argv[]) +{ + (void)sym; + t_sfizz_tilde* self = (t_sfizz_tilde*)pd_new(cls_sfizz_tilde); + + const char* file; + if (argc == 0) + file = ""; + else if (argc == 1 && argv[0].a_type == A_SYMBOL) + file = argv[0].a_w.w_symbol->s_name; + else { + pd_free((t_pd*)self); + return NULL; + } + + self->dir = canvas_getcurrentdir(); + + self->outputs[0] = outlet_new(&self->obj, &s_signal); + self->outputs[1] = outlet_new(&self->obj, &s_signal); + + sfizz_synth_t* synth = sfizz_create_synth(); + self->synth = synth; + + self->mutex = spin_mutex_create(); + + sfizz_set_sample_rate(synth, sys_getsr()); + sfizz_set_samples_per_block(synth, sys_getblksize()); + + sfizz_tilde_set_file(self, file); + if (!sfizz_tilde_do_load(self)) { + pd_free((t_pd*)self); + return NULL; + } + + return self; +} + +static void sfizz_tilde_free(t_sfizz_tilde* self) +{ + if (self->filepath) + free(self->filepath); + if (self->synth) + sfizz_free(self->synth); + if (self->mutex) + spin_mutex_destroy(self->mutex); + if (self->outputs[0]) + outlet_free(self->outputs[0]); + if (self->outputs[1]) + outlet_free(self->outputs[1]); +} + +static t_int* sfizz_tilde_perform(t_int* w) +{ + t_sfizz_tilde* self; + t_sample* outputs[2]; + t_int nframes; + + w++; + self = (t_sfizz_tilde*)*w++; + outputs[0] = (t_sample*)*w++; + outputs[1] = (t_sample*)*w++; + nframes = (t_int)*w++; + + if (spin_mutex_trylock(self->mutex)) { + sfizz_render_block(self->synth, outputs, 2, nframes); + spin_mutex_unlock(self->mutex); + } + else { + memset(outputs[0], 0, nframes * sizeof(t_sample)); + memset(outputs[1], 0, nframes * sizeof(t_sample)); + } + + return w; +} + +static void sfizz_tilde_dsp(t_sfizz_tilde* self, t_signal** sp) +{ + dsp_add( + &sfizz_tilde_perform, 4, (t_int)self, + (t_int)sp[0]->s_vec, (t_int)sp[1]->s_vec, (t_int)sp[0]->s_n); +} + +static void sfizz_tilde_midiin(t_sfizz_tilde* self, t_float f) +{ + int byte = (int)f; + bool isstatus = (byte & 0x80) != 0; + + int* midi = self->midi; + int midinum = self->midinum; + + // + if (isstatus) { + midi[0] = byte; + midinum = 1; + } + else if (midinum != -1 && midinum < 3) + midi[midinum++] = byte; + else + midinum = -1; + + // + switch (midinum) { + case 2: + switch (midi[0] & 0xf0) { + case 0xd0: // channel aftertouch + if (spin_mutex_trylock(self->mutex)) { + sfizz_send_channel_aftertouch(self->synth, 0, midi[1]); + spin_mutex_unlock(self->mutex); + } + break; + } + break; + case 3: + switch (midi[0] & 0xf0) { + case 0x90: // note on + if (midi[2] == 0) + goto noteoff; + if (spin_mutex_trylock(self->mutex)) { + sfizz_send_note_on(self->synth, 0, midi[1], midi[2]); + spin_mutex_unlock(self->mutex); + } + break; + case 0x80: // note off + noteoff: + if (spin_mutex_trylock(self->mutex)) { + sfizz_send_note_off(self->synth, 0, midi[1], midi[2]); + spin_mutex_unlock(self->mutex); + } + break; + case 0xb0: // controller + if (spin_mutex_trylock(self->mutex)) { + sfizz_send_cc(self->synth, 0, midi[1], midi[2]); + spin_mutex_unlock(self->mutex); + } + break; + case 0xa0: // key aftertouch + if (spin_mutex_trylock(self->mutex)) { + sfizz_send_poly_aftertouch(self->synth, 0, midi[1], midi[2]); + spin_mutex_unlock(self->mutex); + } + break; + case 0xe0: // pitch bend + if (spin_mutex_trylock(self->mutex)) { + sfizz_send_pitch_wheel(self->synth, 0, (midi[1] + (midi[2] << 7)) - 8192); + spin_mutex_unlock(self->mutex); + } + break; + + } + break; + } + + self->midinum = midinum; +} + +static void sfizz_tilde_load(t_sfizz_tilde* self, t_symbol* sym) +{ + sfizz_tilde_set_file(self, sym->s_name); + sfizz_tilde_do_load(self); +} + +static void sfizz_tilde_reload(t_sfizz_tilde* self, t_float value) +{ + (void)value; + sfizz_tilde_do_load(self); +} + +static void sfizz_tilde_hdcc(t_sfizz_tilde* self, t_float cc, t_float value) +{ + if (spin_mutex_trylock(self->mutex)) { + sfizz_automate_hdcc(self->synth, 0, (int)cc, value); + spin_mutex_unlock(self->mutex); + } +} + +static void sfizz_tilde_voices(t_sfizz_tilde* self, t_float value) +{ + if (spin_mutex_trylock(self->mutex)) { + int numvoices = (int)value; + if (numvoices < 1) + numvoices = 1; + sfizz_set_num_voices(self->synth, numvoices); + spin_mutex_unlock(self->mutex); + } +} + +#if defined(_WIN32) +__declspec(dllexport) +#else +__attribute__((visibility("default"))) +#endif +void sfizz_setup() +{ + post("sfizz external for Puredata"); + + cls_sfizz_tilde = class_new( + gensym("sfizz~"), + (t_newmethod)&sfizz_tilde_new, + (t_method)&sfizz_tilde_free, + sizeof(t_sfizz_tilde), + CLASS_DEFAULT, A_GIMME, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_dsp, gensym("dsp"), A_CANT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_midiin, &s_float, A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_load, gensym("load"), A_DEFSYM, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_reload, gensym("reload"), A_DEFFLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_hdcc, gensym("hdcc"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_voices, gensym("voices"), A_FLOAT, A_NULL); +} diff --git a/plugins/puredata/sfizz~-help.pd b/plugins/puredata/sfizz~-help.pd new file mode 100644 index 000000000..96600cbac --- /dev/null +++ b/plugins/puredata/sfizz~-help.pd @@ -0,0 +1,46 @@ +#N canvas 932 395 659 405 12; +#X declare -lib sfizz; +#X obj 4 3 cnv 15 650 80 empty empty sfizz 20 30 0 40 -261682 -66577 +0; +#X text 45 54 A synthesizer for instruments in SFZ format; +#X obj 241 165 r sfizz-ctl; +#X text 33 167 create a SFZ instrument; +#X obj 29 324 midiin; +#X obj 29 349 s sfizz-ctl; +#X text 26 301 connect MIDI-in; +#X obj 162 223 dac~; +#X msg 180 346 voices \$1; +#X obj 180 371 s sfizz-ctl; +#X text 178 302 modify the polyphony; +#X obj 183 326 hsl 128 15 1 256 0 1 empty empty empty -2 -8 0 10 -262144 +-1 -1 3138 1; +#X floatatom 273 346 5 0 0 0 - - -; +#X obj 378 372 s sfizz-ctl; +#X obj 381 327 hsl 128 15 0 1 0 1 empty empty empty -2 -8 0 10 -262144 +-1 -1 6350 1; +#X floatatom 471 347 5 0 0 0 - - -; +#X text 377 303 modulate a parameter; +#X msg 378 347 hdcc 300 \$1; +#X obj 27 115 declare -lib sfizz; +#X obj 163 192 sfizz~ example.sfz; +#X text 18 93 load the sfizz library; +#X msg 221 115 \; pd dsp 1; +#X text 218 94 click to turn on DSP; +#X text 153 247 output stereo audio; +#X msg 366 223 reload; +#X obj 366 248 s sfizz-ctl; +#X obj 466 248 s sfizz-ctl; +#X msg 466 223 load example.sfz; +#X text 364 200 reload the instrument or load another; +#X connect 2 0 19 0; +#X connect 4 0 5 0; +#X connect 8 0 9 0; +#X connect 11 0 8 0; +#X connect 11 0 12 0; +#X connect 14 0 17 0; +#X connect 14 0 15 0; +#X connect 17 0 13 0; +#X connect 19 0 7 0; +#X connect 19 1 7 1; +#X connect 24 0 25 0; +#X connect 27 0 26 0; From e77444451ec6447984c1847d5097e45f1d4788f3 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 30 Apr 2021 00:55:33 +0200 Subject: [PATCH 038/193] pd: voices should always lock --- plugins/puredata/sfizz_puredata.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/puredata/sfizz_puredata.c b/plugins/puredata/sfizz_puredata.c index f1aafe69c..8637ecc6f 100644 --- a/plugins/puredata/sfizz_puredata.c +++ b/plugins/puredata/sfizz_puredata.c @@ -230,13 +230,13 @@ static void sfizz_tilde_hdcc(t_sfizz_tilde* self, t_float cc, t_float value) static void sfizz_tilde_voices(t_sfizz_tilde* self, t_float value) { - if (spin_mutex_trylock(self->mutex)) { - int numvoices = (int)value; - if (numvoices < 1) - numvoices = 1; - sfizz_set_num_voices(self->synth, numvoices); - spin_mutex_unlock(self->mutex); - } + int numvoices = (int)value; + if (numvoices < 1) + numvoices = 1; + + spin_mutex_lock(self->mutex); + sfizz_set_num_voices(self->synth, numvoices); + spin_mutex_unlock(self->mutex); } #if defined(_WIN32) From 96c0fb06908a333d846d621254f0baa4dd167bf9 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 30 Apr 2021 07:54:49 +0200 Subject: [PATCH 039/193] Remove sync because pd uses one thread only --- plugins/puredata/CMakeLists.txt | 2 +- plugins/puredata/sfizz_puredata.c | 54 +++++-------------------------- 2 files changed, 9 insertions(+), 47 deletions(-) diff --git a/plugins/puredata/CMakeLists.txt b/plugins/puredata/CMakeLists.txt index 7372c863f..e6495246a 100644 --- a/plugins/puredata/CMakeLists.txt +++ b/plugins/puredata/CMakeLists.txt @@ -25,7 +25,7 @@ endfunction() add_library(sfizz_puredata MODULE sfizz_puredata.c) target_include_directories(sfizz_puredata PRIVATE "${PUREDATA_INCLUDE_DIR}") -target_link_libraries(sfizz_puredata PRIVATE sfizz::sfizz sfizz::spin_mutex) +target_link_libraries(sfizz_puredata PRIVATE sfizz::sfizz) set_target_properties(sfizz_puredata PROPERTIES PREFIX "" diff --git a/plugins/puredata/sfizz_puredata.c b/plugins/puredata/sfizz_puredata.c index 8637ecc6f..89422f7ad 100644 --- a/plugins/puredata/sfizz_puredata.c +++ b/plugins/puredata/sfizz_puredata.c @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -18,7 +17,6 @@ typedef struct _sfizz_tilde { t_object obj; t_outlet* outputs[2]; sfizz_synth_t* synth; - spin_mutex_t* mutex; int midi[3]; int midinum; t_symbol* dir; @@ -44,12 +42,10 @@ static void sfizz_tilde_set_file(t_sfizz_tilde* self, const char* file) static bool sfizz_tilde_do_load(t_sfizz_tilde* self) { bool loaded; - spin_mutex_lock(self->mutex); if (self->filepath[0] != '\0') loaded = sfizz_load_file(self->synth, self->filepath); else loaded = sfizz_load_string(self->synth, "default.sfz", "sample=*sine"); - spin_mutex_unlock(self->mutex); return loaded; } @@ -76,8 +72,6 @@ static void* sfizz_tilde_new(t_symbol* sym, int argc, t_atom argv[]) sfizz_synth_t* synth = sfizz_create_synth(); self->synth = synth; - self->mutex = spin_mutex_create(); - sfizz_set_sample_rate(synth, sys_getsr()); sfizz_set_samples_per_block(synth, sys_getblksize()); @@ -96,8 +90,6 @@ static void sfizz_tilde_free(t_sfizz_tilde* self) free(self->filepath); if (self->synth) sfizz_free(self->synth); - if (self->mutex) - spin_mutex_destroy(self->mutex); if (self->outputs[0]) outlet_free(self->outputs[0]); if (self->outputs[1]) @@ -116,14 +108,7 @@ static t_int* sfizz_tilde_perform(t_int* w) outputs[1] = (t_sample*)*w++; nframes = (t_int)*w++; - if (spin_mutex_trylock(self->mutex)) { - sfizz_render_block(self->synth, outputs, 2, nframes); - spin_mutex_unlock(self->mutex); - } - else { - memset(outputs[0], 0, nframes * sizeof(t_sample)); - memset(outputs[1], 0, nframes * sizeof(t_sample)); - } + sfizz_render_block(self->synth, outputs, 2, nframes); return w; } @@ -158,10 +143,7 @@ static void sfizz_tilde_midiin(t_sfizz_tilde* self, t_float f) case 2: switch (midi[0] & 0xf0) { case 0xd0: // channel aftertouch - if (spin_mutex_trylock(self->mutex)) { - sfizz_send_channel_aftertouch(self->synth, 0, midi[1]); - spin_mutex_unlock(self->mutex); - } + sfizz_send_channel_aftertouch(self->synth, 0, midi[1]); break; } break; @@ -170,35 +152,20 @@ static void sfizz_tilde_midiin(t_sfizz_tilde* self, t_float f) case 0x90: // note on if (midi[2] == 0) goto noteoff; - if (spin_mutex_trylock(self->mutex)) { - sfizz_send_note_on(self->synth, 0, midi[1], midi[2]); - spin_mutex_unlock(self->mutex); - } + sfizz_send_note_on(self->synth, 0, midi[1], midi[2]); break; case 0x80: // note off noteoff: - if (spin_mutex_trylock(self->mutex)) { - sfizz_send_note_off(self->synth, 0, midi[1], midi[2]); - spin_mutex_unlock(self->mutex); - } + sfizz_send_note_off(self->synth, 0, midi[1], midi[2]); break; case 0xb0: // controller - if (spin_mutex_trylock(self->mutex)) { - sfizz_send_cc(self->synth, 0, midi[1], midi[2]); - spin_mutex_unlock(self->mutex); - } + sfizz_send_cc(self->synth, 0, midi[1], midi[2]); break; case 0xa0: // key aftertouch - if (spin_mutex_trylock(self->mutex)) { - sfizz_send_poly_aftertouch(self->synth, 0, midi[1], midi[2]); - spin_mutex_unlock(self->mutex); - } + sfizz_send_poly_aftertouch(self->synth, 0, midi[1], midi[2]); break; case 0xe0: // pitch bend - if (spin_mutex_trylock(self->mutex)) { - sfizz_send_pitch_wheel(self->synth, 0, (midi[1] + (midi[2] << 7)) - 8192); - spin_mutex_unlock(self->mutex); - } + sfizz_send_pitch_wheel(self->synth, 0, (midi[1] + (midi[2] << 7)) - 8192); break; } @@ -222,10 +189,7 @@ static void sfizz_tilde_reload(t_sfizz_tilde* self, t_float value) static void sfizz_tilde_hdcc(t_sfizz_tilde* self, t_float cc, t_float value) { - if (spin_mutex_trylock(self->mutex)) { - sfizz_automate_hdcc(self->synth, 0, (int)cc, value); - spin_mutex_unlock(self->mutex); - } + sfizz_automate_hdcc(self->synth, 0, (int)cc, value); } static void sfizz_tilde_voices(t_sfizz_tilde* self, t_float value) @@ -234,9 +198,7 @@ static void sfizz_tilde_voices(t_sfizz_tilde* self, t_float value) if (numvoices < 1) numvoices = 1; - spin_mutex_lock(self->mutex); sfizz_set_num_voices(self->synth, numvoices); - spin_mutex_unlock(self->mutex); } #if defined(_WIN32) From b79308a37f578b0afb24a10316d107aab453c482 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 30 Apr 2021 08:08:52 +0200 Subject: [PATCH 040/193] Use a fallback puredata header --- cmake/PuredataConfig.cmake | 9 +- plugins/puredata/external/pd/LICENSE.txt | 30 + plugins/puredata/external/pd/include/m_pd.h | 911 ++++++++++++++++++++ 3 files changed, 946 insertions(+), 4 deletions(-) create mode 100644 plugins/puredata/external/pd/LICENSE.txt create mode 100644 plugins/puredata/external/pd/include/m_pd.h diff --git a/cmake/PuredataConfig.cmake b/cmake/PuredataConfig.cmake index 2cb764ffc..eb794d1d1 100644 --- a/cmake/PuredataConfig.cmake +++ b/cmake/PuredataConfig.cmake @@ -1,11 +1,12 @@ find_path(PUREDATA_INCLUDE_DIR "m_pd.h" PATH_SUFFIXES "pd") -if(NOT PUREDATA_INCLUDE_DIR) - message(FATAL_ERROR "Cannot find Puredata headers") +if(PUREDATA_INCLUDE_DIR) + message(STATUS "Puredata headers: ${PUREDATA_INCLUDE_DIR}") +else() + message(STATUS "Puredata headers not found, using our own") + set(PUREDATA_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/plugins/puredata/external/pd/include") endif() -message(STATUS "Puredata headers: ${PUREDATA_INCLUDE_DIR}") - if(WIN32) set(PUREDATA_SUFFIX ".dll") elseif(APPLE) diff --git a/plugins/puredata/external/pd/LICENSE.txt b/plugins/puredata/external/pd/LICENSE.txt new file mode 100644 index 000000000..a56a51eb5 --- /dev/null +++ b/plugins/puredata/external/pd/LICENSE.txt @@ -0,0 +1,30 @@ +This software is copyrighted by Miller Puckette and others. The following +terms (the "Standard Improved BSD License") apply to all files associated with +the software unless explicitly disclaimed in individual files: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. +3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plugins/puredata/external/pd/include/m_pd.h b/plugins/puredata/external/pd/include/m_pd.h new file mode 100644 index 000000000..1bcbfff49 --- /dev/null +++ b/plugins/puredata/external/pd/include/m_pd.h @@ -0,0 +1,911 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#ifndef __m_pd_h_ + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +extern "C" { +#endif + +#define PD_MAJOR_VERSION 0 +#define PD_MINOR_VERSION 51 +#define PD_BUGFIX_VERSION 4 +#define PD_TEST_VERSION "" +extern int pd_compatibilitylevel; /* e.g., 43 for pd 0.43 compatibility */ + +/* old name for "MSW" flag -- we have to take it for the sake of many old +"nmakefiles" for externs, which will define NT and not MSW */ +#if defined(NT) && !defined(MSW) +#define MSW +#endif + +/* These pragmas are only used for MSVC, not MinGW or Cygwin */ +#ifdef _MSC_VER +/* #pragma warning( disable : 4091 ) */ +#pragma warning( disable : 4305 ) /* uncast const double to float */ +#pragma warning( disable : 4244 ) /* uncast float/int conversion etc. */ +#pragma warning( disable : 4101 ) /* unused automatic variables */ +#endif /* _MSC_VER */ + + /* the external storage class is "extern" in UNIX; in MSW it's ugly. */ +#ifdef _WIN32 +#ifdef PD_INTERNAL +#define EXTERN __declspec(dllexport) extern +#else +#define EXTERN __declspec(dllimport) extern +#endif /* PD_INTERNAL */ +#else +#define EXTERN extern +#endif /* _WIN32 */ + + /* On most c compilers, you can just say "struct foo;" to declare a + structure whose elements are defined elsewhere. On MSVC, when compiling + C (but not C++) code, you have to say "extern struct foo;". So we make + a stupid macro: */ +#if defined(_MSC_VER) && !defined(_LANGUAGE_C_PLUS_PLUS) \ + && !defined(__cplusplus) +#define EXTERN_STRUCT extern struct +#else +#define EXTERN_STRUCT struct +#endif + +/* Define some attributes, specific to the compiler */ +#if defined(__GNUC__) +#define ATTRIBUTE_FORMAT_PRINTF(a, b) __attribute__ ((format (printf, a, b))) +#else +#define ATTRIBUTE_FORMAT_PRINTF(a, b) +#endif + +#if !defined(_SIZE_T) && !defined(_SIZE_T_) +#include /* just for size_t -- how lame! */ +#endif + +/* Microsoft Visual Studio is not C99, but since VS2015 has included most C99 headers: + https://docs.microsoft.com/en-us/previous-versions/hh409293(v=vs.140)#c-runtime-library + These definitions recreate stdint.h types, but only in pre-2015 Visual Studio: */ +#if defined(_MSC_VER) && _MSC_VER < 1900 +typedef signed __int8 int8_t; +typedef signed __int16 int16_t; +typedef signed __int32 int32_t; +typedef signed __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#else +# include +#endif + +/* for FILE, needed by sys_fopen() and sys_fclose() only */ +#include + +#define MAXPDSTRING 1000 /* use this for anything you want */ +#define MAXPDARG 5 /* max number of args we can typecheck today */ + +/* signed and unsigned integer types the size of a pointer: */ +#if !defined(PD_LONGINTTYPE) +#if defined(_WIN32) && defined(_WIN64) +#define PD_LONGINTTYPE long long +#else +#define PD_LONGINTTYPE long +#endif +#endif + +#if !defined(PD_FLOATSIZE) + /* normally, our floats (t_float, t_sample,...) are 32bit */ +# define PD_FLOATSIZE 32 +#endif + +#if PD_FLOATSIZE == 32 +# define PD_FLOATTYPE float +/* an unsigned int of the same size as FLOATTYPE: */ +# define PD_FLOATUINTTYPE uint32_t + +#elif PD_FLOATSIZE == 64 +# define PD_FLOATTYPE double +# define PD_FLOATUINTTYPE uint64_t +#else +# error invalid FLOATSIZE: must be 32 or 64 +#endif + +typedef PD_LONGINTTYPE t_int; /* pointer-size integer */ +typedef PD_FLOATTYPE t_float; /* a float type at most the same size */ +typedef PD_FLOATTYPE t_floatarg; /* float type for function calls */ + +typedef struct _symbol +{ + const char *s_name; + struct _class **s_thing; + struct _symbol *s_next; +} t_symbol; + +EXTERN_STRUCT _array; +#define t_array struct _array /* g_canvas.h */ + +/* pointers to glist and array elements go through a "stub" which sticks +around after the glist or array is freed. The stub itself is deleted when +both the glist/array is gone and the refcount is zero, ensuring that no +gpointers are pointing here. */ + +#define GP_NONE 0 /* the stub points nowhere (has been cut off) */ +#define GP_GLIST 1 /* the stub points to a glist element */ +#define GP_ARRAY 2 /* ... or array */ + +typedef struct _gstub +{ + union + { + struct _glist *gs_glist; /* glist we're in */ + struct _array *gs_array; /* array we're in */ + } gs_un; + int gs_which; /* GP_GLIST/GP_ARRAY */ + int gs_refcount; /* number of gpointers pointing here */ +} t_gstub; + +typedef struct _gpointer /* pointer to a gobj in a glist */ +{ + union + { + struct _scalar *gp_scalar; /* scalar we're in (if glist) */ + union word *gp_w; /* raw data (if array) */ + } gp_un; + int gp_valid; /* number which must match gpointee */ + t_gstub *gp_stub; /* stub which points to glist/array */ +} t_gpointer; + +typedef union word +{ + t_float w_float; + t_symbol *w_symbol; + t_gpointer *w_gpointer; + t_array *w_array; + struct _binbuf *w_binbuf; + int w_index; +} t_word; + +typedef enum +{ + A_NULL, + A_FLOAT, + A_SYMBOL, + A_POINTER, + A_SEMI, + A_COMMA, + A_DEFFLOAT, + A_DEFSYM, + A_DOLLAR, + A_DOLLSYM, + A_GIMME, + A_CANT +} t_atomtype; + +#define A_DEFSYMBOL A_DEFSYM /* better name for this */ + +typedef struct _atom +{ + t_atomtype a_type; + union word a_w; +} t_atom; + +EXTERN_STRUCT _class; +#define t_class struct _class + +EXTERN_STRUCT _outlet; +#define t_outlet struct _outlet + +EXTERN_STRUCT _inlet; +#define t_inlet struct _inlet + +EXTERN_STRUCT _binbuf; +#define t_binbuf struct _binbuf + +EXTERN_STRUCT _clock; +#define t_clock struct _clock + +EXTERN_STRUCT _outconnect; +#define t_outconnect struct _outconnect + +EXTERN_STRUCT _glist; +#define t_glist struct _glist +#define t_canvas struct _glist /* LATER lose this */ + +EXTERN_STRUCT _template; + +typedef t_class *t_pd; /* pure datum: nothing but a class pointer */ + +typedef struct _gobj /* a graphical object */ +{ + t_pd g_pd; /* pure datum header (class) */ + struct _gobj *g_next; /* next in list */ +} t_gobj; + +typedef struct _scalar /* a graphical object holding data */ +{ + t_gobj sc_gobj; /* header for graphical object */ + t_symbol *sc_template; /* template name (LATER replace with pointer) */ + t_word sc_vec[1]; /* indeterminate-length array of words */ +} t_scalar; + +typedef struct _text /* patchable object - graphical, with text */ +{ + t_gobj te_g; /* header for graphical object */ + t_binbuf *te_binbuf; /* holder for the text */ + t_outlet *te_outlet; /* linked list of outlets */ + t_inlet *te_inlet; /* linked list of inlets */ + short te_xpix; /* x&y location (within the toplevel) */ + short te_ypix; + short te_width; /* requested width in chars, 0 if auto */ + unsigned int te_type:2; /* from defs below */ +} t_text; + +#define T_TEXT 0 /* just a textual comment */ +#define T_OBJECT 1 /* a MAX style patchable object */ +#define T_MESSAGE 2 /* a MAX type message */ +#define T_ATOM 3 /* a cell to display a number or symbol */ + +#define te_pd te_g.g_pd + + /* t_object is synonym for t_text (LATER unify them) */ + +typedef struct _text t_object; + +#define ob_outlet te_outlet +#define ob_inlet te_inlet +#define ob_binbuf te_binbuf +#define ob_pd te_g.g_pd +#define ob_g te_g + +typedef void (*t_method)(void); +typedef void *(*t_newmethod)(void); + +/* in ARM 64 a varargs prototype generates a different function call sequence +from a fixed one, so in that special case we make a more restrictive +definition for t_gotfn. This will break some code in the "chaos" package +in Pd extended. (that code will run incorrectly anyhow so why not catch it +at compile time anyhow.) */ +#if defined(__APPLE__) && defined(__aarch64__) +typedef void (*t_gotfn)(void *x); +#else +typedef void (*t_gotfn)(void *x, ...); +#endif + +/* ---------------- pre-defined objects and symbols --------------*/ +EXTERN t_pd pd_objectmaker; /* factory for creating "object" boxes */ +EXTERN t_pd pd_canvasmaker; /* factory for creating canvases */ + +/* --------- prototypes from the central message system ----------- */ +EXTERN void pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv); +EXTERN void pd_forwardmess(t_pd *x, int argc, t_atom *argv); +EXTERN t_symbol *gensym(const char *s); +EXTERN t_gotfn getfn(const t_pd *x, t_symbol *s); +EXTERN t_gotfn zgetfn(const t_pd *x, t_symbol *s); +EXTERN void nullfn(void); +EXTERN void pd_vmess(t_pd *x, t_symbol *s, const char *fmt, ...); + +/* the following macros are for sending non-type-checkable messages, i.e., +using function lookup but circumventing type checking on arguments. Only +use for internal messaging protected by A_CANT so that the message can't +be generated at patch level. */ +#define mess0(x, s) ((*getfn((x), (s)))((x))) +typedef void (*t_gotfn1)(void *x, void *arg1); +#define mess1(x, s, a) ((*(t_gotfn1)getfn((x), (s)))((x), (a))) +typedef void (*t_gotfn2)(void *x, void *arg1, void *arg2); +#define mess2(x, s, a,b) ((*(t_gotfn2)getfn((x), (s)))((x), (a),(b))) +typedef void (*t_gotfn3)(void *x, void *arg1, void *arg2, void *arg3); +#define mess3(x, s, a,b,c) ((*(t_gotfn3)getfn((x), (s)))((x), (a),(b),(c))) +typedef void (*t_gotfn4)(void *x, + void *arg1, void *arg2, void *arg3, void *arg4); +#define mess4(x, s, a,b,c,d) \ + ((*(t_gotfn4)getfn((x), (s)))((x), (a),(b),(c),(d))) +typedef void (*t_gotfn5)(void *x, + void *arg1, void *arg2, void *arg3, void *arg4, void *arg5); +#define mess5(x, s, a,b,c,d,e) \ + ((*(t_gotfn5)getfn((x), (s)))((x), (a),(b),(c),(d),(e))) + +EXTERN void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv); +EXTERN t_pd *pd_newest(void); + +/* --------------- memory management -------------------- */ +EXTERN void *getbytes(size_t nbytes); +EXTERN void *getzbytes(size_t nbytes); +EXTERN void *copybytes(const void *src, size_t nbytes); +EXTERN void freebytes(void *x, size_t nbytes); +EXTERN void *resizebytes(void *x, size_t oldsize, size_t newsize); + +/* -------------------- atoms ----------------------------- */ + +#define SETSEMI(atom) ((atom)->a_type = A_SEMI, (atom)->a_w.w_index = 0) +#define SETCOMMA(atom) ((atom)->a_type = A_COMMA, (atom)->a_w.w_index = 0) +#define SETPOINTER(atom, gp) ((atom)->a_type = A_POINTER, \ + (atom)->a_w.w_gpointer = (gp)) +#define SETFLOAT(atom, f) ((atom)->a_type = A_FLOAT, (atom)->a_w.w_float = (f)) +#define SETSYMBOL(atom, s) ((atom)->a_type = A_SYMBOL, \ + (atom)->a_w.w_symbol = (s)) +#define SETDOLLAR(atom, n) ((atom)->a_type = A_DOLLAR, \ + (atom)->a_w.w_index = (n)) +#define SETDOLLSYM(atom, s) ((atom)->a_type = A_DOLLSYM, \ + (atom)->a_w.w_symbol= (s)) + +EXTERN t_float atom_getfloat(const t_atom *a); +EXTERN t_int atom_getint(const t_atom *a); +EXTERN t_symbol *atom_getsymbol(const t_atom *a); +EXTERN t_symbol *atom_gensym(const t_atom *a); +EXTERN t_float atom_getfloatarg(int which, int argc, const t_atom *argv); +EXTERN t_int atom_getintarg(int which, int argc, const t_atom *argv); +EXTERN t_symbol *atom_getsymbolarg(int which, int argc, const t_atom *argv); + +EXTERN void atom_string(const t_atom *a, char *buf, unsigned int bufsize); + +/* ------------------ binbufs --------------- */ + +EXTERN t_binbuf *binbuf_new(void); +EXTERN void binbuf_free(t_binbuf *x); +EXTERN t_binbuf *binbuf_duplicate(const t_binbuf *y); + +EXTERN void binbuf_text(t_binbuf *x, const char *text, size_t size); +EXTERN void binbuf_gettext(const t_binbuf *x, char **bufp, int *lengthp); +EXTERN void binbuf_clear(t_binbuf *x); +EXTERN void binbuf_add(t_binbuf *x, int argc, const t_atom *argv); +EXTERN void binbuf_addv(t_binbuf *x, const char *fmt, ...); +EXTERN void binbuf_addbinbuf(t_binbuf *x, const t_binbuf *y); +EXTERN void binbuf_addsemi(t_binbuf *x); +EXTERN void binbuf_restore(t_binbuf *x, int argc, const t_atom *argv); +EXTERN void binbuf_print(const t_binbuf *x); +EXTERN int binbuf_getnatom(const t_binbuf *x); +EXTERN t_atom *binbuf_getvec(const t_binbuf *x); +EXTERN int binbuf_resize(t_binbuf *x, int newsize); +EXTERN void binbuf_eval(const t_binbuf *x, t_pd *target, int argc, const t_atom *argv); +EXTERN int binbuf_read(t_binbuf *b, const char *filename, const char *dirname, + int crflag); +EXTERN int binbuf_read_via_canvas(t_binbuf *b, const char *filename, const t_canvas *canvas, + int crflag); +EXTERN int binbuf_read_via_path(t_binbuf *b, const char *filename, const char *dirname, + int crflag); +EXTERN int binbuf_write(const t_binbuf *x, const char *filename, const char *dir, + int crflag); +EXTERN void binbuf_evalfile(t_symbol *name, t_symbol *dir); +EXTERN t_symbol *binbuf_realizedollsym(t_symbol *s, int ac, const t_atom *av, + int tonew); + +/* ------------------ clocks --------------- */ + +EXTERN t_clock *clock_new(void *owner, t_method fn); +EXTERN void clock_set(t_clock *x, double systime); +EXTERN void clock_delay(t_clock *x, double delaytime); +EXTERN void clock_unset(t_clock *x); +EXTERN void clock_setunit(t_clock *x, double timeunit, int sampflag); +EXTERN double clock_getlogicaltime(void); +EXTERN double clock_getsystime(void); /* OBSOLETE; use clock_getlogicaltime() */ +EXTERN double clock_gettimesince(double prevsystime); +EXTERN double clock_gettimesincewithunits(double prevsystime, + double units, int sampflag); +EXTERN double clock_getsystimeafter(double delaytime); +EXTERN void clock_free(t_clock *x); + +/* ----------------- pure data ---------------- */ +EXTERN t_pd *pd_new(t_class *cls); +EXTERN void pd_free(t_pd *x); +EXTERN void pd_bind(t_pd *x, t_symbol *s); +EXTERN void pd_unbind(t_pd *x, t_symbol *s); +EXTERN t_pd *pd_findbyclass(t_symbol *s, const t_class *c); +EXTERN void pd_pushsym(t_pd *x); +EXTERN void pd_popsym(t_pd *x); +EXTERN void pd_bang(t_pd *x); +EXTERN void pd_pointer(t_pd *x, t_gpointer *gp); +EXTERN void pd_float(t_pd *x, t_float f); +EXTERN void pd_symbol(t_pd *x, t_symbol *s); +EXTERN void pd_list(t_pd *x, t_symbol *s, int argc, t_atom *argv); +EXTERN void pd_anything(t_pd *x, t_symbol *s, int argc, t_atom *argv); +#define pd_class(x) (*(x)) + +/* ----------------- pointers ---------------- */ +EXTERN void gpointer_init(t_gpointer *gp); +EXTERN void gpointer_copy(const t_gpointer *gpfrom, t_gpointer *gpto); +EXTERN void gpointer_unset(t_gpointer *gp); +EXTERN int gpointer_check(const t_gpointer *gp, int headok); + +/* ----------------- patchable "objects" -------------- */ +EXTERN t_inlet *inlet_new(t_object *owner, t_pd *dest, t_symbol *s1, + t_symbol *s2); +EXTERN t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp); +EXTERN t_inlet *floatinlet_new(t_object *owner, t_float *fp); +EXTERN t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp); +EXTERN t_inlet *signalinlet_new(t_object *owner, t_float f); +EXTERN void inlet_free(t_inlet *x); + +EXTERN t_outlet *outlet_new(t_object *owner, t_symbol *s); +EXTERN void outlet_bang(t_outlet *x); +EXTERN void outlet_pointer(t_outlet *x, t_gpointer *gp); +EXTERN void outlet_float(t_outlet *x, t_float f); +EXTERN void outlet_symbol(t_outlet *x, t_symbol *s); +EXTERN void outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv); +EXTERN void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv); +EXTERN t_symbol *outlet_getsymbol(t_outlet *x); +EXTERN void outlet_free(t_outlet *x); +EXTERN t_object *pd_checkobject(t_pd *x); + + +/* -------------------- canvases -------------- */ + +EXTERN void glob_setfilename(void *dummy, t_symbol *name, t_symbol *dir); + +EXTERN void canvas_setargs(int argc, const t_atom *argv); +EXTERN void canvas_getargs(int *argcp, t_atom **argvp); +EXTERN t_symbol *canvas_getcurrentdir(void); +EXTERN t_glist *canvas_getcurrent(void); +EXTERN void canvas_makefilename(const t_glist *c, const char *file, + char *result, int resultsize); +EXTERN t_symbol *canvas_getdir(const t_glist *x); +EXTERN char sys_font[]; /* default typeface set in s_main.c */ +EXTERN char sys_fontweight[]; /* default font weight set in s_main.c */ +EXTERN int sys_hostfontsize(int fontsize, int zoom); +EXTERN int sys_zoomfontwidth(int fontsize, int zoom, int worstcase); +EXTERN int sys_zoomfontheight(int fontsize, int zoom, int worstcase); +EXTERN int sys_fontwidth(int fontsize); +EXTERN int sys_fontheight(int fontsize); +EXTERN void canvas_dataproperties(t_glist *x, t_scalar *sc, t_binbuf *b); +EXTERN int canvas_open(const t_canvas *x, const char *name, const char *ext, + char *dirresult, char **nameresult, unsigned int size, int bin); + +/* ---------------- widget behaviors ---------------------- */ + +EXTERN_STRUCT _widgetbehavior; +#define t_widgetbehavior struct _widgetbehavior + +EXTERN_STRUCT _parentwidgetbehavior; +#define t_parentwidgetbehavior struct _parentwidgetbehavior +EXTERN const t_parentwidgetbehavior *pd_getparentwidget(t_pd *x); + +/* -------------------- classes -------------- */ + +#define CLASS_DEFAULT 0 /* flags for new classes below */ +#define CLASS_PD 1 +#define CLASS_GOBJ 2 +#define CLASS_PATCHABLE 3 +#define CLASS_NOINLET 8 + +#define CLASS_TYPEMASK 3 + +EXTERN t_class *class_new(t_symbol *name, t_newmethod newmethod, + t_method freemethod, size_t size, int flags, t_atomtype arg1, ...); + +EXTERN t_class *class_new64(t_symbol *name, t_newmethod newmethod, + t_method freemethod, size_t size, int flags, t_atomtype arg1, ...); + +EXTERN void class_free(t_class *c); + +#ifdef PDINSTANCE +EXTERN t_class *class_getfirst(void); +#endif + +EXTERN void class_addcreator(t_newmethod newmethod, t_symbol *s, + t_atomtype type1, ...); +EXTERN void class_addmethod(t_class *c, t_method fn, t_symbol *sel, + t_atomtype arg1, ...); +EXTERN void class_addbang(t_class *c, t_method fn); +EXTERN void class_addpointer(t_class *c, t_method fn); +EXTERN void class_doaddfloat(t_class *c, t_method fn); +EXTERN void class_addsymbol(t_class *c, t_method fn); +EXTERN void class_addlist(t_class *c, t_method fn); +EXTERN void class_addanything(t_class *c, t_method fn); +EXTERN void class_sethelpsymbol(t_class *c, t_symbol *s); +EXTERN void class_setwidget(t_class *c, const t_widgetbehavior *w); +EXTERN void class_setparentwidget(t_class *c, const t_parentwidgetbehavior *w); +EXTERN const char *class_getname(const t_class *c); +EXTERN const char *class_gethelpname(const t_class *c); +EXTERN const char *class_gethelpdir(const t_class *c); +EXTERN void class_setdrawcommand(t_class *c); +EXTERN int class_isdrawcommand(const t_class *c); +EXTERN void class_domainsignalin(t_class *c, int onset); +EXTERN void class_set_extern_dir(t_symbol *s); +#define CLASS_MAINSIGNALIN(c, type, field) \ + class_domainsignalin(c, (char *)(&((type *)0)->field) - (char *)0) + + /* prototype for functions to save Pd's to a binbuf */ +typedef void (*t_savefn)(t_gobj *x, t_binbuf *b); +EXTERN void class_setsavefn(t_class *c, t_savefn f); +EXTERN t_savefn class_getsavefn(const t_class *c); +EXTERN void obj_saveformat(const t_object *x, t_binbuf *bb); /* add format to bb */ + + /* prototype for functions to open properties dialogs */ +typedef void (*t_propertiesfn)(t_gobj *x, struct _glist *glist); +EXTERN void class_setpropertiesfn(t_class *c, t_propertiesfn f); +EXTERN t_propertiesfn class_getpropertiesfn(const t_class *c); + +typedef void (*t_classfreefn)(t_class *); +EXTERN void class_setfreefn(t_class *c, t_classfreefn fn); + +#ifndef PD_CLASS_DEF +#define class_addbang(x, y) class_addbang((x), (t_method)(y)) +#define class_addpointer(x, y) class_addpointer((x), (t_method)(y)) +#define class_addfloat(x, y) class_doaddfloat((x), (t_method)(y)) +#define class_addsymbol(x, y) class_addsymbol((x), (t_method)(y)) +#define class_addlist(x, y) class_addlist((x), (t_method)(y)) +#define class_addanything(x, y) class_addanything((x), (t_method)(y)) +#endif + +#if PD_FLOATSIZE == 64 +# define class_new class_new64 +#endif + +/* ------------ printing --------------------------------- */ +EXTERN void post(const char *fmt, ...); +EXTERN void startpost(const char *fmt, ...); +EXTERN void poststring(const char *s); +EXTERN void postfloat(t_floatarg f); +EXTERN void postatom(int argc, const t_atom *argv); +EXTERN void endpost(void); +EXTERN void error(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2); +EXTERN void verbose(int level, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(2, 3); +EXTERN void bug(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2); +EXTERN void pd_error(const void *object, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(2, 3); +EXTERN void logpost(const void *object, const int level, const char *fmt, ...) + ATTRIBUTE_FORMAT_PRINTF(3, 4); + +/* ------------ system interface routines ------------------- */ +EXTERN int sys_isabsolutepath(const char *dir); +EXTERN void sys_bashfilename(const char *from, char *to); +EXTERN void sys_unbashfilename(const char *from, char *to); +EXTERN int open_via_path(const char *dir, const char *name, const char *ext, + char *dirresult, char **nameresult, unsigned int size, int bin); +EXTERN int sched_geteventno(void); +EXTERN double sys_getrealtime(void); +EXTERN int (*sys_idlehook)(void); /* hook to add idle time computation */ + +/* Win32's open()/fopen() do not handle UTF-8 filenames so we need + * these internal versions that handle UTF-8 filenames the same across + * all platforms. They are recommended for use in external + * objectclasses as well so they work with Unicode filenames on Windows */ +EXTERN int sys_open(const char *path, int oflag, ...); +EXTERN int sys_close(int fd); +EXTERN FILE *sys_fopen(const char *filename, const char *mode); +EXTERN int sys_fclose(FILE *stream); + +/* ------------ threading ------------------- */ +EXTERN void sys_lock(void); +EXTERN void sys_unlock(void); +EXTERN int sys_trylock(void); + + +/* --------------- signals ----------------------------------- */ + +typedef PD_FLOATTYPE t_sample; +typedef union _sampleint_union { + t_sample f; + PD_FLOATUINTTYPE i; +} t_sampleint_union; +#define MAXLOGSIG 32 +#define MAXSIGSIZE (1 << MAXLOGSIG) + +typedef struct _signal +{ + int s_n; /* number of points in the array */ + t_sample *s_vec; /* the array */ + t_float s_sr; /* sample rate */ + int s_refcount; /* number of times used */ + int s_isborrowed; /* whether we're going to borrow our array */ + struct _signal *s_borrowedfrom; /* signal to borrow it from */ + struct _signal *s_nextfree; /* next in freelist */ + struct _signal *s_nextused; /* next in used list */ + int s_vecsize; /* allocated size of array in points */ +} t_signal; + +typedef t_int *(*t_perfroutine)(t_int *args); + +EXTERN t_int *plus_perform(t_int *args); +EXTERN t_int *zero_perform(t_int *args); +EXTERN t_int *copy_perform(t_int *args); + +EXTERN void dsp_add_plus(t_sample *in1, t_sample *in2, t_sample *out, int n); +EXTERN void dsp_add_copy(t_sample *in, t_sample *out, int n); +EXTERN void dsp_add_scalarcopy(t_float *in, t_sample *out, int n); +EXTERN void dsp_add_zero(t_sample *out, int n); + +EXTERN int sys_getblksize(void); +EXTERN t_float sys_getsr(void); +EXTERN int sys_get_inchannels(void); +EXTERN int sys_get_outchannels(void); + +EXTERN void dsp_add(t_perfroutine f, int n, ...); +EXTERN void dsp_addv(t_perfroutine f, int n, t_int *vec); +EXTERN void pd_fft(t_float *buf, int npoints, int inverse); +EXTERN int ilog2(int n); + +EXTERN void mayer_fht(t_sample *fz, int n); +EXTERN void mayer_fft(int n, t_sample *real, t_sample *imag); +EXTERN void mayer_ifft(int n, t_sample *real, t_sample *imag); +EXTERN void mayer_realfft(int n, t_sample *real); +EXTERN void mayer_realifft(int n, t_sample *real); + +EXTERN float *cos_table; +#define LOGCOSTABSIZE 9 +#define COSTABSIZE (1<> 1) & 0x20000000)); +} + +#elif PD_FLOATSIZE == 64 + +typedef union +{ + t_float f; + unsigned int ui[2]; +}t_bigorsmall64; + +static inline int PD_BADFLOAT(t_float f) /* malformed double */ +{ + t_bigorsmall64 pun; + pun.f = f; + pun.ui[1] &= 0x7ff00000; + return((pun.ui[1] == 0) | (pun.ui[1] == 0x7ff00000)); +} + +static inline int PD_BIGORSMALL(t_float f) /* exponent outside (-512,512) */ +{ + t_bigorsmall64 pun; + pun.f = f; + return((pun.ui[1] & 0x20000000) == ((pun.ui[1] >> 1) & 0x20000000)); +} + +#endif /* PD_FLOATSIZE */ +#else /* not INTEL or ARM */ +#define PD_BADFLOAT(f) 0 +#define PD_BIGORSMALL(f) 0 +#endif + +#else /* _MSC_VER */ +#if PD_FLOATSIZE == 32 +#define PD_BADFLOAT(f) ((((*(unsigned int*)&(f))&0x7f800000)==0) || \ + (((*(unsigned int*)&(f))&0x7f800000)==0x7f800000)) +/* more stringent test: anything not between 1e-19 and 1e19 in absolute val */ +#define PD_BIGORSMALL(f) ((((*(unsigned int*)&(f))&0x60000000)==0) || \ + (((*(unsigned int*)&(f))&0x60000000)==0x60000000)) +#else /* 64 bits... don't know what to do here */ +#define PD_BADFLOAT(f) (!(((f) >= 0) || ((f) <= 0))) +#define PD_BIGORSMALL(f) ((f) > 1e150 || (f) < -1e150 \ + || (f) > -1e-150 && (f) < 1e-150 ) +#endif +#endif /* _MSC_VER */ + /* get version number at run time */ +EXTERN void sys_getversion(int *major, int *minor, int *bugfix); + +EXTERN_STRUCT _instancemidi; +#define t_instancemidi struct _instancemidi + +EXTERN_STRUCT _instanceinter; +#define t_instanceinter struct _instanceinter + +EXTERN_STRUCT _instancecanvas; +#define t_instancecanvas struct _instancecanvas + +EXTERN_STRUCT _instanceugen; +#define t_instanceugen struct _instanceugen + +EXTERN_STRUCT _instancestuff; +#define t_instancestuff struct _instancestuff + +#ifndef PDTHREADS +#define PDTHREADS 1 +#endif + +struct _pdinstance +{ + double pd_systime; /* global time in Pd ticks */ + t_clock *pd_clock_setlist; /* list of set clocks */ + t_canvas *pd_canvaslist; /* list of all root canvases */ + struct _template *pd_templatelist; /* list of all templates */ + int pd_instanceno; /* ordinal number of this instance */ + t_symbol **pd_symhash; /* symbol table hash table */ + t_instancemidi *pd_midi; /* private stuff for x_midi.c */ + t_instanceinter *pd_inter; /* private stuff for s_inter.c */ + t_instanceugen *pd_ugen; /* private stuff for d_ugen.c */ + t_instancecanvas *pd_gui; /* semi-private stuff in g_canvas.h */ + t_instancestuff *pd_stuff; /* semi-private stuff in s_stuff.h */ + t_pd *pd_newest; /* most recently created object */ +#ifdef PDINSTANCE + t_symbol pd_s_pointer; + t_symbol pd_s_float; + t_symbol pd_s_symbol; + t_symbol pd_s_bang; + t_symbol pd_s_list; + t_symbol pd_s_anything; + t_symbol pd_s_signal; + t_symbol pd_s__N; + t_symbol pd_s__X; + t_symbol pd_s_x; + t_symbol pd_s_y; + t_symbol pd_s_; +#endif +#if PDTHREADS + int pd_islocked; +#endif +}; +#define t_pdinstance struct _pdinstance +EXTERN t_pdinstance pd_maininstance; + +/* m_pd.c */ +#ifdef PDINSTANCE +EXTERN t_pdinstance *pdinstance_new(void); +EXTERN void pd_setinstance(t_pdinstance *x); +EXTERN void pdinstance_free(t_pdinstance *x); +#endif /* PDINSTANCE */ + +#if defined(PDTHREADS) && defined(PDINSTANCE) +#ifdef _MSC_VER +#define PERTHREAD __declspec(thread) +#else +#define PERTHREAD __thread +#endif /* _MSC_VER */ +#else +#define PERTHREAD +#endif + +#ifdef PDINSTANCE +extern PERTHREAD t_pdinstance *pd_this; +EXTERN t_pdinstance **pd_instances; +EXTERN int pd_ninstances; +#else +#define pd_this (&pd_maininstance) +#endif /* PDINSTANCE */ + +#ifdef PDINSTANCE +#define s_pointer (pd_this->pd_s_pointer) +#define s_float (pd_this->pd_s_float) +#define s_symbol (pd_this->pd_s_symbol) +#define s_bang (pd_this->pd_s_bang) +#define s_list (pd_this->pd_s_list) +#define s_anything (pd_this->pd_s_anything) +#define s_signal (pd_this->pd_s_signal) +#define s__N (pd_this->pd_s__N) +#define s__X (pd_this->pd_s__X) +#define s_x (pd_this->pd_s_x) +#define s_y (pd_this->pd_s_y) +#define s_ (pd_this->pd_s_) +#else +EXTERN t_symbol s_pointer, s_float, s_symbol, s_bang, s_list, s_anything, + s_signal, s__N, s__X, s_x, s_y, s_; +#endif + +EXTERN t_canvas *pd_getcanvaslist(void); +EXTERN int pd_getdspstate(void); + +/* x_text.c */ +EXTERN t_binbuf *text_getbufbyname(t_symbol *s); /* get binbuf from text obj */ +EXTERN void text_notifybyname(t_symbol *s); /* notify it was modified */ + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +} +#endif + +#define __m_pd_h_ +#endif /* __m_pd_h_ */ From 13dfddae645464e89f7a383042e416365948f124 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 30 Apr 2021 09:56:07 +0200 Subject: [PATCH 041/193] Ensure plugin CC does not Reset all controllers --- plugins/lv2/sfizz.cpp | 33 ++++++++++++++++++++++--------- plugins/vst/SfizzVstProcessor.cpp | 6 +++--- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/plugins/lv2/sfizz.cpp b/plugins/lv2/sfizz.cpp index 0c60824a9..6c1d56a21 100644 --- a/plugins/lv2/sfizz.cpp +++ b/plugins/lv2/sfizz.cpp @@ -645,7 +645,7 @@ sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, int delay, const LV2_Atom_Obj if (cc != -1) { if (atom->type == self->atom_float_uri && atom->size == sizeof(float)) { float value = *(const float *)LV2_ATOM_BODY_CONST(atom); - sfizz_send_hdcc(self->synth, delay, cc, value); + sfizz_automate_hdcc(self->synth, delay, cc, value); self->cc_current[cc] = value; self->ccauto[cc] = absl::nullopt; } @@ -711,13 +711,28 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev) { unsigned cc = msg[1]; float value = float(msg[2]) * (1.0f / 127.0f); - sfizz_send_hdcc(self->synth, - (int)ev->time.frames, - (int)cc, - value); - self->cc_current[cc] = value; - self->ccauto[cc] = value; - self->have_ccauto = true; + + switch (cc) + { + default: + { + sfizz_automate_hdcc(self->synth, + (int)ev->time.frames, + (int)cc, + value); + self->cc_current[cc] = value; + self->ccauto[cc] = value; + self->have_ccauto = true; + } + break; + case LV2_MIDI_CTL_ALL_NOTES_OFF: + // TODO implemented as all-sound-off + sfizz_all_sound_off(self->synth); + break; + case LV2_MIDI_CTL_ALL_SOUNDS_OFF: + sfizz_all_sound_off(self->synth); + break; + } } break; #endif @@ -1416,7 +1431,7 @@ restore(LV2_Handle instance, absl::optional value = cc_values[cc]; if (value) { // Set CC in the synth - sfizz_send_hdcc(self->synth, 0, int(cc), *value); + sfizz_automate_hdcc(self->synth, 0, int(cc), *value); // Mark CCs for automation with state values self->ccauto[cc] = *value; self->have_ccauto = true; diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp index 0614803be..467a9e414 100644 --- a/plugins/vst/SfizzVstProcessor.cpp +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -442,7 +442,7 @@ void SfizzVstProcessor::playOrderedParameter(int32 sampleOffset, Vst::ParamID id default: if (id >= kPidCC0 && id <= kPidCCLast) { int32 ccNumber = static_cast(id - kPidCC0); - synth.hdcc(sampleOffset, ccNumber, value); + synth.automateHdcc(sampleOffset, ccNumber, value); _state.controllers[ccNumber] = value; } break; @@ -518,7 +518,7 @@ void SfizzVstProcessor::processMessagesFromUi() synth.noteOn(0, data[1] & 0x7f, data[2] & 0x7f); break; case 0xb0: - synth.cc(0, data[1] & 0x7f, data[2] & 0x7f); + synth.automateHdcc(0, data[1] & 0x7f, (data[2] & 0x7f) / 127.0f); break; case 0xe0: synth.pitchWheel(0, (data[2] << 7) + data[1] - 8192); @@ -713,7 +713,7 @@ void SfizzVstProcessor::loadSfzFileOrDefault(const std::string& filePath, bool i for (uint32 cc = 0; cc < sfz::config::numCCs; ++cc) { if (absl::optional value = oldControllers[cc]) { newControllers[cc] = *value; - synth.hdcc(0, int(cc), *value); + synth.automateHdcc(0, int(cc), *value); } } } From fb340037d77990615052a75d24f6bc0ebae7f249 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 30 Apr 2021 10:01:36 +0200 Subject: [PATCH 042/193] Fix a clang-tidy warning --- plugins/vst/SfizzVstProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp index 467a9e414..bb1bcac5e 100644 --- a/plugins/vst/SfizzVstProcessor.cpp +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -518,7 +518,7 @@ void SfizzVstProcessor::processMessagesFromUi() synth.noteOn(0, data[1] & 0x7f, data[2] & 0x7f); break; case 0xb0: - synth.automateHdcc(0, data[1] & 0x7f, (data[2] & 0x7f) / 127.0f); + synth.automateHdcc(0, data[1] & 0x7f, static_cast(data[2] & 0x7f) / 127.0f); break; case 0xe0: synth.pitchWheel(0, (data[2] << 7) + data[1] - 8192); From 852a18558966e949954ddc9da38428936e903944 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 30 Apr 2021 11:43:27 +0200 Subject: [PATCH 043/193] pd: default patch improvements --- plugins/puredata/example.sfz | 2 +- plugins/puredata/sfizz~-help.pd | 44 ++++++++++++++++----------------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/plugins/puredata/example.sfz b/plugins/puredata/example.sfz index 1ab726998..6e64db979 100644 --- a/plugins/puredata/example.sfz +++ b/plugins/puredata/example.sfz @@ -5,7 +5,7 @@ ampeg_sustain=50 ampeg_release=2 fil_type=lpf_4p resonance=3 -cutoff=2000 +cutoff=3000 oscillator_phase=-1 cutoff_oncc300=3600 cutoff_curvecc300=1 diff --git a/plugins/puredata/sfizz~-help.pd b/plugins/puredata/sfizz~-help.pd index 96600cbac..98d0e0f0c 100644 --- a/plugins/puredata/sfizz~-help.pd +++ b/plugins/puredata/sfizz~-help.pd @@ -1,23 +1,16 @@ -#N canvas 932 395 659 405 12; +#N canvas 614 464 659 405 12; #X declare -lib sfizz; #X obj 4 3 cnv 15 650 80 empty empty sfizz 20 30 0 40 -261682 -66577 0; #X text 45 54 A synthesizer for instruments in SFZ format; -#X obj 241 165 r sfizz-ctl; #X text 33 167 create a SFZ instrument; #X obj 29 324 midiin; -#X obj 29 349 s sfizz-ctl; #X text 26 301 connect MIDI-in; #X obj 162 223 dac~; #X msg 180 346 voices \$1; -#X obj 180 371 s sfizz-ctl; #X text 178 302 modify the polyphony; -#X obj 183 326 hsl 128 15 1 256 0 1 empty empty empty -2 -8 0 10 -262144 --1 -1 3138 1; -#X floatatom 273 346 5 0 0 0 - - -; -#X obj 378 372 s sfizz-ctl; -#X obj 381 327 hsl 128 15 0 1 0 1 empty empty empty -2 -8 0 10 -262144 --1 -1 6350 1; +#X obj 381 327 hsl 127 15 0 1 0 1 empty empty empty -2 -8 0 10 -262144 +-1 -1 6300 1; #X floatatom 471 347 5 0 0 0 - - -; #X text 377 303 modulate a parameter; #X msg 378 347 hdcc 300 \$1; @@ -28,19 +21,24 @@ #X text 218 94 click to turn on DSP; #X text 153 247 output stereo audio; #X msg 366 223 reload; -#X obj 366 248 s sfizz-ctl; -#X obj 466 248 s sfizz-ctl; #X msg 466 223 load example.sfz; #X text 364 200 reload the instrument or load another; -#X connect 2 0 19 0; -#X connect 4 0 5 0; +#X obj 241 165 r \$0-input; +#X obj 29 349 s \$0-input; +#X obj 180 371 s \$0-input; +#X obj 378 372 s \$0-input; +#X obj 366 248 s \$0-input; +#X obj 466 248 s \$0-input; +#X obj 180 327 nbx 5 14 1 256 0 1 empty empty empty 0 -8 0 10 -262144 +-1 -1 64 256; +#X connect 3 0 22 0; +#X connect 6 0 23 0; +#X connect 8 0 11 0; #X connect 8 0 9 0; -#X connect 11 0 8 0; -#X connect 11 0 12 0; -#X connect 14 0 17 0; -#X connect 14 0 15 0; -#X connect 17 0 13 0; -#X connect 19 0 7 0; -#X connect 19 1 7 1; -#X connect 24 0 25 0; -#X connect 27 0 26 0; +#X connect 11 0 24 0; +#X connect 13 0 5 0; +#X connect 13 1 5 1; +#X connect 18 0 25 0; +#X connect 19 0 26 0; +#X connect 21 0 13 0; +#X connect 27 0 6 0; From a9f119d63b77a92986cd9336c52c04ce747826c9 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 30 Apr 2021 12:03:13 +0200 Subject: [PATCH 044/193] Update pd external and help with new features --- plugins/puredata/sfizz_puredata.c | 113 ++++++++++++++++++++++++++++-- plugins/puredata/sfizz~-help.pd | 54 +++++++++----- 2 files changed, 142 insertions(+), 25 deletions(-) diff --git a/plugins/puredata/sfizz_puredata.c b/plugins/puredata/sfizz_puredata.c index 89422f7ad..16b97c6d9 100644 --- a/plugins/puredata/sfizz_puredata.c +++ b/plugins/puredata/sfizz_puredata.c @@ -16,6 +16,10 @@ static t_class* cls_sfizz_tilde; typedef struct _sfizz_tilde { t_object obj; t_outlet* outputs[2]; + t_inlet* input_cc; + t_inlet* input_bend; + t_inlet* input_touch; + t_inlet* input_polytouch; sfizz_synth_t* synth; int midi[3]; int midinum; @@ -23,6 +27,20 @@ typedef struct _sfizz_tilde { char* filepath; } t_sfizz_tilde; +static t_float clamp01(t_float x) +{ + x = (x > 0) ? x : 0; + x = (x < 1) ? x : 1; + return x; +} + +static t_float clampB1(t_float x) +{ + x = (x > -1) ? x : -1; + x = (x < 1) ? x : 1; + return x; +} + static void sfizz_tilde_set_file(t_sfizz_tilde* self, const char* file) { const char* dir = self->dir->s_name; @@ -69,6 +87,11 @@ static void* sfizz_tilde_new(t_symbol* sym, int argc, t_atom argv[]) self->outputs[0] = outlet_new(&self->obj, &s_signal); self->outputs[1] = outlet_new(&self->obj, &s_signal); + self->input_cc = inlet_new(&self->obj, &self->obj.ob_pd, &s_float, gensym("cc")); + self->input_bend = inlet_new(&self->obj, &self->obj.ob_pd, &s_float, gensym("bend")); + self->input_touch = inlet_new(&self->obj, &self->obj.ob_pd, &s_float, gensym("touch")); + self->input_polytouch = inlet_new(&self->obj, &self->obj.ob_pd, &s_float, gensym("polytouch")); + sfizz_synth_t* synth = sfizz_create_synth(); self->synth = synth; @@ -94,6 +117,14 @@ static void sfizz_tilde_free(t_sfizz_tilde* self) outlet_free(self->outputs[0]); if (self->outputs[1]) outlet_free(self->outputs[1]); + if (self->input_cc) + inlet_free(self->input_cc); + if (self->input_bend) + inlet_free(self->input_bend); + if (self->input_touch) + inlet_free(self->input_touch); + if (self->input_polytouch) + inlet_free(self->input_polytouch); } static t_int* sfizz_tilde_perform(t_int* w) @@ -120,6 +151,22 @@ static void sfizz_tilde_dsp(t_sfizz_tilde* self, t_signal** sp) (t_int)sp[0]->s_vec, (t_int)sp[1]->s_vec, (t_int)sp[0]->s_n); } +static void sfizz_tilde_list(t_sfizz_tilde* self, t_symbol* sym, int argc, t_atom* argv) +{ + (void)sym; + + if (argc == 2 && argv[0].a_type == A_FLOAT && argv[1].a_type == A_FLOAT) { + int key = (int)argv[0].a_w.w_float; + if (key < 0 || key > 127) + return; + t_float vel = clamp01(argv[1].a_w.w_float / 127); + if (vel > 0) + sfizz_send_hd_note_on(self->synth, 0, key, vel); + else + sfizz_send_hd_note_off(self->synth, 0, key, 0); + } +} + static void sfizz_tilde_midiin(t_sfizz_tilde* self, t_float f) { int byte = (int)f; @@ -187,17 +234,55 @@ static void sfizz_tilde_reload(t_sfizz_tilde* self, t_float value) sfizz_tilde_do_load(self); } -static void sfizz_tilde_hdcc(t_sfizz_tilde* self, t_float cc, t_float value) +static void sfizz_tilde_hdcc(t_sfizz_tilde* self, t_float f1, t_float f2) +{ + int cc = (int)f1; + if (cc < 0 || cc > 127) + return; + sfizz_automate_hdcc(self->synth, 0, (int)cc, clamp01(f2)); +} + +static void sfizz_tilde_cc(t_sfizz_tilde* self, t_float f1, t_float f2) +{ + sfizz_tilde_hdcc(self, f1, f2 / 127); +} + +static void sfizz_tilde_hdbend(t_sfizz_tilde* self, t_float f1) +{ + sfizz_send_hd_pitch_wheel(self->synth, 0, clampB1(f1)); +} + +static void sfizz_tilde_bend(t_sfizz_tilde* self, t_float f1) +{ + return sfizz_tilde_hdbend(self, f1 / 8191); +} + +static void sfizz_tilde_hdtouch(t_sfizz_tilde* self, t_float f1) +{ + sfizz_send_hd_channel_aftertouch(self->synth, 0, clamp01(f1)); +} + +static void sfizz_tilde_touch(t_sfizz_tilde* self, t_float f1) +{ + sfizz_tilde_hdtouch(self, f1 / 127); +} + +static void sfizz_tilde_hdpolytouch(t_sfizz_tilde* self, t_float key, t_float f2) { - sfizz_automate_hdcc(self->synth, 0, (int)cc, value); + if (key < 0 || key > 127) + return; + sfizz_send_hd_poly_aftertouch(self->synth, 0, (int)key, clamp01(f2)); } -static void sfizz_tilde_voices(t_sfizz_tilde* self, t_float value) +static void sfizz_tilde_polytouch(t_sfizz_tilde* self, t_float f1, t_float f2) { - int numvoices = (int)value; - if (numvoices < 1) - numvoices = 1; + sfizz_tilde_hdpolytouch(self, f1, f2 / 127); +} +static void sfizz_tilde_voices(t_sfizz_tilde* self, t_float f1) +{ + int numvoices = (int)f1; + numvoices = (numvoices < 1) ? 1 : numvoices; sfizz_set_num_voices(self->synth, numvoices); } @@ -218,14 +303,30 @@ void sfizz_setup() CLASS_DEFAULT, A_GIMME, A_NULL); class_addmethod( cls_sfizz_tilde, (t_method)&sfizz_tilde_dsp, gensym("dsp"), A_CANT, A_NULL); + class_addlist( + cls_sfizz_tilde, (t_method)&sfizz_tilde_list); class_addmethod( cls_sfizz_tilde, (t_method)&sfizz_tilde_midiin, &s_float, A_FLOAT, A_NULL); class_addmethod( cls_sfizz_tilde, (t_method)&sfizz_tilde_load, gensym("load"), A_DEFSYM, A_NULL); class_addmethod( cls_sfizz_tilde, (t_method)&sfizz_tilde_reload, gensym("reload"), A_DEFFLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_cc, gensym("cc"), A_FLOAT, A_FLOAT, A_NULL); class_addmethod( cls_sfizz_tilde, (t_method)&sfizz_tilde_hdcc, gensym("hdcc"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_bend, gensym("bend"), A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_hdbend, gensym("hdbend"), A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_touch, gensym("touch"), A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_hdtouch, gensym("hdtouch"), A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_polytouch, gensym("polytouch"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_hdpolytouch, gensym("hdpolytouch"), A_FLOAT, A_FLOAT, A_NULL); class_addmethod( cls_sfizz_tilde, (t_method)&sfizz_tilde_voices, gensym("voices"), A_FLOAT, A_NULL); } diff --git a/plugins/puredata/sfizz~-help.pd b/plugins/puredata/sfizz~-help.pd index 98d0e0f0c..c3024d327 100644 --- a/plugins/puredata/sfizz~-help.pd +++ b/plugins/puredata/sfizz~-help.pd @@ -1,36 +1,48 @@ -#N canvas 614 464 659 405 12; +#N canvas 614 464 709 426 12; #X declare -lib sfizz; -#X obj 4 3 cnv 15 650 80 empty empty sfizz 20 30 0 40 -261682 -66577 +#X obj 4 3 cnv 15 700 80 empty empty sfizz 20 30 0 40 -261682 -66577 0; #X text 45 54 A synthesizer for instruments in SFZ format; #X text 33 167 create a SFZ instrument; -#X obj 29 324 midiin; -#X text 26 301 connect MIDI-in; +#X obj 26 323 midiin; +#X text 23 300 connect MIDI-in; #X obj 162 223 dac~; -#X msg 180 346 voices \$1; -#X text 178 302 modify the polyphony; -#X obj 381 327 hsl 127 15 0 1 0 1 empty empty empty -2 -8 0 10 -262144 +#X msg 177 345 voices \$1; +#X text 175 301 modify the polyphony; +#X obj 376 326 hsl 127 15 0 1 0 1 empty empty empty -2 -8 0 10 -262144 -1 -1 6300 1; -#X floatatom 471 347 5 0 0 0 - - -; -#X text 377 303 modulate a parameter; -#X msg 378 347 hdcc 300 \$1; +#X floatatom 466 346 5 0 0 0 - - -; +#X text 372 302 modulate a parameter; +#X msg 373 346 hdcc 300 \$1; #X obj 27 115 declare -lib sfizz; #X obj 163 192 sfizz~ example.sfz; #X text 18 93 load the sfizz library; #X msg 221 115 \; pd dsp 1; #X text 218 94 click to turn on DSP; #X text 153 247 output stereo audio; -#X msg 366 223 reload; -#X msg 466 223 load example.sfz; -#X text 364 200 reload the instrument or load another; +#X msg 398 223 reload; +#X msg 498 223 load example.sfz; +#X text 396 200 reload the instrument or load another; #X obj 241 165 r \$0-input; -#X obj 29 349 s \$0-input; -#X obj 180 371 s \$0-input; -#X obj 378 372 s \$0-input; -#X obj 366 248 s \$0-input; -#X obj 466 248 s \$0-input; -#X obj 180 327 nbx 5 14 1 256 0 1 empty empty empty 0 -8 0 10 -262144 +#X obj 26 348 s \$0-input; +#X obj 177 370 s \$0-input; +#X obj 373 371 s \$0-input; +#X obj 398 248 s \$0-input; +#X obj 498 248 s \$0-input; +#X obj 177 326 nbx 5 14 1 256 0 1 empty empty empty 0 -8 0 10 -262144 -1 -1 64 256; +#X text 567 302 send a note; +#X obj 568 348 makenote 100 500; +#X obj 568 373 list; +#X obj 568 398 s \$0-input; +#X msg 568 323 60; +#X obj 386 92 cnv 15 300 100 empty empty Inlets 20 12 0 14 -233017 +-66577 0; +#X text 395 140 3: pitch bend; +#X text 395 154 4: channel aftertouch; +#X text 395 168 5: polyphonic aftertouch and key; +#X text 395 126 2: controller and value; +#X text 395 112 1: note and velocity; #X connect 3 0 22 0; #X connect 6 0 23 0; #X connect 8 0 11 0; @@ -42,3 +54,7 @@ #X connect 19 0 26 0; #X connect 21 0 13 0; #X connect 27 0 6 0; +#X connect 29 0 30 0; +#X connect 29 1 30 1; +#X connect 30 0 31 0; +#X connect 32 0 29 0; From 650018254b7bd18d67e7c82e37ed999784af4ef1 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 30 Apr 2021 14:18:35 +0200 Subject: [PATCH 045/193] pd: accept CC numbers up to the limit --- plugins/puredata/CMakeLists.txt | 1 + plugins/puredata/sfizz_puredata.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/puredata/CMakeLists.txt b/plugins/puredata/CMakeLists.txt index e6495246a..abb196063 100644 --- a/plugins/puredata/CMakeLists.txt +++ b/plugins/puredata/CMakeLists.txt @@ -24,6 +24,7 @@ function(copy_puredata_resources TARGET SOURCE_DIR DESTINATION_DIR) endfunction() add_library(sfizz_puredata MODULE sfizz_puredata.c) +target_compile_definitions(sfizz_puredata PRIVATE "SFIZZ_NUM_CCS=${SFIZZ_NUM_CCS}") target_include_directories(sfizz_puredata PRIVATE "${PUREDATA_INCLUDE_DIR}") target_link_libraries(sfizz_puredata PRIVATE sfizz::sfizz) diff --git a/plugins/puredata/sfizz_puredata.c b/plugins/puredata/sfizz_puredata.c index 16b97c6d9..d998929b9 100644 --- a/plugins/puredata/sfizz_puredata.c +++ b/plugins/puredata/sfizz_puredata.c @@ -237,7 +237,7 @@ static void sfizz_tilde_reload(t_sfizz_tilde* self, t_float value) static void sfizz_tilde_hdcc(t_sfizz_tilde* self, t_float f1, t_float f2) { int cc = (int)f1; - if (cc < 0 || cc > 127) + if (cc < 0 || cc >= SFIZZ_NUM_CCS) return; sfizz_automate_hdcc(self->synth, 0, (int)cc, clamp01(f2)); } From 3afb426d5572caf76b676b784edbf9724ea70881 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 30 Apr 2021 14:18:58 +0200 Subject: [PATCH 046/193] pd: cleanup [ci skip] --- plugins/puredata/sfizz_puredata.c | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/puredata/sfizz_puredata.c b/plugins/puredata/sfizz_puredata.c index d998929b9..ac7f4624b 100644 --- a/plugins/puredata/sfizz_puredata.c +++ b/plugins/puredata/sfizz_puredata.c @@ -214,7 +214,6 @@ static void sfizz_tilde_midiin(t_sfizz_tilde* self, t_float f) case 0xe0: // pitch bend sfizz_send_pitch_wheel(self->synth, 0, (midi[1] + (midi[2] << 7)) - 8192); break; - } break; } From 5e8443892b71e5d4bb0ee628de3e19563a08aabf Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 30 Apr 2021 14:23:16 +0200 Subject: [PATCH 047/193] Allow puredata to build in CI --- .github/workflows/build.yml | 7 +++++-- scripts/appveyor/before_build.sh | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9fbd501e8..1feadfd70 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -70,6 +70,7 @@ jobs: -DSFIZZ_JACK=ON \ -DSFIZZ_VST=ON \ -DSFIZZ_LV2_UI=ON \ + -DSFIZZ_PUREDATA=ON \ -DSFIZZ_TESTS=ON \ -DSFIZZ_SHARED=OFF \ -DSFIZZ_STATIC_DEPENDENCIES=OFF \ @@ -157,7 +158,7 @@ jobs: - name: Configure CMake working-directory: ${{runner.workspace}}/build run: | - cmake "${Env:GITHUB_WORKSPACE}" -G"Visual Studio 16 2019" -A"${Env:release_arch}" -DCMAKE_BUILD_TYPE="${Env:BUILD_TYPE}" -DCMAKE_CXX_STANDARD=17 -DSFIZZ_TESTS=ON -DSFIZZ_VST=ON -DSFIZZ_LV2=ON + cmake "${Env:GITHUB_WORKSPACE}" -G"Visual Studio 16 2019" -A"${Env:release_arch}" -DCMAKE_BUILD_TYPE="${Env:BUILD_TYPE}" -DCMAKE_CXX_STANDARD=17 -DSFIZZ_TESTS=ON -DSFIZZ_VST=ON -DSFIZZ_LV2=ON -DSFIZZ_PUREDATA=ON - name: Build tests working-directory: ${{runner.workspace}}/build run: cmake --build . --config "${Env:BUILD_TYPE}" -j 2 --target sfizz_tests @@ -192,7 +193,7 @@ jobs: - name: Configure CMake working-directory: ${{runner.workspace}}/build run: | - cmake "${Env:GITHUB_WORKSPACE}" -G"Visual Studio 16 2019" -A"${Env:release_arch}" -DCMAKE_BUILD_TYPE="${Env:BUILD_TYPE}" -DCMAKE_CXX_STANDARD=17 -DSFIZZ_TESTS=ON -DSFIZZ_VST=ON -DSFIZZ_LV2=ON + cmake "${Env:GITHUB_WORKSPACE}" -G"Visual Studio 16 2019" -A"${Env:release_arch}" -DCMAKE_BUILD_TYPE="${Env:BUILD_TYPE}" -DCMAKE_CXX_STANDARD=17 -DSFIZZ_TESTS=ON -DSFIZZ_VST=ON -DSFIZZ_LV2=ON -DSFIZZ_PUREDATA=ON - name: Build tests working-directory: ${{runner.workspace}}/build run: cmake --build . --config "${Env:BUILD_TYPE}" -j 2 --target sfizz_tests @@ -270,6 +271,7 @@ jobs: -DENABLE_LTO=OFF \ -DSFIZZ_JACK=OFF \ -DSFIZZ_VST=ON \ + -DSFIZZ_PUREDATA=ON \ -DSFIZZ_STATIC_DEPENDENCIES=ON \ -DCMAKE_CXX_STANDARD=17 - name: Build @@ -339,6 +341,7 @@ jobs: -DENABLE_LTO=OFF \ -DSFIZZ_JACK=OFF \ -DSFIZZ_VST=ON \ + -DSFIZZ_PUREDATA=ON \ -DSFIZZ_STATIC_DEPENDENCIES=ON \ -DCMAKE_CXX_STANDARD=17 - name: Build diff --git a/scripts/appveyor/before_build.sh b/scripts/appveyor/before_build.sh index 5989ba146..e813e0552 100644 --- a/scripts/appveyor/before_build.sh +++ b/scripts/appveyor/before_build.sh @@ -7,6 +7,7 @@ cmake -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ -DSFIZZ_VST=ON \ -DSFIZZ_AU=ON \ + -DSFIZZ_PUREDATA=ON \ -DSFIZZ_RENDER=OFF \ -DSFIZZ_SHARED=OFF \ -DSFIZZ_TESTS=ON \ From c575b2fc6c88ff189d4579222b62ee3751c4992b Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 30 Apr 2021 15:00:46 +0200 Subject: [PATCH 048/193] Attempt to make it build --- cmake/PuredataConfig.cmake | 49 +- plugins/puredata/CMakeLists.txt | 10 +- plugins/puredata/external/pd/bin/pd.def | 601 ++++++++++++++++++++++++ 3 files changed, 653 insertions(+), 7 deletions(-) create mode 100644 plugins/puredata/external/pd/bin/pd.def diff --git a/cmake/PuredataConfig.cmake b/cmake/PuredataConfig.cmake index eb794d1d1..1fc7f0055 100644 --- a/cmake/PuredataConfig.cmake +++ b/cmake/PuredataConfig.cmake @@ -1,10 +1,11 @@ -find_path(PUREDATA_INCLUDE_DIR "m_pd.h" PATH_SUFFIXES "pd") +find_path(PD_INCLUDE_BASEDIR "m_pd.h" PATH_SUFFIXES "pd") +set(PD_IMP_DEF "${PROJECT_SOURCE_DIR}/plugins/puredata/external/pd/bin/pd.def") -if(PUREDATA_INCLUDE_DIR) +if(PD_INCLUDE_BASEDIR) message(STATUS "Puredata headers: ${PUREDATA_INCLUDE_DIR}") else() message(STATUS "Puredata headers not found, using our own") - set(PUREDATA_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/plugins/puredata/external/pd/include") + set(PD_INCLUDE_BASEDIR "${PROJECT_SOURCE_DIR}/plugins/puredata/external/pd/include") endif() if(WIN32) @@ -14,3 +15,45 @@ elseif(APPLE) else() set(PUREDATA_SUFFIX ".pd_linux") endif() + +if(WIN32) + add_library(pdex-implib STATIC IMPORTED) + if(MSVC) + add_custom_command( + OUTPUT "pd.lib" + COMMAND "lib" "/out:pd.lib" "/def:${PD_IMP_DEF}" "/machine:${MSVC_C_ARCHITECTURE_ID}" + DEPENDS "${PD_IMP_DEF}") + add_custom_target(pdex-implib-generated + DEPENDS "pd.lib") + set_target_properties(pdex-implib PROPERTIES + IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/pd.lib") + else() + find_program(PD_DLLTOOL_PROGRAM "dlltool") + if(NOT PD_DLLTOOL_PROGRAM) + message(FATAL_ERROR "Cannot find dlltool") + endif() + add_custom_command( + OUTPUT "libpd.dll.a" + COMMAND "${PD_DLLTOOL_PROGRAM}" "-l" "libpd.dll.a" "-d" "${PD_IMP_DEF}" + DEPENDS "${PD_IMP_DEF}") + add_custom_target(pdex-implib-generated + DEPENDS "libpd.dll.a") + set_target_properties(pdex-implib PROPERTIES + IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/libpd.dll.a") + endif() + add_dependencies(pdex-implib pdex-implib-generated) +endif() + +function(add_pd_external TARGET) + add_library("${TARGET}" MODULE ${ARGN}) + target_include_directories("${TARGET}" PRIVATE "${PD_INCLUDE_BASEDIR}") + set_target_properties("${TARGET}" PROPERTIES + PREFIX "" + SUFFIX "${PUREDATA_SUFFIX}") + if(APPLE) + set_property(TARGET "${TARGET}" APPEND_STRING + PROPERTY LINK_FLAGS " -Wl,-undefined,suppress,-flat_namespace") + elseif(WIN32) + target_link_libraries("${TARGET}" PRIVATE pdex-implib) + endif() +endfunction() diff --git a/plugins/puredata/CMakeLists.txt b/plugins/puredata/CMakeLists.txt index abb196063..0ad11a25e 100644 --- a/plugins/puredata/CMakeLists.txt +++ b/plugins/puredata/CMakeLists.txt @@ -23,17 +23,19 @@ function(copy_puredata_resources TARGET SOURCE_DIR DESTINATION_DIR) add_dependencies("${TARGET}" "${TARGET}_puredata_resources") endfunction() -add_library(sfizz_puredata MODULE sfizz_puredata.c) +add_pd_external(sfizz_puredata "sfizz_puredata.c") target_compile_definitions(sfizz_puredata PRIVATE "SFIZZ_NUM_CCS=${SFIZZ_NUM_CCS}") -target_include_directories(sfizz_puredata PRIVATE "${PUREDATA_INCLUDE_DIR}") target_link_libraries(sfizz_puredata PRIVATE sfizz::sfizz) set_target_properties(sfizz_puredata PROPERTIES - PREFIX "" - SUFFIX "${PUREDATA_SUFFIX}" OUTPUT_NAME "sfizz" LIBRARY_OUTPUT_DIRECTORY "${PUREDATA_BINARY_DIR}/$<0:>") +if(MINGW) + set_property(TARGET sfizz_puredata APPEND_STRING + PROPERTY LINK_FLAGS " -static") +endif() + copy_puredata_resources(sfizz_puredata "${CMAKE_CURRENT_SOURCE_DIR}" "${PUREDATA_BINARY_DIR}") diff --git a/plugins/puredata/external/pd/bin/pd.def b/plugins/puredata/external/pd/bin/pd.def new file mode 100644 index 000000000..607bd9905 --- /dev/null +++ b/plugins/puredata/external/pd/bin/pd.def @@ -0,0 +1,601 @@ +LIBRARY pd +EXPORTS + ABORT + array_free + array_getcoordinate + array_getfields + array_new + array_redraw + array_resize + array_resize_and_redraw + atom_gensym + atom_getfloat + atom_getfloatarg + atom_getint + atom_getintarg + atom_getsymbol + atom_getsymbolarg + atom_string + audio_isopen + audio_shouldkeepopen + binbuf_add + binbuf_addbinbuf + binbuf_addsemi + binbuf_addv + binbuf_clear + binbuf_duplicate + binbuf_eval + binbuf_evalfile + binbuf_free + binbuf_getnatom + binbuf_gettext + binbuf_getvec + binbuf_new + binbuf_print + binbuf_read + binbuf_read_via_canvas + binbuf_read_via_path + binbuf_realizedollsym + binbuf_resize + binbuf_restore + binbuf_text + binbuf_write + bug + canvas_addinlet + canvas_addoutlet + canvas_class DATA + canvas_closebang + canvas_connect + canvas_create_editor + canvas_dataproperties + canvas_deletelinesfor + canvas_deletelinesforio + canvas_destroy_editor + canvas_dirty + canvas_disconnect + canvas_dspstate DATA + canvas_editmode + canvas_fixlinesfor + canvas_free + canvas_getargs + canvas_getcurrent + canvas_getcurrentdir + canvas_getdir + canvas_getdollarzero + canvas_getenv + canvas_getindex + canvas_getrootfor + canvas_hitbox + canvas_initbang + canvas_isabstraction + canvas_isconnected + canvas_istable + canvas_loadbang + canvas_makebindsym + canvas_makefilename + canvas_new + canvas_noundo + canvas_open + canvas_path_iterate + canvas_readscalar + canvas_realizedollar + canvas_redraw + canvas_redrawallfortemplate + canvas_redrawallfortemplatecanvas + canvas_rename + canvas_resortinlets + canvas_resortoutlets + canvas_restoreconnections + canvas_resume_dsp + canvas_rminlet + canvas_rmoutlet + canvas_selectinrect + canvas_setargs + canvas_setcurrent + canvas_setcursor + canvas_setdeleting + canvas_setundo + canvas_showtext + canvas_stowconnections + canvas_suspend_dsp + canvas_unsetcurrent + canvas_update_dsp + canvas_updatewindowlist + canvas_vis + canvas_whichfind DATA + canvas_writescalar + class_addanything + class_addbang + class_addcreator + class_addlist + class_addmethod + class_addpointer + class_addsymbol + class_doaddfloat + class_domainsignalin + class_gethelpdir + class_gethelpname + class_getname + class_getpropertiesfn + class_getsavefn + class_isdrawcommand + class_new + class_new64 + class_set_extern_dir + class_setdrawcommand + class_sethelpsymbol + class_setparentwidget + class_setpropertiesfn + class_setsavefn + class_setwidget + clock_delay + clock_free + clock_getlogicaltime + clock_getsystime + clock_getsystimeafter + clock_gettimesince + clock_gettimesincewithunits + clock_new + clock_set + clock_setunit + clock_unset + clone_class DATA + copy_perform + copybytes + cos_table DATA + dbtopow + dbtorms + dsp_add + dsp_add_copy + dsp_add_plus + dsp_add_scalarcopy + dsp_add_zero + dsp_addv + endpost + error + ex_Avg + ex_Sum + ex_avg + ex_funcs DATA + ex_getsym + ex_mkvector + ex_size + ex_store + ex_sum + ex_symname + fielddesc_cvtfromcoord + fielddesc_cvttocoord + fielddesc_getcoord + fielddesc_setcoord + floatinlet_new + freebytes + ftom + g_editor_freepdinstance + g_editor_newpdinstance + g_template_freepdinstance + g_template_newpdinstance + garray_class DATA + garray_getarray + garray_getfloatarray + garray_getfloatwords + garray_getglist + garray_npoints + garray_redraw + garray_resize + garray_resize_long + garray_setsaveit + garray_template + garray_usedindsp + garray_vec + gensym + get_sys_dacsr + get_sys_main_advance + get_sys_schedadvance + get_sys_schedblocksize + get_sys_sleepgrain + get_sys_soundin + get_sys_soundout + get_sys_time + get_sys_time_per_dsp_tick + getbytes + getfn + getzbytes + gfxstub_deleteforkey + gfxstub_new + glist_add + glist_addglist + glist_arraydialog + glist_clear + glist_delete + glist_deselect + glist_dpixtodx + glist_dpixtody + glist_drawiofor + glist_eraseiofor + glist_findgraph + glist_findrtext + glist_fontheight + glist_fontwidth + glist_getcanvas + glist_getfont + glist_getnextxy + glist_getzoom + glist_glist + glist_grab + glist_init + glist_isgraph + glist_isselected + glist_istoplevel + glist_isvisible + glist_mergefile + glist_noselect + glist_pixelstox + glist_pixelstoy + glist_read + glist_redraw + glist_retext + glist_select + glist_selectall + glist_sort + glist_valid DATA + glist_writetobinbuf + glist_xtopixels + glist_ytopixels + glob_evalfile + glob_initfromgui + glob_pdobject DATA + glob_quit + glob_setfilename + gobj_activate + gobj_click + gobj_delete + gobj_displace + gobj_getrect + gobj_save + gobj_select + gobj_shouldvis + gobj_vis + gpointer_check + gpointer_copy + gpointer_init + gpointer_setarray + gpointer_setglist + gpointer_unset + graph_array + gstub_cutoff + gstub_new + gtemplate_get + guiconnect_new + guiconnect_notarget + iem_fstyletoint + iem_inttofstyle + iem_inttosymargs + iem_symargstoint + iemgui_all_dollar2raute + iemgui_all_dollararg2sym + iemgui_all_loadcolors + iemgui_all_raute2dollar + iemgui_all_sym2dollararg + iemgui_clip_font + iemgui_clip_size + iemgui_color + iemgui_color_hex DATA + iemgui_delete + iemgui_delta + iemgui_dialog + iemgui_displace + iemgui_dollar2raute + iemgui_label + iemgui_label_font + iemgui_label_pos + iemgui_new_dogetname + iemgui_new_getnames + iemgui_newzoom + iemgui_pos + iemgui_properties + iemgui_raute2dollar + iemgui_receive + iemgui_save + iemgui_select + iemgui_send + iemgui_size + iemgui_verify_snd_ne_rcv + iemgui_vis + iemgui_vu_col DATA + iemgui_vu_db2i DATA + iemgui_vu_scale_str DATA + iemgui_zoom + ilog2 + inlet_free + inlet_new + inmidi_aftertouch + inmidi_byte + inmidi_controlchange + inmidi_noteon + inmidi_pitchbend + inmidi_polyaftertouch + inmidi_programchange + inmidi_realtimein + inmidi_sysex + linetraverser_next + linetraverser_skipobject + linetraverser_start + logpost + max_ex_tab + max_ex_tab_store + max_ex_var + max_ex_var_store + mayer_fft + mayer_fht + mayer_ifft + mayer_realfft + mayer_realifft + midi_getdevs + mmio_close_audio + mmio_getdevs + mmio_open_audio + mmio_reportidle + mmio_send_dacs + mtof + namelist_append + namelist_append_files + namelist_free + namelist_get + nullfn + obj_connect + obj_disconnect + obj_issignalinlet + obj_issignaloutlet + obj_list + obj_nexttraverseoutlet + obj_ninlets + obj_noutlets + obj_nsiginlets + obj_nsigoutlets + obj_saveformat + obj_siginletindex + obj_sigoutletindex + obj_starttraverseoutlet + open_via_helppath + open_via_path + outlet_anything + outlet_bang + outlet_float + outlet_free + outlet_getsymbol + outlet_list + outlet_new + outlet_pointer + outlet_setstacklim + outlet_symbol + pa_close_audio + pa_getdevs + pa_open_audio + pa_send_dacs + pd_bang + pd_bind + pd_canvasmaker DATA + pd_checkglist + pd_checkobject + pd_compatibilitylevel DATA + pd_emptylist + pd_error + pd_fft + pd_findbyclass + pd_float + pd_forwardmess + pd_free + pd_getcanvaslist + pd_getdspstate + pd_getparentwidget + pd_globallock + pd_globalunlock + pd_list + pd_maininstance DATA + pd_new + pd_newest + pd_objectmaker DATA + pd_pointer + pd_popsym + pd_pushsym + pd_symbol + pd_typedmess + pd_unbind + pd_vmess + plus_perform + pointerinlet_new + post + postatom + postfloat + poststring + powtodb + q8_rsqrt + q8_sqrt + qrsqrt + qsqrt + resample_dsp + resample_free + resample_init + resamplefrom_dsp + resampleto_dsp + resizebytes + rmstodb + rtext_activate + rtext_displace + rtext_draw + rtext_erase + rtext_free + rtext_getseltext + rtext_gettag + rtext_gettext + rtext_height + rtext_key + rtext_mouse + rtext_new + rtext_retext + rtext_select + rtext_width + s_ DATA + s__N DATA + s__X DATA + s_anything DATA + s_bang DATA + s_float DATA + s_list DATA + s_pointer DATA + s_signal DATA + s_symbol DATA + s_x DATA + s_y DATA + scalar_class DATA + scalar_getbasexy + scalar_new + scalar_redraw + sched_geteventno + sched_set_using_audio + sched_tick + signalinlet_new + socketreceiver_new + socketreceiver_read + startpost + symbolinlet_new + sys_addhist + sys_addpollfn + sys_advance_samples DATA + sys_audioapi DATA + sys_audiodevnametonumber + sys_audiodevnumbertoname + sys_bail + sys_bashfilename + sys_clearhist + sys_close + sys_close_audio + sys_close_midi + sys_closesocket + sys_debuglevel DATA + sys_decodedialog + sys_defaultfont DATA + sys_defeatrt DATA + sys_do_open_midi + sys_externalschedlib DATA + sys_fclose + sys_flags DATA + sys_font DATA + sys_fontheight + sys_fontweight DATA + sys_fontwidth + sys_fopen + sys_get_audio_apis + sys_get_audio_devs + sys_get_audio_params + sys_get_inchannels + sys_get_midi_apis + sys_get_midi_devs + sys_get_midi_params + sys_get_outchannels + sys_getblksize + sys_getmeters + sys_getrealtime + sys_getsr + sys_getversion + sys_gui + sys_guicmd DATA + sys_havegui + sys_hipriority DATA + sys_hostfontsize + sys_idlehook DATA + sys_init_fdpoll + sys_initmidiqueue + sys_isabsolutepath + sys_libdir DATA + sys_listdevs + sys_listmididevs + sys_load_lib + sys_loadpreferences + sys_lock + sys_log_error + sys_logerror + sys_microsleep + sys_midiapi DATA + sys_midibytein + sys_mididevnametonumber + sys_mididevnumbertoname + sys_midiindevlist DATA + sys_midioutdevlist DATA + sys_nearestfontsize + sys_nmidiin DATA + sys_nmidiout DATA + sys_noloadbang DATA + sys_open + sys_open_absolute + sys_open_midi + sys_ouch + sys_poll_midi + sys_pollgui + sys_pollmidiqueue + sys_pretendguibytes + sys_printhook DATA + sys_printtostderr DATA + sys_putmidibyte + sys_putmidimess + sys_queuegui + sys_register_loader + sys_reopen_audio + sys_reopen_midi + sys_reportidle + sys_rmpollfn + sys_save_audio_params + sys_savepreferences + sys_schedadvance DATA + sys_send_dacs + sys_set_audio_api + sys_set_audio_settings + sys_set_audio_settings_reopen + sys_set_audio_state + sys_set_midi_api + sys_setchsr + sys_setextrapath + sys_setmiditimediff + sys_sleepgrain DATA + sys_sockerror + sys_trylock + sys_trytoopenone + sys_unbashfilename + sys_unixerror + sys_unlock + sys_unqueuegui + sys_usestdpath DATA + sys_verbose DATA + sys_vgui + sys_zoom_open DATA + sys_zoomfontheight + sys_zoomfontwidth + template_find_field + template_findbyname + template_findcanvas + template_free + template_getfloat + template_getsymbol + template_match + template_new + template_notify + template_setfloat + template_setsymbol + text_drawborder + text_eraseborder + text_setto + text_widgetbehavior DATA + text_xpix + text_ypix + value_get + value_getfloat + value_release + value_setfloat + verbose + vinlet_class DATA + voutlet_class DATA + word_free + word_init + word_restore + zero_perform + zgetfn From e48d818b3c47de04975b494097751a13fbade8c4 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 30 Apr 2021 15:46:40 +0200 Subject: [PATCH 049/193] Add install rules for puredata --- cmake/PuredataConfig.cmake | 11 +++++++++++ plugins/puredata/CMakeLists.txt | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/cmake/PuredataConfig.cmake b/cmake/PuredataConfig.cmake index 1fc7f0055..11eab2fb1 100644 --- a/cmake/PuredataConfig.cmake +++ b/cmake/PuredataConfig.cmake @@ -16,6 +16,17 @@ else() set(PUREDATA_SUFFIX ".pd_linux") endif() +if(APPLE) + set(PDPLUGIN_INSTALL_DIR "$ENV{HOME}/Library/Pd" CACHE STRING + "Install destination for Puredata bundle [default: $ENV{HOME}/Library/Pd]") +elseif(MSVC) + set(PDPLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/Pd/extra" CACHE STRING + "Install destination for Puredata bundle [default: ${CMAKE_INSTALL_PREFIX}/Pd/extra]") +else() + set(PDPLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/pd/extra" CACHE STRING + "Install destination for Puredata bundle [default: ${CMAKE_INSTALL_PREFIX}/lib/pd/extra]") +endif() + if(WIN32) add_library(pdex-implib STATIC IMPORTED) if(MSVC) diff --git a/plugins/puredata/CMakeLists.txt b/plugins/puredata/CMakeLists.txt index 0ad11a25e..81176ed67 100644 --- a/plugins/puredata/CMakeLists.txt +++ b/plugins/puredata/CMakeLists.txt @@ -36,6 +36,16 @@ if(MINGW) PROPERTY LINK_FLAGS " -static") endif() +# Copy resources copy_puredata_resources(sfizz_puredata "${CMAKE_CURRENT_SOURCE_DIR}" "${PUREDATA_BINARY_DIR}") + +# Installation +if(NOT MSVC) + install(DIRECTORY "${PUREDATA_BINARY_DIR}" DESTINATION "${PDPLUGIN_INSTALL_DIR}" + COMPONENT "puredata") + bundle_dylibs(puredata + "${PDPLUGIN_INSTALL_DIR}/sfizz/sfizz${PUREDATA_SUFFIX}" + COMPONENT "puredata") +endif() From 0741da428ca08cf81a10e5b3280dfe5dafe933b8 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 30 Apr 2021 15:54:01 +0200 Subject: [PATCH 050/193] Install Puredata in the macOS CI --- scripts/appveyor/after_build.sh | 10 +++++++++- scripts/appveyor/before_build.sh | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/appveyor/after_build.sh b/scripts/appveyor/after_build.sh index fd6863834..27718a3f1 100755 --- a/scripts/appveyor/after_build.sh +++ b/scripts/appveyor/after_build.sh @@ -31,6 +31,13 @@ else codesign --sign "${CODESIGN_IDENTITY}" --keychain build.keychain --force --verbose \ "${INSTALL_DIR}"/sfizz.lv2/Contents/Frameworks/*.dylib fi + # code-sign Puredata and dylibs + codesign --sign "${CODESIGN_IDENTITY}" --keychain build.keychain --force --verbose \ + "${INSTALL_DIR}"/Puredata/*/*.pd_darwin + if ls "${INSTALL_DIR}"/Puredata/*/*.dylib &> /dev/null; then + codesign --sign "${CODESIGN_IDENTITY}" --keychain build.keychain --force --verbose \ + "${INSTALL_DIR}"/Puredata/*/*.dylib + fi fi # Create the DMG @@ -39,12 +46,13 @@ cat > sfizz-dmg.json << EOF "title": "sfizz", "background": "${APPVEYOR_BUILD_FOLDER}/mac/dmg-back.png", "window": { - "size": { "width": 500, "height": 500 } + "size": { "width": 650, "height": 500 } }, "contents": [ { "x": 100, "y": 50, "type": "file", "path": "${INSTALL_DIR}/sfizz.vst3" }, { "x": 250, "y": 50, "type": "file", "path": "${INSTALL_DIR}/sfizz.component" }, { "x": 400, "y": 50, "type": "file", "path": "${INSTALL_DIR}/sfizz.lv2" }, + { "x": 550, "y": 50, "type": "file", "path": "${INSTALL_DIR}/Puredata" }, { "x": 100, "y": 400, "type": "link", "path": "/Library/Audio/Plug-Ins/VST3" }, { "x": 250, "y": 400, "type": "link", "path": "/Library/Audio/Plug-Ins/Components" }, { "x": 400, "y": 400, "type": "link", "path": "/Library/Audio/Plug-Ins/LV2" } diff --git a/scripts/appveyor/before_build.sh b/scripts/appveyor/before_build.sh index e813e0552..479b10181 100644 --- a/scripts/appveyor/before_build.sh +++ b/scripts/appveyor/before_build.sh @@ -15,4 +15,5 @@ cmake -DCMAKE_BUILD_TYPE=Release \ -DLV2PLUGIN_INSTALL_DIR=/ \ -DVSTPLUGIN_INSTALL_DIR=/ \ -DAUPLUGIN_INSTALL_DIR=/ \ + -DPDPLUGIN_INSTALL_DIR=/Puredata \ .. From f0ec0a525598d45a91d6ca4060604c13a66bbd59 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 30 Apr 2021 16:43:32 +0200 Subject: [PATCH 051/193] Win32 installer script for puredata --- scripts/innosetup.iss.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/innosetup.iss.in b/scripts/innosetup.iss.in index 577376fa4..87159e322 100644 --- a/scripts/innosetup.iss.in +++ b/scripts/innosetup.iss.in @@ -46,6 +46,7 @@ Name: "english"; MessagesFile: "compiler:Default.isl" Name: "main"; Description: "Shared files"; Types: full custom; Flags: fixed Name: "lv2"; Description: "LV2 plugin"; Types: full custom; Name: "vst3"; Description: "VST3 plugin"; Types: full custom; +Name: "puredata"; Description: "Puredata external"; Types: full custom; [Files] Source: "sfizz.lv2\Contents\Binary\sfizz.dll"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2\Contents\Binary"; Flags: ignoreversion @@ -61,6 +62,7 @@ Source: "sfizz.vst3\Contents\@VST3_PACKAGE_ARCHITECTURE@-win\sfizz.vst3"; Compon Source: "sfizz.vst3\Contents\Resources\*"; Components: vst3; DestDir: "{commoncf}\VST3\sfizz.vst3\Contents\Resources"; Flags: recursesubdirs Source: "sfizz.vst3\Plugin.ico"; Components: vst3; DestDir: "{commoncf}\VST3\sfizz.vst3" Source: "sfizz.vst3\gpl-3.0.txt"; Components: main; DestDir: "{app}" +Source: "pd\*"; Components: puredata; DestDir: "{commoncf}\Pd"; Flags: recursesubdirs ; Note(sfizz): OS older than Windows 10 require UI fonts to be installed system-wide Source: "sfizz.vst3\Contents\Resources\Fonts\sfizz-fluentui-system-r20.ttf"; DestDir: "{fonts}"; FontInstall: "Sfizz Fluent System R20"; Flags: uninsneveruninstall; OnlyBelowVersion: 6.4 Source: "sfizz.vst3\Contents\Resources\Fonts\sfizz-fluentui-system-f20.ttf"; DestDir: "{fonts}"; FontInstall: "Sfizz Fluent System F20"; Flags: uninsneveruninstall; OnlyBelowVersion: 6.4 From 3e4da876b3da3ff5211bdbf38c24354735058070 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 30 Apr 2021 18:02:20 +0200 Subject: [PATCH 052/193] Generalized build ID for all subprojects --- {plugins/editor/cmake => cmake}/GitBuildID.cmake | 0 plugins/editor/CMakeLists.txt | 14 +++----------- src/CMakeLists.txt | 13 +++++++++++++ .../editor => src/sfizz/git-build-id}/GitBuildId.h | 4 ++++ 4 files changed, 20 insertions(+), 11 deletions(-) rename {plugins/editor/cmake => cmake}/GitBuildID.cmake (100%) rename {plugins/editor/src/editor => src/sfizz/git-build-id}/GitBuildId.h (88%) diff --git a/plugins/editor/cmake/GitBuildID.cmake b/cmake/GitBuildID.cmake similarity index 100% rename from plugins/editor/cmake/GitBuildID.cmake rename to cmake/GitBuildID.cmake diff --git a/plugins/editor/CMakeLists.txt b/plugins/editor/CMakeLists.txt index a87d58bfa..4eda6a9be 100644 --- a/plugins/editor/CMakeLists.txt +++ b/plugins/editor/CMakeLists.txt @@ -74,9 +74,7 @@ add_library(sfizz_editor STATIC EXCLUDE_FROM_ALL src/editor/layout/about.hpp src/editor/utility/vstgui_after.h src/editor/utility/vstgui_before.h - src/editor/GitBuildId.h - ${UI_FILES} - "${CMAKE_CURRENT_BINARY_DIR}/git-build-id/GitBuildId.c") + ${UI_FILES}) add_library(sfizz::editor ALIAS sfizz_editor) target_include_directories(sfizz_editor PUBLIC "src") target_link_libraries(sfizz_editor PUBLIC sfizz::messaging sfizz::plugins-common) @@ -138,11 +136,5 @@ if(NOT CMAKE_CROSSCOMPILING) endforeach() endif() -# git build identifier -add_custom_target(sfizz_editor_git_build_id - COMMAND "${CMAKE_COMMAND}" - "-DSOURCE_DIR=${PROJECT_SOURCE_DIR}" - "-DOUTPUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/git-build-id/GitBuildId.c" - "-P" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/GitBuildID.cmake" - BYPRODUCTS "git-build-id/GitBuildId.c") -add_dependencies(sfizz_editor sfizz_editor_git_build_id) +# Git build identifier +target_link_libraries(sfizz_editor PRIVATE sfizz-git-build-id) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ea627f7db..000c1eaa8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -351,6 +351,19 @@ endif() # Generic library alias add_library(sfizz::sfizz ALIAS sfizz_static) +# Git build identifier +add_custom_target(sfizz-generate-git-build-id + COMMAND "${CMAKE_COMMAND}" + "-DSOURCE_DIR=${PROJECT_SOURCE_DIR}" + "-DOUTPUT_FILE=${PROJECT_BINARY_DIR}/git-build-id/GitBuildId.c" + "-P" "${PROJECT_SOURCE_DIR}/cmake/GitBuildID.cmake" + BYPRODUCTS "${PROJECT_BINARY_DIR}/git-build-id/GitBuildId.c") +add_library(sfizz-git-build-id STATIC EXCLUDE_FROM_ALL + "sfizz/git-build-id/GitBuildId.h" + "${PROJECT_BINARY_DIR}/git-build-id/GitBuildId.c") +target_include_directories(sfizz-git-build-id PUBLIC "sfizz/git-build-id") +add_dependencies(sfizz-git-build-id sfizz-generate-git-build-id) + # Preserve generated files (Faust) set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM TRUE) diff --git a/plugins/editor/src/editor/GitBuildId.h b/src/sfizz/git-build-id/GitBuildId.h similarity index 88% rename from plugins/editor/src/editor/GitBuildId.h rename to src/sfizz/git-build-id/GitBuildId.h index 1d2e3edf9..827f93a2f 100644 --- a/plugins/editor/src/editor/GitBuildId.h +++ b/src/sfizz/git-build-id/GitBuildId.h @@ -6,7 +6,9 @@ #pragma once +#if defined(__cplusplus) extern "C" { +#endif /** * @brief Short identifier of the current head commit. @@ -14,4 +16,6 @@ extern "C" { */ extern const char* GitBuildId; +#if defined(__cplusplus) } // extern "C" +#endif From a191e9fc751d266962b6baf81adfdedcfd22b94b Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 30 Apr 2021 18:02:35 +0200 Subject: [PATCH 053/193] Display the build ID in puredata --- plugins/puredata/CMakeLists.txt | 7 ++++++- plugins/puredata/sfizz_puredata.c | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/puredata/CMakeLists.txt b/plugins/puredata/CMakeLists.txt index 81176ed67..e861cf927 100644 --- a/plugins/puredata/CMakeLists.txt +++ b/plugins/puredata/CMakeLists.txt @@ -24,7 +24,9 @@ function(copy_puredata_resources TARGET SOURCE_DIR DESTINATION_DIR) endfunction() add_pd_external(sfizz_puredata "sfizz_puredata.c") -target_compile_definitions(sfizz_puredata PRIVATE "SFIZZ_NUM_CCS=${SFIZZ_NUM_CCS}") +target_compile_definitions(sfizz_puredata PRIVATE + "SFIZZ_NUM_CCS=${SFIZZ_NUM_CCS}" + "SFIZZ_VERSION=\"${CMAKE_PROJECT_VERSION}\"") target_link_libraries(sfizz_puredata PRIVATE sfizz::sfizz) set_target_properties(sfizz_puredata PROPERTIES @@ -36,6 +38,9 @@ if(MINGW) PROPERTY LINK_FLAGS " -static") endif() +# Git build identifier +target_link_libraries(sfizz_puredata PRIVATE sfizz-git-build-id) + # Copy resources copy_puredata_resources(sfizz_puredata "${CMAKE_CURRENT_SOURCE_DIR}" diff --git a/plugins/puredata/sfizz_puredata.c b/plugins/puredata/sfizz_puredata.c index ac7f4624b..94cfca220 100644 --- a/plugins/puredata/sfizz_puredata.c +++ b/plugins/puredata/sfizz_puredata.c @@ -4,6 +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 "GitBuildId.h" #include #include #include @@ -292,7 +293,10 @@ __attribute__((visibility("default"))) #endif void sfizz_setup() { - post("sfizz external for Puredata"); + if (GitBuildId[0]) + post("sfizz external for Puredata, version '%s.%s'", SFIZZ_VERSION, GitBuildId); + else + post("sfizz external for Puredata, version '%s'", SFIZZ_VERSION); cls_sfizz_tilde = class_new( gensym("sfizz~"), From 4d841aeddf8e4af0f392d7f088b501cd2a0fd50b Mon Sep 17 00:00:00 2001 From: alexmitchell Date: Sat, 1 May 2021 19:24:05 +0930 Subject: [PATCH 054/193] Make meters 9px each --- plugins/editor/layout/main.fl | 42 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/plugins/editor/layout/main.fl b/plugins/editor/layout/main.fl index 394d9334b..08f9c9ae1 100644 --- a/plugins/editor/layout/main.fl +++ b/plugins/editor/layout/main.fl @@ -11,7 +11,7 @@ widget_class mainView {open class Background } Fl_Group {} { - comment {palette=invertedPalette} open + comment {palette=invertedPalette} open selected xywh {0 0 800 110} class LogicalGroup } { @@ -41,27 +41,27 @@ widget_class mainView {open } } Fl_Group {} {open - xywh {185 5 395 100} box ROUNDED_BOX + xywh {185 5 418 100} box ROUNDED_BOX class RoundedGroup } { Fl_Box {} { label {Separator 1} - xywh {195 41 375 5} box BORDER_BOX labeltype NO_LABEL + xywh {195 41 395 5} box BORDER_BOX labeltype NO_LABEL class HLine } Fl_Box {} { label {Separator 2} - xywh {195 73 375 5} box BORDER_BOX labeltype NO_LABEL + xywh {195 73 395 5} box BORDER_BOX labeltype NO_LABEL class HLine } Fl_Box sfzFileLabel_ { label {DefaultInstrument.sfz} comment {tag=kTagLoadSfzFile} - xywh {195 13 295 30} labelsize 20 align 20 + xywh {195 13 320 30} labelsize 20 align 20 class ClickableLabel } Fl_Box keyswitchLabel_ { - xywh {265 45 305 30} labelsize 20 align 20 + xywh {265 45 325 35} labelsize 20 align 20 class Label } Fl_Box keyswitchBadge_ { @@ -80,17 +80,17 @@ widget_class mainView {open } Fl_Button {} { comment {tag=kTagPreviousSfzFile} - xywh {495 18 25 25} labelsize 24 + xywh {515 18 25 25} labelsize 24 class PreviousFileButton } Fl_Button {} { comment {tag=kTagNextSfzFile} - xywh {520 18 25 25} labelsize 24 + xywh {540 18 25 25} labelsize 24 class NextFileButton } Fl_Button fileOperationsMenu_ { comment {tag=kTagFileOperations} - xywh {545 18 25 25} labelsize 24 + xywh {565 18 25 25} labelsize 24 class ChevronDropDown } Fl_Box infoVoicesLabel_ { @@ -99,30 +99,30 @@ widget_class mainView {open } Fl_Box {} { label {Max:} - xywh {325 78 40 25} labelsize 12 align 24 + xywh {332 78 40 25} labelsize 12 align 24 class Label } Fl_Box numVoicesLabel_ { - xywh {370 78 35 25} labelsize 12 align 16 + xywh {378 78 35 25} labelsize 12 align 16 class Label } Fl_Box {} { - label {Memory:} selected - xywh {440 78 60 25} labelsize 12 align 24 + label {Memory:} + xywh {459 78 60 25} labelsize 12 align 24 class Label } - Fl_Box memoryLabel_ {selected - xywh {505 78 60 25} labelsize 12 align 16 + Fl_Box memoryLabel_ { + xywh {525 78 60 25} labelsize 12 align 16 class Label } Fl_Button numVoicesSlider_ { comment {tag=kTagSetNumVoices} - xywh {405 82 20 20} labelsize 16 + xywh {420 82 20 20} labelsize 16 class ChevronValueDropDown } } Fl_Group {} {open - xywh {585 5 210 100} box ROUNDED_BOX + xywh {608 5 187 100} box ROUNDED_BOX class RoundedGroup } { Fl_Dial {} { @@ -137,21 +137,21 @@ widget_class mainView {open Fl_Box volumeCCKnob_ { label Volume comment {tag=kTagSetCCVolume} - xywh {595 10 70 90} box BORDER_BOX labelsize 12 align 17 + xywh {614 10 70 90} box BORDER_BOX labelsize 12 align 17 class KnobCCBox } Fl_Box panCCKnob_ { label Pan comment {tag=kTagSetCCPan} - xywh {670 10 70 90} box BORDER_BOX labelsize 12 align 17 + xywh {691 10 70 90} box BORDER_BOX labelsize 12 align 17 class KnobCCBox } Fl_Box leftMeter_ { - xywh {750 15 15 85} box BORDER_BOX + xywh {767 10 9 90} box BORDER_BOX class VMeter } Fl_Box rightMeter_ { - xywh {770 15 15 85} box BORDER_BOX + xywh {779 10 9 90} box BORDER_BOX class VMeter } } From db1a6e2e8309cec38e1ae2deb41852b129108409 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 1 May 2021 12:07:53 +0200 Subject: [PATCH 055/193] Add the generated file --- plugins/editor/src/editor/layout/main.hpp | 40 +++++++++++------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/plugins/editor/src/editor/layout/main.hpp b/plugins/editor/src/editor/layout/main.hpp index 6e296644e..5e6b208e5 100644 --- a/plugins/editor/src/editor/layout/main.hpp +++ b/plugins/editor/src/editor/layout/main.hpp @@ -20,16 +20,16 @@ view__3->addView(view__6); auto* const view__7 = createSettingsButton(CRect(111, 69, 143, 101), kTagFirstChangePanel+kPanelSettings, "", kCenterText, 30); panelButtons_[kPanelSettings] = view__7; view__3->addView(view__7); -auto* const view__8 = createRoundedGroup(CRect(185, 5, 580, 105), -1, "", kCenterText, 14); +auto* const view__8 = createRoundedGroup(CRect(185, 5, 603, 105), -1, "", kCenterText, 14); view__2->addView(view__8); -auto* const view__9 = createHLine(CRect(10, 36, 385, 41), -1, "", kCenterText, 14); +auto* const view__9 = createHLine(CRect(10, 36, 405, 41), -1, "", kCenterText, 14); view__8->addView(view__9); -auto* const view__10 = createHLine(CRect(10, 68, 385, 73), -1, "", kCenterText, 14); +auto* const view__10 = createHLine(CRect(10, 68, 405, 73), -1, "", kCenterText, 14); view__8->addView(view__10); -auto* const view__11 = createClickableLabel(CRect(10, 8, 305, 38), kTagLoadSfzFile, "DefaultInstrument.sfz", kLeftText, 20); +auto* const view__11 = createClickableLabel(CRect(10, 8, 330, 38), kTagLoadSfzFile, "DefaultInstrument.sfz", kLeftText, 20); sfzFileLabel_ = view__11; view__8->addView(view__11); -auto* const view__12 = createLabel(CRect(80, 40, 385, 70), -1, "", kLeftText, 20); +auto* const view__12 = createLabel(CRect(80, 40, 405, 75), -1, "", kLeftText, 20); keyswitchLabel_ = view__12; view__8->addView(view__12); auto* const view__13 = createBadge(CRect(10, 42, 70, 68), -1, "", kCenterText, 20); @@ -41,47 +41,47 @@ view__8->addView(view__14); view__14->setVisible(false); auto* const view__15 = createLabel(CRect(10, 73, 70, 98), -1, "Voices:", kRightText, 12); view__8->addView(view__15); -auto* const view__16 = createPreviousFileButton(CRect(310, 13, 335, 38), kTagPreviousSfzFile, "", kCenterText, 24); +auto* const view__16 = createPreviousFileButton(CRect(330, 13, 355, 38), kTagPreviousSfzFile, "", kCenterText, 24); view__8->addView(view__16); -auto* const view__17 = createNextFileButton(CRect(335, 13, 360, 38), kTagNextSfzFile, "", kCenterText, 24); +auto* const view__17 = createNextFileButton(CRect(355, 13, 380, 38), kTagNextSfzFile, "", kCenterText, 24); view__8->addView(view__17); -auto* const view__18 = createChevronDropDown(CRect(360, 13, 385, 38), kTagFileOperations, "", kCenterText, 24); +auto* const view__18 = createChevronDropDown(CRect(380, 13, 405, 38), kTagFileOperations, "", kCenterText, 24); fileOperationsMenu_ = view__18; view__8->addView(view__18); auto* const view__19 = createLabel(CRect(75, 73, 115, 98), -1, "", kCenterText, 12); infoVoicesLabel_ = view__19; view__8->addView(view__19); -auto* const view__20 = createLabel(CRect(140, 73, 180, 98), -1, "Max:", kRightText, 12); +auto* const view__20 = createLabel(CRect(147, 73, 187, 98), -1, "Max:", kRightText, 12); view__8->addView(view__20); -auto* const view__21 = createLabel(CRect(185, 73, 220, 98), -1, "", kCenterText, 12); +auto* const view__21 = createLabel(CRect(193, 73, 228, 98), -1, "", kCenterText, 12); numVoicesLabel_ = view__21; view__8->addView(view__21); -auto* const view__22 = createLabel(CRect(255, 73, 315, 98), -1, "Memory:", kRightText, 12); +auto* const view__22 = createLabel(CRect(274, 73, 334, 98), -1, "Memory:", kRightText, 12); view__8->addView(view__22); -auto* const view__23 = createLabel(CRect(320, 73, 380, 98), -1, "", kCenterText, 12); +auto* const view__23 = createLabel(CRect(340, 73, 400, 98), -1, "", kCenterText, 12); memoryLabel_ = view__23; view__8->addView(view__23); -auto* const view__24 = createChevronValueDropDown(CRect(220, 77, 240, 97), kTagSetNumVoices, "", kCenterText, 16); +auto* const view__24 = createChevronValueDropDown(CRect(235, 77, 255, 97), kTagSetNumVoices, "", kCenterText, 16); numVoicesSlider_ = view__24; view__8->addView(view__24); -auto* const view__25 = createRoundedGroup(CRect(585, 5, 795, 105), -1, "", kCenterText, 14); +auto* const view__25 = createRoundedGroup(CRect(608, 5, 795, 105), -1, "", kCenterText, 14); view__2->addView(view__25); -auto* const view__26 = createKnob48(CRect(30, 15, 78, 63), -1, "", kCenterText, 14); +auto* const view__26 = createKnob48(CRect(7, 15, 55, 63), -1, "", kCenterText, 14); view__25->addView(view__26); view__26->setVisible(false); -auto* const view__27 = createValueLabel(CRect(25, 65, 85, 70), -1, "Center", kCenterText, 12); +auto* const view__27 = createValueLabel(CRect(2, 65, 62, 70), -1, "Center", kCenterText, 12); view__25->addView(view__27); view__27->setVisible(false); -auto* const view__28 = createKnobCCBox(CRect(10, 5, 80, 95), kTagSetCCVolume, "Volume", kCenterText, 12); +auto* const view__28 = createKnobCCBox(CRect(6, 5, 76, 95), kTagSetCCVolume, "Volume", kCenterText, 12); volumeCCKnob_ = view__28; view__25->addView(view__28); -auto* const view__29 = createKnobCCBox(CRect(85, 5, 155, 95), kTagSetCCPan, "Pan", kCenterText, 12); +auto* const view__29 = createKnobCCBox(CRect(83, 5, 153, 95), kTagSetCCPan, "Pan", kCenterText, 12); panCCKnob_ = view__29; view__25->addView(view__29); -auto* const view__30 = createVMeter(CRect(165, 10, 180, 95), -1, "", kCenterText, 14); +auto* const view__30 = createVMeter(CRect(159, 5, 168, 95), -1, "", kCenterText, 14); leftMeter_ = view__30; view__25->addView(view__30); -auto* const view__31 = createVMeter(CRect(185, 10, 200, 95), -1, "", kCenterText, 14); +auto* const view__31 = createVMeter(CRect(171, 5, 180, 95), -1, "", kCenterText, 14); rightMeter_ = view__31; view__25->addView(view__31); enterPalette(defaultPalette); From e8b475906648af21e1d5ff7c56c4de2096dfabde Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 1 May 2021 20:25:59 +0200 Subject: [PATCH 056/193] Upgrade the VST to EditControllerEx1 --- plugins/vst/SfizzVstController.cpp | 10 +++++----- plugins/vst/SfizzVstController.h | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index 4cd7abfc4..7e17b1d99 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -15,7 +15,7 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) { - tresult result = EditController::initialize(context); + tresult result = EditControllerEx1::initialize(context); if (result != kResultTrue) return result; @@ -135,7 +135,7 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) tresult PLUGIN_API SfizzVstControllerNoUi::terminate() { - return EditController::terminate(); + return EditControllerEx1::terminate(); } tresult PLUGIN_API SfizzVstControllerNoUi::getMidiControllerAssignment(int32 busIndex, int16 channel, Vst::CtrlNumber midiControllerNumber, Vst::ParamID& id) @@ -166,7 +166,7 @@ tresult PLUGIN_API SfizzVstControllerNoUi::getParamStringByValue(Vst::ParamID ta } } - return EditController::getParamStringByValue(tag, valueNormalized, string); + return EditControllerEx1::getParamStringByValue(tag, valueNormalized, string); } tresult PLUGIN_API SfizzVstControllerNoUi::getParamValueByString(Vst::ParamID tag, Vst::TChar* string, Vst::ParamValue& valueNormalized) @@ -184,7 +184,7 @@ tresult PLUGIN_API SfizzVstControllerNoUi::getParamValueByString(Vst::ParamID ta } } - return EditController::getParamValueByString(tag, string, valueNormalized); + return EditControllerEx1::getParamValueByString(tag, string, valueNormalized); } tresult SfizzVstControllerNoUi::setParam(Vst::ParamID tag, float value) @@ -229,7 +229,7 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) { // Note: is expected to be called from the controller thread only - tresult result = EditController::notify(message); + tresult result = EditControllerEx1::notify(message); if (result != kResultFalse) return result; diff --git a/plugins/vst/SfizzVstController.h b/plugins/vst/SfizzVstController.h index 153bcd85c..f976d7e98 100644 --- a/plugins/vst/SfizzVstController.h +++ b/plugins/vst/SfizzVstController.h @@ -20,7 +20,7 @@ class SfizzVstEditor; using namespace Steinberg; using namespace VSTGUI; -class SfizzVstControllerNoUi : public Vst::EditController, +class SfizzVstControllerNoUi : public Vst::EditControllerEx1, public Vst::IMidiMapping { public: virtual ~SfizzVstControllerNoUi() {} @@ -38,11 +38,11 @@ class SfizzVstControllerNoUi : public Vst::EditController, tresult PLUGIN_API notify(Vst::IMessage* message) override; // interfaces - OBJ_METHODS(SfizzVstControllerNoUi, Vst::EditController) + OBJ_METHODS(SfizzVstControllerNoUi, Vst::EditControllerEx1) DEFINE_INTERFACES DEF_INTERFACE(Vst::IMidiMapping) - END_DEFINE_INTERFACES(Vst::EditController) - REFCOUNT_METHODS(Vst::EditController) + END_DEFINE_INTERFACES(Vst::EditControllerEx1) + REFCOUNT_METHODS(Vst::EditControllerEx1) protected: std::unique_ptr threadChecker_; From 911e43205add7efbcac260ec8189b635bc66ebf9 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 1 May 2021 21:50:47 +0200 Subject: [PATCH 057/193] Implement pitch names for VST3 --- plugins/vst/SfizzVstController.cpp | 50 ++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index 7e17b1d99..9b5458d9b 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -8,11 +8,16 @@ #include "SfizzVstEditor.h" #include "SfizzVstParameters.h" #include "SfizzVstIDs.h" +#include "plugin/InstrumentDescription.h" #include "base/source/fstreamer.h" #include "base/source/updatehandler.h" +#include +#include #include #include +enum { kProgramListID = 0 }; + tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) { tresult result = EditControllerEx1::initialize(context); @@ -32,6 +37,9 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) scalaUpdate_ = Steinberg::owned(new ScalaUpdate); playStateUpdate_ = Steinberg::owned(new PlayStateUpdate); + // Unit + addUnit(new Vst::Unit(Steinberg::String("Root"), Vst::kRootUnitId, Vst::kNoParentUnitId, kProgramListID)); + // Parameters Vst::ParamID pid = 0; @@ -130,6 +138,13 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) midiMapping_[i] = id; } + // Program list + IPtr list = Steinberg::owned( + new Vst::ProgramListWithPitchNames(Steinberg::String("Programs"), kProgramListID, Vst::kRootUnitId)); + list->addProgram(Steinberg::String("Default")); + addProgramList(list); + list->addRef(); + return kResultTrue; } @@ -247,6 +262,18 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) assert(false); return kResultFalse; } + + // update the program name and notify + std::string name = fs::u8path(sfzUpdate_->getPath()).filename().u8string(); + if (absl::EndsWithIgnoreCase(name, ".sfz")) + name.resize(name.size() - 4); + setProgramName(kProgramListID, 0, Steinberg::String(name.c_str())); + + FUnknownPtr unitHandler(getComponentHandler()); + if (unitHandler) + unitHandler->notifyProgramListChange(kProgramListID, 0); + + // sfzUpdate_->deferUpdate(); } else if (!strcmp(id, SfzDescriptionUpdate::getFClassID())) { @@ -254,6 +281,29 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) assert(false); return kResultFalse; } + + // parse the description blob + const InstrumentDescription desc = parseDescriptionBlob( + sfzDescriptionUpdate_->getDescription()); + + // update pitch names and notify + Vst::ProgramListWithPitchNames* list = + static_cast(getProgramList(kProgramListID)); + for (int16 pitch = 0; pitch < 128; ++pitch) { + Steinberg::String pitchName; + if (!desc.keyLabel[pitch].empty()) + pitchName = Steinberg::String(desc.keyLabel[pitch].c_str()); + else if (!desc.keyswitchLabel[pitch].empty()) + pitchName = Steinberg::String(desc.keyswitchLabel[pitch].c_str()); + + list->setPitchName(0, pitch, pitchName); + } + + FUnknownPtr unitHandler(getComponentHandler()); + if (unitHandler) + unitHandler->notifyProgramListChange(kProgramListID, 0); + + // sfzDescriptionUpdate_->deferUpdate(); } else if (!strcmp(id, ScalaUpdate::getFClassID())) { From 2839b297d5d4545a606fb98c2e8a504f2256d197 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 2 May 2021 11:57:10 +0200 Subject: [PATCH 058/193] Implement the VST keyswitch interface --- plugins/vst/SfizzVstController.cpp | 55 ++++++++++++++++++++++++++++-- plugins/vst/SfizzVstController.h | 9 ++++- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index 9b5458d9b..4ba9aa6bd 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -167,6 +167,30 @@ tresult PLUGIN_API SfizzVstControllerNoUi::getMidiControllerAssignment(int32 bus return kResultTrue; } +int32 PLUGIN_API SfizzVstControllerNoUi::getKeyswitchCount(int32 busIndex, int16 channel) +{ + (void)channel; + + if (busIndex != 0) + return 0; + + return keyswitches_.size(); +} + +tresult PLUGIN_API SfizzVstControllerNoUi::getKeyswitchInfo(int32 busIndex, int16 channel, int32 keySwitchIndex, Vst::KeyswitchInfo& info) +{ + (void)channel; + + if (busIndex != 0) + return kResultFalse; + + if (keySwitchIndex < 0 || keySwitchIndex >= keyswitches_.size()) + return kResultFalse; + + info = keyswitches_[keySwitchIndex]; + return kResultTrue; +} + tresult PLUGIN_API SfizzVstControllerNoUi::getParamStringByValue(Vst::ParamID tag, Vst::ParamValue valueNormalized, Vst::String128 string) { switch (tag) { @@ -291,9 +315,9 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) static_cast(getProgramList(kProgramListID)); for (int16 pitch = 0; pitch < 128; ++pitch) { Steinberg::String pitchName; - if (!desc.keyLabel[pitch].empty()) + if (desc.keyUsed.test(pitch) && !desc.keyLabel[pitch].empty()) pitchName = Steinberg::String(desc.keyLabel[pitch].c_str()); - else if (!desc.keyswitchLabel[pitch].empty()) + else if (desc.keyswitchUsed.test(pitch) && !desc.keyswitchLabel[pitch].empty()) pitchName = Steinberg::String(desc.keyswitchLabel[pitch].c_str()); list->setPitchName(0, pitch, pitchName); @@ -303,6 +327,33 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) if (unitHandler) unitHandler->notifyProgramListChange(kProgramListID, 0); + // update the key switches and notify + size_t idKeyswitch = 0; + for (int16 pitch = 0; pitch < 128; ++pitch) + idKeyswitch += desc.keyswitchUsed.test(pitch); + keyswitches_.resize(idKeyswitch); + + idKeyswitch = 0; + for (int16 pitch = 0; pitch < 128; ++pitch) { + if (!desc.keyswitchUsed.test(pitch)) + continue; + Vst::KeyswitchInfo info {}; + info.typeId = Vst::kNoteOnKeyswitchTypeID; + Steinberg::String(desc.keyswitchLabel[pitch].c_str()).copyTo(info.title); + Steinberg::String(desc.keyswitchLabel[pitch].c_str()).copyTo(info.shortTitle); + info.keyswitchMin = pitch; // TODO reexamine this when supporting keyswitch groups + info.keyswitchMax = pitch; // TODO reexamine this when supporting keyswitch groups + info.keyRemapped = pitch; + info.unitId = Vst::kRootUnitId; + info.flags = 0; + keyswitches_[idKeyswitch++] = info; + } + + Vst::IComponentHandler* componentHandler = getComponentHandler(); + if (componentHandler) + // NOTE(jpc) I think that's the right one, but it needs confirmation + componentHandler->restartComponent(Vst::kNoteExpressionChanged); + // sfzDescriptionUpdate_->deferUpdate(); } diff --git a/plugins/vst/SfizzVstController.h b/plugins/vst/SfizzVstController.h index f976d7e98..cf0b0d7f6 100644 --- a/plugins/vst/SfizzVstController.h +++ b/plugins/vst/SfizzVstController.h @@ -11,6 +11,7 @@ #include "public.sdk/source/vst/vstparameters.h" #include "public.sdk/source/common/threadchecker.h" #include "pluginterfaces/vst/ivstmidicontrollers.h" +#include "pluginterfaces/vst/ivstnoteexpression.h" #include "vstgui/plugin-bindings/vst3editor.h" #include #include @@ -21,7 +22,8 @@ using namespace Steinberg; using namespace VSTGUI; class SfizzVstControllerNoUi : public Vst::EditControllerEx1, - public Vst::IMidiMapping { + public Vst::IMidiMapping, + public Vst::IKeyswitchController { public: virtual ~SfizzVstControllerNoUi() {} @@ -30,6 +32,9 @@ class SfizzVstControllerNoUi : public Vst::EditControllerEx1, tresult PLUGIN_API getMidiControllerAssignment(int32 busIndex, int16 channel, Vst::CtrlNumber midiControllerNumber, Vst::ParamID& id) override; + int32 PLUGIN_API getKeyswitchCount (int32 busIndex, int16 channel) override; + tresult PLUGIN_API getKeyswitchInfo (int32 busIndex, int16 channel, int32 keySwitchIndex, Vst::KeyswitchInfo& info) override; + tresult PLUGIN_API getParamStringByValue(Vst::ParamID tag, Vst::ParamValue valueNormalized, Vst::String128 string) override; tresult PLUGIN_API getParamValueByString(Vst::ParamID tag, Vst::TChar* string, Vst::ParamValue& valueNormalized) override; @@ -41,6 +46,7 @@ class SfizzVstControllerNoUi : public Vst::EditControllerEx1, OBJ_METHODS(SfizzVstControllerNoUi, Vst::EditControllerEx1) DEFINE_INTERFACES DEF_INTERFACE(Vst::IMidiMapping) + DEF_INTERFACE(Vst::IKeyswitchController) END_DEFINE_INTERFACES(Vst::EditControllerEx1) REFCOUNT_METHODS(Vst::EditControllerEx1) @@ -52,6 +58,7 @@ class SfizzVstControllerNoUi : public Vst::EditControllerEx1, Steinberg::IPtr scalaUpdate_; Steinberg::IPtr playStateUpdate_; Vst::ParamID midiMapping_[Vst::kCountCtrlNumber] {}; + std::vector keyswitches_; }; class SfizzVstController : public SfizzVstControllerNoUi, public VSTGUI::VST3EditorDelegate { From 607596cef1492b9466ad39829782833f20f1a1c1 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 2 May 2021 15:15:57 +0200 Subject: [PATCH 059/193] Dynamic VST parameter titles --- plugins/vst/SfizzVstController.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index 4ba9aa6bd..4a788a50e 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -349,11 +349,32 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) keyswitches_[idKeyswitch++] = info; } - Vst::IComponentHandler* componentHandler = getComponentHandler(); - if (componentHandler) + if (Vst::IComponentHandler* componentHandler = getComponentHandler()) // NOTE(jpc) I think that's the right one, but it needs confirmation componentHandler->restartComponent(Vst::kNoteExpressionChanged); + // update the parameter titles and notify + for (uint32 cc = 0; cc < sfz::config::numCCs; ++cc) { + Vst::ParamID pid = kPidCC0 + cc; + Vst::Parameter* param = getParameterObject(pid); + Vst::ParameterInfo& info = param->getInfo(); + Steinberg::String title; + Steinberg::String shortTitle; + if (!desc.ccLabel[cc].empty()) { + title = desc.ccLabel[cc].c_str(); + shortTitle = title; + } + else { + title.printf("Controller %u", cc); + shortTitle.printf("CC%u", cc); + } + title.copyTo(info.title); + shortTitle.copyTo(info.shortTitle); + } + + if (Vst::IComponentHandler* componentHandler = getComponentHandler()) + componentHandler->restartComponent(Vst::kParamTitlesChanged); + // sfzDescriptionUpdate_->deferUpdate(); } From 87f3a2b2249351ae97b40cc36904bec868eac36f Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 2 May 2021 17:08:45 +0200 Subject: [PATCH 060/193] Attempt to fix rt_decay --- src/sfizz/MidiState.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sfizz/MidiState.cpp b/src/sfizz/MidiState.cpp index e82c3dba9..2072b1fda 100644 --- a/src/sfizz/MidiState.cpp +++ b/src/sfizz/MidiState.cpp @@ -119,8 +119,10 @@ float sfz::MidiState::getNoteDuration(int noteNumber, int delay) const if (noteNumber < 0 || noteNumber >= 128) return 0.0f; +#if 0 if (!noteStates[noteNumber]) return 0.0f; +#endif const unsigned timeInSamples = internalClock + static_cast(delay) - noteOnTimes[noteNumber]; return static_cast(timeInSamples) / sampleRate; From 6984ef9e75ade22458b15b4387b7ed49c29f1a8d Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 8 May 2021 11:59:02 +0200 Subject: [PATCH 061/193] Parse cpuinfo without std::regex --- plugins/editor/src/editor/NativeHelpers.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/plugins/editor/src/editor/NativeHelpers.cpp b/plugins/editor/src/editor/NativeHelpers.cpp index e4f0b3201..0be3a57bd 100644 --- a/plugins/editor/src/editor/NativeHelpers.cpp +++ b/plugins/editor/src/editor/NativeHelpers.cpp @@ -163,8 +163,9 @@ std::string getCurrentProcessName() #include #include #include +#include +#include #include -#include #include #include #include @@ -305,14 +306,22 @@ std::string getProcessorName() std::string name; std::string line; std::ifstream in("/proc/cpuinfo", std::ios::binary); - std::regex re("^model name\\s*:\\s*(.*)"); line.reserve(256); while (name.empty() && std::getline(in, line) && !line.empty()) { - std::smatch match; - if (std::regex_match(line, match, re)) - name = match[1]; + size_t pos = line.find(':'); + if (pos == line.npos) + continue; + + absl::string_view left = absl::string_view(line).substr(0, pos); + absl::string_view right = absl::string_view(line).substr(pos + 1); + + left = absl::StripAsciiWhitespace(left); + right = absl::StripAsciiWhitespace(right); + + if (left == "model name") + name = std::string(right); } if (name.empty()) From 60ad6a5e5786d912e92e4cf7484d11745f6ab559 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 8 May 2021 16:14:15 +0200 Subject: [PATCH 062/193] Eliminate the glib requirement for ui-less plugins --- plugins/CMakeLists.txt | 4 -- plugins/common/plugin/NativeHelpers.cpp | 96 ++++++++++++++++++++++--- plugins/common/plugin/NativeHelpers.h | 8 +++ plugins/common/plugin/SfizzSettings.cpp | 12 +--- 4 files changed, 96 insertions(+), 24 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 77f133748..d88dcd717 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -41,10 +41,6 @@ elseif(APPLE) target_link_libraries(plugins-common PRIVATE "${APPLE_FOUNDATION_LIBRARY}") else() - find_package(PkgConfig REQUIRED) - pkg_check_modules(GLIB REQUIRED glib-2.0) - target_include_directories(plugins-common PRIVATE ${GLIB_INCLUDE_DIRS}) - target_link_libraries(plugins-common PRIVATE ${GLIB_LIBRARIES}) endif() if((SFIZZ_LV2 AND SFIZZ_LV2_UI) OR SFIZZ_VST OR SFIZZ_AU OR SFIZZ_VST2) diff --git a/plugins/common/plugin/NativeHelpers.cpp b/plugins/common/plugin/NativeHelpers.cpp index 3ada4d939..f6988de08 100644 --- a/plugins/common/plugin/NativeHelpers.cpp +++ b/plugins/common/plugin/NativeHelpers.cpp @@ -5,6 +5,9 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "NativeHelpers.h" +#include +#include +#include #include #include @@ -25,21 +28,94 @@ const fs::path& getUserDocumentsDirectory() #elif defined(__APPLE__) // implemented in NativeHelpers.mm #else -#include - const fs::path& getUserDocumentsDirectory() { static const fs::path directory = []() -> fs::path { - const gchar* path = g_get_user_special_dir(G_USER_DIRECTORY_DOCUMENTS); - if (path) - return fs::path(path); - else { - const char* home = getenv("HOME"); - if (home && home[0] == '/') - return fs::path(home) / "Documents"; - throw std::runtime_error("Cannot get the document directory."); + for (const XdgUserDirsEntry& ent : + parseXdgUserDirs(getXdgConfigHome() / "user-dirs.dirs")) { + if (ent.name == "XDG_DOCUMENTS_DIR") + return ent.value; } + return getUserHomeDirectory() / "Documents"; + }(); + return directory; +} + +const fs::path& getUserHomeDirectory() +{ + static const fs::path directory = []() -> fs::path { + const char* home = getenv("HOME"); + if (home && home[0] == '/') + return fs::u8path(home); + else + throw std::runtime_error("Cannot get the home directory."); }(); return directory; } + +const fs::path& getXdgConfigHome() +{ + static const fs::path directory = []() -> fs::path { + const char* config = getenv("XDG_CONFIG_HOME"); + if (config && config[0] == '/') + return fs::u8path(config); + else + return getUserHomeDirectory() / ".config"; + + }(); + return directory; +} + +std::vector parseXdgUserDirs(const fs::path& userDirsPath) +{ + // from user-dirs.dirs(5) + // This file contains lines of the form `XDG_NAME_DIR=VALUE` + // VALUE must be of the form "$HOME/Path" or "/Path". + // Lines beginning with a # character are ignored. + + std::vector ents; + const fs::path& home = getUserHomeDirectory(); + + fs::ifstream in(userDirsPath); + std::string lineBuf; + + lineBuf.reserve(256); + while (std::getline(in, lineBuf)) { + absl::string_view line(lineBuf); + + line = absl::StripLeadingAsciiWhitespace(line); + if (line.empty() || line.front() == '#') + continue; + + size_t pos = line.find('='); + if (pos == line.npos) + continue; + + XdgUserDirsEntry ent; + ent.name = std::string(line.substr(0, pos)); + + absl::string_view rawValue = line.substr(pos + 1); + + rawValue = absl::StripTrailingAsciiWhitespace(rawValue); + + if (rawValue.size() < 2 || rawValue.front() != '"' || rawValue.back() != '"') + continue; + + rawValue.remove_prefix(1); + rawValue.remove_suffix(1); + + if (!rawValue.empty() && rawValue.front() == '/') + ent.value = fs::u8path(rawValue.begin(), rawValue.end()); + else if (absl::StartsWith(rawValue, "$HOME")) { + absl::string_view part = rawValue.substr(5); + ent.value = home / fs::u8path(part.begin(), part.end()).relative_path(); + } + else + continue; + + ents.push_back(std::move(ent)); + } + + return ents; +} #endif diff --git a/plugins/common/plugin/NativeHelpers.h b/plugins/common/plugin/NativeHelpers.h index 766d01d74..22407662b 100644 --- a/plugins/common/plugin/NativeHelpers.h +++ b/plugins/common/plugin/NativeHelpers.h @@ -6,5 +6,13 @@ #pragma once #include +#include const fs::path& getUserDocumentsDirectory(); + +#if !defined(_WIN32) && !defined(__APPLE__) +const fs::path& getUserHomeDirectory(); +const fs::path& getXdgConfigHome(); +struct XdgUserDirsEntry { std::string name; fs::path value; }; +std::vector parseXdgUserDirs(const fs::path& userDirsPath); +#endif diff --git a/plugins/common/plugin/SfizzSettings.cpp b/plugins/common/plugin/SfizzSettings.cpp index 10e987ab5..6e1bf5202 100644 --- a/plugins/common/plugin/SfizzSettings.cpp +++ b/plugins/common/plugin/SfizzSettings.cpp @@ -5,6 +5,7 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "SfizzSettings.h" +#include "NativeHelpers.h" #include #include @@ -112,16 +113,7 @@ bool SfizzSettings::store(const char* name, absl::string_view value) static const fs::path getSettingsPath() { - fs::path dirPath; - const char* env; - if ((env = getenv("XDG_CONFIG_HOME")) && env[0] == '/') - dirPath = fs::path(env); - else if ((env = getenv("HOME")) && env[0] == '/') - dirPath = fs::path(env) / ".config"; - else - return {}; - dirPath /= "SFZTools"; - dirPath /= "sfizz"; + const fs::path dirPath = getXdgConfigHome() / "SFZTools" / "sfizz"; std::error_code ec; fs::create_directories(dirPath, ec); if (ec) From d255f682e9cb3b9b02df8534e289b17e20982eaa Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 8 May 2021 16:15:08 +0200 Subject: [PATCH 063/193] Deduplicate win32 string conversions --- plugins/common/plugin/NativeHelpers.cpp | 22 ++++++++++++++++++++ plugins/common/plugin/NativeHelpers.h | 8 +++++++ plugins/common/plugin/SfizzSettings.cpp | 22 -------------------- plugins/editor/src/editor/NativeHelpers.cpp | 23 +-------------------- 4 files changed, 31 insertions(+), 44 deletions(-) diff --git a/plugins/common/plugin/NativeHelpers.cpp b/plugins/common/plugin/NativeHelpers.cpp index f6988de08..7df142922 100644 --- a/plugins/common/plugin/NativeHelpers.cpp +++ b/plugins/common/plugin/NativeHelpers.cpp @@ -25,6 +25,28 @@ const fs::path& getUserDocumentsDirectory() }(); return directory; } + +wchar_t *stringToWideChar(const char *str, int strCch) +{ + unsigned strSize = MultiByteToWideChar(CP_UTF8, 0, str, strCch, nullptr, 0); + if (strSize == 0) + return {}; + std::unique_ptr strW(new wchar_t[strSize]); + if (MultiByteToWideChar(CP_UTF8, 0, str, strCch, strW.get(), strSize) == 0) + return {}; + return strW.release(); +} + +char* stringToUTF8(const wchar_t *strW, int strWCch) +{ + unsigned strSize = WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, nullptr, 0, nullptr, nullptr); + if (strSize == 0) + return {}; + std::unique_ptr str(new char[strSize]); + if (WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, str.get(), strSize, nullptr, nullptr) == 0) + return {}; + return str.release(); +} #elif defined(__APPLE__) // implemented in NativeHelpers.mm #else diff --git a/plugins/common/plugin/NativeHelpers.h b/plugins/common/plugin/NativeHelpers.h index 22407662b..0b534a197 100644 --- a/plugins/common/plugin/NativeHelpers.h +++ b/plugins/common/plugin/NativeHelpers.h @@ -7,6 +7,9 @@ #pragma once #include #include +#if defined(_WIN32) +#include +#endif const fs::path& getUserDocumentsDirectory(); @@ -16,3 +19,8 @@ const fs::path& getXdgConfigHome(); struct XdgUserDirsEntry { std::string name; fs::path value; }; std::vector parseXdgUserDirs(const fs::path& userDirsPath); #endif + +#if defined(_WIN32) +wchar_t *stringToWideChar(const char *str, int strCch = -1); +char* stringToUTF8(const wchar_t *strW, int strWCch = -1); +#endif diff --git a/plugins/common/plugin/SfizzSettings.cpp b/plugins/common/plugin/SfizzSettings.cpp index 6e1bf5202..091ad6f75 100644 --- a/plugins/common/plugin/SfizzSettings.cpp +++ b/plugins/common/plugin/SfizzSettings.cpp @@ -37,28 +37,6 @@ static HKEY openRegistryKey() return key; } -static WCHAR* stringToWideChar(const char *str, int strCch = -1) -{ - unsigned strSize = MultiByteToWideChar(CP_UTF8, 0, str, strCch, nullptr, 0); - if (strSize == 0) - return {}; - std::unique_ptr strW(new WCHAR[strSize]); - if (MultiByteToWideChar(CP_UTF8, 0, str, strCch, strW.get(), strSize) == 0) - return {}; - return strW.release(); -} - -static char* stringToUTF8(const wchar_t *strW, int strWCch = -1) -{ - unsigned strSize = WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, nullptr, 0, nullptr, nullptr); - if (strSize == 0) - return {}; - std::unique_ptr str(new char[strSize]); - if (WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, str.get(), strSize, nullptr, nullptr) == 0) - return {}; - return str.release(); -} - absl::optional SfizzSettings::load(const char* name) { std::unique_ptr nameW { stringToWideChar(name) }; diff --git a/plugins/editor/src/editor/NativeHelpers.cpp b/plugins/editor/src/editor/NativeHelpers.cpp index 0be3a57bd..1fdfcedd0 100644 --- a/plugins/editor/src/editor/NativeHelpers.cpp +++ b/plugins/editor/src/editor/NativeHelpers.cpp @@ -5,34 +5,13 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "NativeHelpers.h" +#include "plugin/NativeHelpers.h" #if defined(_WIN32) #include "ghc/fs_std.hpp" #include #include -static WCHAR *stringToWideChar(const char *str, int strCch = -1) -{ - unsigned strSize = MultiByteToWideChar(CP_UTF8, 0, str, strCch, nullptr, 0); - if (strSize == 0) - return {}; - std::unique_ptr strW(new WCHAR[strSize]); - if (MultiByteToWideChar(CP_UTF8, 0, str, strCch, strW.get(), strSize) == 0) - return {}; - return strW.release(); -} - -static char* stringToUTF8(const wchar_t *strW, int strWCch = -1) -{ - unsigned strSize = WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, nullptr, 0, nullptr, nullptr); - if (strSize == 0) - return {}; - std::unique_ptr str(new char[strSize]); - if (WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, str.get(), strSize, nullptr, nullptr) == 0) - return {}; - return str.release(); -} - bool openFileInExternalEditor(const char *filename) { std::wstring path = stringToWideChar(filename); From ce6541e643301a78aa355084d05103834d141ebf Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 9 May 2021 18:45:12 +0200 Subject: [PATCH 064/193] Update simde, to fix ARMv7 builds --- external/simde | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/simde b/external/simde index 5c2f423b4..98075d059 160000 --- a/external/simde +++ b/external/simde @@ -1 +1 @@ -Subproject commit 5c2f423b41c06228e4be0cce0010a252297da4e7 +Subproject commit 98075d0593f539762125dbb215d95e782a6ae344 From 44261f2a1259f8888d8898faea97c371ed2dc28c Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 11 May 2021 18:18:02 +0200 Subject: [PATCH 065/193] Refresh the entire editor after a theme change --- plugins/editor/src/editor/Editor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/editor/src/editor/Editor.cpp b/plugins/editor/src/editor/Editor.cpp index 6c3d72e37..0671a6c88 100644 --- a/plugins/editor/src/editor/Editor.cpp +++ b/plugins/editor/src/editor/Editor.cpp @@ -1998,4 +1998,5 @@ void Editor::Impl::onThemeChanged() if (function) function(); } + frame_->invalid(); } From 2724a3490c7ff88550ef0fe8f2ce8dc138bbfc13 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 11 May 2021 18:19:23 +0200 Subject: [PATCH 066/193] Refresh after theme change, 2nd attempt --- plugins/editor/src/editor/Editor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/editor/src/editor/Editor.cpp b/plugins/editor/src/editor/Editor.cpp index 0671a6c88..276afbac4 100644 --- a/plugins/editor/src/editor/Editor.cpp +++ b/plugins/editor/src/editor/Editor.cpp @@ -1998,5 +1998,6 @@ void Editor::Impl::onThemeChanged() if (function) function(); } - frame_->invalid(); + if (CFrame* frame = frame_) + frame->invalid(); } From cbc7c912ddd056ce444efe045fd579b979e2fa72 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 11 May 2021 19:43:44 +0200 Subject: [PATCH 067/193] Rate-limit volume meter updates --- plugins/editor/src/editor/GUIComponents.cpp | 18 +++++++++++++++++- plugins/editor/src/editor/GUIComponents.h | 2 ++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/plugins/editor/src/editor/GUIComponents.cpp b/plugins/editor/src/editor/GUIComponents.cpp index 88e486092..769503fc9 100644 --- a/plugins/editor/src/editor/GUIComponents.cpp +++ b/plugins/editor/src/editor/GUIComponents.cpp @@ -991,7 +991,23 @@ void SLevelMeter::setValue(float value) return; value_ = value; - invalid(); + + // instantiate the timer lazily + if (!timer_) { + const uint32_t interval = 10; + timer_ = makeOwned( + [this](CVSTGUITimer* timer) { + timer->stop(); + timerArmed_ = false; + invalid(); + }, interval, false); + } + + // defer the update, but do not rearm the timer + if (!timerArmed_) { + timerArmed_ = true; + timer_->start(); + } } void SLevelMeter::draw(CDrawContext* dc) diff --git a/plugins/editor/src/editor/GUIComponents.h b/plugins/editor/src/editor/GUIComponents.h index dfbd0be44..8161154f2 100644 --- a/plugins/editor/src/editor/GUIComponents.h +++ b/plugins/editor/src/editor/GUIComponents.h @@ -402,6 +402,8 @@ class SLevelMeter : public CView { CColor dangerFillColor_; CColor backColor_; CCoord radius_ = 5.0; + SharedPointer timer_; + bool timerArmed_ = false; }; /// From f572cd35a23b151cd916b2c0147db6913982c666 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Thu, 13 May 2021 18:23:40 +0200 Subject: [PATCH 068/193] Work around Ardour6 parameter edits not working --- plugins/vst/SfizzVstController.cpp | 14 ++++++++++++++ plugins/vst/SfizzVstController.h | 7 ++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index 4a788a50e..a3d38f07d 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -191,6 +191,20 @@ tresult PLUGIN_API SfizzVstControllerNoUi::getKeyswitchInfo(int32 busIndex, int1 return kResultTrue; } +tresult PLUGIN_API SfizzVstControllerNoUi::beginEditFromHost(Vst::ParamID paramID) +{ + // Note(jpc) implementing this interface is a workaround to make + // non-automatable parameters editable in Ardour (as of 6.6) + (void)paramID; + return kResultTrue; +} + +tresult PLUGIN_API SfizzVstControllerNoUi::endEditFromHost(Vst::ParamID paramID) +{ + (void)paramID; + return kResultTrue; +} + tresult PLUGIN_API SfizzVstControllerNoUi::getParamStringByValue(Vst::ParamID tag, Vst::ParamValue valueNormalized, Vst::String128 string) { switch (tag) { diff --git a/plugins/vst/SfizzVstController.h b/plugins/vst/SfizzVstController.h index cf0b0d7f6..c26e4b18d 100644 --- a/plugins/vst/SfizzVstController.h +++ b/plugins/vst/SfizzVstController.h @@ -23,7 +23,8 @@ using namespace VSTGUI; class SfizzVstControllerNoUi : public Vst::EditControllerEx1, public Vst::IMidiMapping, - public Vst::IKeyswitchController { + public Vst::IKeyswitchController, + public Vst::IEditControllerHostEditing { public: virtual ~SfizzVstControllerNoUi() {} @@ -35,6 +36,9 @@ class SfizzVstControllerNoUi : public Vst::EditControllerEx1, int32 PLUGIN_API getKeyswitchCount (int32 busIndex, int16 channel) override; tresult PLUGIN_API getKeyswitchInfo (int32 busIndex, int16 channel, int32 keySwitchIndex, Vst::KeyswitchInfo& info) override; + tresult PLUGIN_API beginEditFromHost(Vst::ParamID paramID) override; + tresult PLUGIN_API endEditFromHost(Vst::ParamID paramID) override; + tresult PLUGIN_API getParamStringByValue(Vst::ParamID tag, Vst::ParamValue valueNormalized, Vst::String128 string) override; tresult PLUGIN_API getParamValueByString(Vst::ParamID tag, Vst::TChar* string, Vst::ParamValue& valueNormalized) override; @@ -47,6 +51,7 @@ class SfizzVstControllerNoUi : public Vst::EditControllerEx1, DEFINE_INTERFACES DEF_INTERFACE(Vst::IMidiMapping) DEF_INTERFACE(Vst::IKeyswitchController) + DEF_INTERFACE(Vst::IEditControllerHostEditing) END_DEFINE_INTERFACES(Vst::EditControllerEx1) REFCOUNT_METHODS(Vst::EditControllerEx1) From 5fd5a5a8085269a885256488d97252564dde1fca Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 20 May 2021 16:26:15 +0200 Subject: [PATCH 069/193] Fill gaps on opcodes related to vel and AT triggers --- src/sfizz/Defaults.cpp | 16 ++++++++-------- src/sfizz/Defaults.h | 8 ++++---- src/sfizz/Region.cpp | 16 ++++++++-------- tests/RegionValuesT.cpp | 32 ++++++++++++++++---------------- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index dece22d50..5908a3407 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -50,18 +50,18 @@ UInt8Spec loKey { 0, {0, 127}, kCanBeNote }; UInt8Spec hiKey { 127, {0, 127}, kCanBeNote }; FloatSpec loCC { 0, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; FloatSpec hiCC { 127, {0.0f, 127.0f}, kNormalizeMidi|kFillGap|kPermissiveBounds }; -FloatSpec xfoutLoCC { 127.0f, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; -FloatSpec xfoutHiCC { 127.0f, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; -FloatSpec xfinLoCC { 0.0f, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; -FloatSpec xfinHiCC { 0.0f, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; +FloatSpec xfoutLo { 127.0f, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; +FloatSpec xfoutHi { 127.0f, {0.0f, 127.0f}, kNormalizeMidi|kFillGap|kPermissiveBounds }; +FloatSpec xfinLo { 0.0f, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; +FloatSpec xfinHi { 0.0f, {0.0f, 127.0f}, kNormalizeMidi|kFillGap|kPermissiveBounds }; FloatSpec loVel { 0, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; -FloatSpec hiVel { 127, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds }; +FloatSpec hiVel { 127, {0.0f, 127.0f}, kNormalizeMidi|kFillGap|kPermissiveBounds }; FloatSpec loChannelAftertouch { 0, {0, 127}, kNormalizeMidi|kPermissiveBounds }; -FloatSpec hiChannelAftertouch { 127, {0, 127}, kNormalizeMidi|kPermissiveBounds }; +FloatSpec hiChannelAftertouch { 127, {0, 127}, kNormalizeMidi|kFillGap|kPermissiveBounds }; FloatSpec loPolyAftertouch { 0, {0, 127}, kNormalizeMidi|kPermissiveBounds }; -FloatSpec hiPolyAftertouch { 127, {0, 127}, kNormalizeMidi|kPermissiveBounds }; +FloatSpec hiPolyAftertouch { 127, {0, 127}, kNormalizeMidi|kFillGap|kPermissiveBounds }; FloatSpec loBend { -8191, {-8192.0f, 8191.0f}, kNormalizeBend|kPermissiveBounds }; -FloatSpec hiBend { 8191, {-8192.0f, 8191.0f}, kNormalizeBend|kPermissiveBounds }; +FloatSpec hiBend { 8191, {-8192.0f, 8191.0f}, kNormalizeBend|kFillGap|kPermissiveBounds }; FloatSpec loNormalized { 0.0f, {0.0f, 1.0f}, kPermissiveBounds }; FloatSpec hiNormalized { 1.0f, {0.0f, 1.0f}, kPermissiveBounds }; FloatSpec loBipolar { -1.0f, {-1.0f, 1.0f}, kPermissiveBounds }; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 7b7183ce7..71e3b0294 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -164,10 +164,10 @@ namespace Default extern const OpcodeSpec hiVel; extern const OpcodeSpec loCC; extern const OpcodeSpec hiCC; - extern const OpcodeSpec xfoutLoCC; - extern const OpcodeSpec xfoutHiCC; - extern const OpcodeSpec xfinHiCC; - extern const OpcodeSpec xfinLoCC; + extern const OpcodeSpec xfoutLo; + extern const OpcodeSpec xfoutHi; + extern const OpcodeSpec xfinHi; + extern const OpcodeSpec xfinLo; extern const OpcodeSpec loBend; extern const OpcodeSpec hiBend; extern const OpcodeSpec loNormalized; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 481cf0c8a..dc0694132 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -474,16 +474,16 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode, bool cleanOpcode) crossfadeKeyOutRange.setEnd(opcode.read(Default::hiKey)); break; case hash("xfin_lovel"): - crossfadeVelInRange.setStart(opcode.read(Default::loVel)); + crossfadeVelInRange.setStart(opcode.read(Default::xfinLo)); break; case hash("xfin_hivel"): - crossfadeVelInRange.setEnd(opcode.read(Default::loVel)); // loVel for the proper default + crossfadeVelInRange.setEnd(opcode.read(Default::xfinHi)); break; case hash("xfout_lovel"): - crossfadeVelOutRange.setStart(opcode.read(Default::hiVel)); // hiVel for the proper default + crossfadeVelOutRange.setStart(opcode.read(Default::xfoutLo)); break; case hash("xfout_hivel"): - crossfadeVelOutRange.setEnd(opcode.read(Default::hiVel)); + crossfadeVelOutRange.setEnd(opcode.read(Default::xfoutHi)); break; case hash("xf_keycurve"): crossfadeKeyCurve = opcode.read(Default::crossfadeCurve); @@ -495,28 +495,28 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode, bool cleanOpcode) if (opcode.parameters.back() >= config::numCCs) return false; crossfadeCCInRange[opcode.parameters.back()].setStart( - opcode.read(Default::xfinLoCC) + opcode.read(Default::xfinLo) ); break; case hash("xfin_hicc&"): if (opcode.parameters.back() >= config::numCCs) return false; crossfadeCCInRange[opcode.parameters.back()].setEnd( - opcode.read(Default::xfinHiCC) + opcode.read(Default::xfinHi) ); break; case hash("xfout_locc&"): if (opcode.parameters.back() >= config::numCCs) return false; crossfadeCCOutRange[opcode.parameters.back()].setStart( - opcode.read(Default::xfoutLoCC) + opcode.read(Default::xfoutLo) ); break; case hash("xfout_hicc&"): if (opcode.parameters.back() >= config::numCCs) return false; crossfadeCCOutRange[opcode.parameters.back()].setEnd( - opcode.read(Default::xfoutHiCC) + opcode.read(Default::xfoutHi) ); break; case hash("xf_cccurve"): diff --git a/tests/RegionValuesT.cpp b/tests/RegionValuesT.cpp index d9402d9d4..a6fc4dd7e 100644 --- a/tests/RegionValuesT.cpp +++ b/tests/RegionValuesT.cpp @@ -599,8 +599,8 @@ TEST_CASE("[Values] Velocity range") synth.dispatchMessage(client, 0, "/region3/vel_range", "", nullptr); std::vector expected { "/region0/vel_range,ff : { 0, 1 }", - "/region1/vel_range,ff : { 0.267717, 0.472441 }", - "/region2/vel_range,ff : { -0.023622, 0.472441 }", + "/region1/vel_range,ff : { 0.267717, 0.480315 }", + "/region2/vel_range,ff : { -0.023622, 0.480315 }", "/region3/vel_range,ff : { 0, -0.00787402 }", }; REQUIRE(messageList == expected); @@ -914,10 +914,10 @@ TEST_CASE("[Values] Aftertouch range") synth.dispatchMessage(client, 0, "/region4/chanaft_range", "", nullptr); std::vector expected { "/region0/chanaft_range,ff : { 0, 1 }", - "/region1/chanaft_range,ff : { 0.267717, 0.472441 }", - "/region2/chanaft_range,ff : { -0.023622, 0.472441 }", + "/region1/chanaft_range,ff : { 0.267717, 0.480315 }", + "/region2/chanaft_range,ff : { -0.023622, 0.480315 }", "/region3/chanaft_range,ff : { 0.15748, -0.00787402 }", - "/region4/chanaft_range,ff : { 0.15748, 0.0787402 }", + "/region4/chanaft_range,ff : { 0.15748, 0.0866142 }", }; REQUIRE(messageList == expected); } @@ -942,10 +942,10 @@ TEST_CASE("[Values] Polyaftertouch range") synth.dispatchMessage(client, 0, "/region4/polyaft_range", "", nullptr); std::vector expected { "/region0/polyaft_range,ff : { 0, 1 }", - "/region1/polyaft_range,ff : { 0.267717, 0.472441 }", - "/region2/polyaft_range,ff : { -0.023622, 0.472441 }", + "/region1/polyaft_range,ff : { 0.267717, 0.480315 }", + "/region2/polyaft_range,ff : { -0.023622, 0.480315 }", "/region3/polyaft_range,ff : { 0.15748, -0.00787402 }", - "/region4/polyaft_range,ff : { 0.15748, 0.0787402 }", + "/region4/polyaft_range,ff : { 0.15748, 0.0866142 }", }; REQUIRE(messageList == expected); } @@ -1747,8 +1747,8 @@ TEST_CASE("[Values] Crossfade velocity range") synth.dispatchMessage(client, 0, "/region3/xfin_vel_range", "", nullptr); std::vector expected { "/region0/xfin_vel_range,ff : { 0, 0 }", - "/region1/xfin_vel_range,ff : { 0.0787402, 0.314961 }", - "/region2/xfin_vel_range,ff : { -0.0787402, 0.314961 }", + "/region1/xfin_vel_range,ff : { 0.0787402, 0.322835 }", + "/region2/xfin_vel_range,ff : { -0.0787402, 0.322835 }", "/region3/xfin_vel_range,ff : { 0.0787402, 1.10236 }", }; REQUIRE(messageList == expected); @@ -1768,8 +1768,8 @@ TEST_CASE("[Values] Crossfade velocity range") synth.dispatchMessage(client, 0, "/region3/xfout_vel_range", "", nullptr); std::vector expected { "/region0/xfout_vel_range,ff : { 1, 1 }", - "/region1/xfout_vel_range,ff : { 0.0787402, 0.314961 }", - "/region2/xfout_vel_range,ff : { -0.0787402, 0.314961 }", + "/region1/xfout_vel_range,ff : { 0.0787402, 0.322835 }", + "/region2/xfout_vel_range,ff : { -0.0787402, 0.322835 }", "/region3/xfout_vel_range,ff : { 0.0787402, 1.10236 }", }; REQUIRE(messageList == expected); @@ -1868,8 +1868,8 @@ TEST_CASE("[Values] Crossfade CC range") synth.dispatchMessage(client, 0, "/region3/xfin_cc_range4", "", nullptr); std::vector expected { "/region0/xfin_cc_range4,N : { }", - "/region1/xfin_cc_range4,ff : { 0.0787402, 0.314961 }", - "/region2/xfin_cc_range4,ff : { -0.0787402, 0.314961 }", + "/region1/xfin_cc_range4,ff : { 0.0787402, 0.322835 }", + "/region2/xfin_cc_range4,ff : { -0.0787402, 0.322835 }", "/region3/xfin_cc_range4,ff : { 0.0787402, 1.10236 }", }; REQUIRE(messageList == expected); @@ -1889,8 +1889,8 @@ TEST_CASE("[Values] Crossfade CC range") synth.dispatchMessage(client, 0, "/region3/xfout_cc_range4", "", nullptr); std::vector expected { "/region0/xfout_cc_range4,N : { }", - "/region1/xfout_cc_range4,ff : { 0.0787402, 0.314961 }", - "/region2/xfout_cc_range4,ff : { -0.0787402, 0.314961 }", + "/region1/xfout_cc_range4,ff : { 0.0787402, 0.322835 }", + "/region2/xfout_cc_range4,ff : { -0.0787402, 0.322835 }", "/region3/xfout_cc_range4,ff : { 0.0787402, 1.10236 }", }; REQUIRE(messageList == expected); From 262953d52adec157f2e94bb11614d4efcad44d95 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 20 May 2021 20:18:52 +0200 Subject: [PATCH 070/193] Tweak the crossfades --- src/sfizz/ModifierHelpers.h | 17 ++++++++++------- tests/FilesT.cpp | 8 ++++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/sfizz/ModifierHelpers.h b/src/sfizz/ModifierHelpers.h index e233009db..810a16dde 100644 --- a/src/sfizz/ModifierHelpers.h +++ b/src/sfizz/ModifierHelpers.h @@ -18,15 +18,17 @@ namespace sfz { template float crossfadeIn(const sfz::Range& crossfadeRange, U value, CrossfadeCurve curve) { + constexpr float gapOffset { static_cast(normalize7Bits(1)) }; if (value < crossfadeRange.getStart()) return 0.0f; - const auto length = static_cast(crossfadeRange.length()); + const auto length = static_cast(crossfadeRange.length()) - gapOffset; if (length <= 0.0f) return 1.0f; else if (value < crossfadeRange.getEnd()) { - const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / length; + const auto distanceFromStart = static_cast(value - crossfadeRange.getStart()); + const auto crossfadePosition = distanceFromStart / length; if (curve == CrossfadeCurve::power) return sqrt(crossfadePosition); if (curve == CrossfadeCurve::gain) @@ -42,15 +44,16 @@ float crossfadeIn(const sfz::Range& crossfadeRange, U value, CrossfadeCurv template float crossfadeOut(const sfz::Range& crossfadeRange, U value, CrossfadeCurve curve) { - if (value > crossfadeRange.getEnd()) - return 0.0f; - - const auto length = static_cast(crossfadeRange.length()); + constexpr float gapOffset { static_cast(normalize7Bits(1)) }; + const auto length = static_cast(crossfadeRange.length()) - gapOffset; if (length <= 0.0f) return 1.0f; else if (value > crossfadeRange.getStart()) { - const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / length; + const auto distanceFromStart = static_cast(value - crossfadeRange.getStart()); + const auto crossfadePosition = distanceFromStart / length; + if (crossfadePosition > 1.0f) + return 0.0f; if (curve == CrossfadeCurve::power) return std::sqrt(1 - crossfadePosition); if (curve == CrossfadeCurve::gain) diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 0c94934b5..196f5bcf9 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -149,10 +149,10 @@ TEST_CASE("[Files] Group from AVL") REQUIRE(synth.getRegionView(i)->keyRange == Range(36, 36)); } - almostEqualRanges(synth.getRegionView(0)->velocityRange, { 1_norm, 26_norm }); - almostEqualRanges(synth.getRegionView(1)->velocityRange, { 27_norm, 52_norm }); - almostEqualRanges(synth.getRegionView(2)->velocityRange, { 53_norm, 77_norm }); - almostEqualRanges(synth.getRegionView(3)->velocityRange, { 78_norm, 102_norm }); + almostEqualRanges(synth.getRegionView(0)->velocityRange, { 1_norm, std::nextafter(27_norm, 0.0f) }); + almostEqualRanges(synth.getRegionView(1)->velocityRange, { 27_norm, std::nextafter(53_norm, 0.0f) }); + almostEqualRanges(synth.getRegionView(2)->velocityRange, { 53_norm, std::nextafter(78_norm, 0.0f) }); + almostEqualRanges(synth.getRegionView(3)->velocityRange, { 78_norm, std::nextafter(103_norm, 0.0f) }); almostEqualRanges(synth.getRegionView(4)->velocityRange, { 103_norm, 127_norm }); } From 13be8c9ac1d2697a60bb6cd6d22cc93fadf6945c Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 22 May 2021 00:40:13 +0200 Subject: [PATCH 071/193] Add the ability to cancel the release on the basic ADSR Envelope --- src/sfizz/ADSREnvelope.cpp | 11 +- src/sfizz/ADSREnvelope.h | 6 + src/sfizz/Voice.cpp | 6 + src/sfizz/modulations/ModGenerator.h | 9 ++ src/sfizz/modulations/ModMatrix.cpp | 13 +++ src/sfizz/modulations/ModMatrix.h | 5 + .../modulations/sources/ADSREnvelope.cpp | 104 +++++++++--------- src/sfizz/modulations/sources/ADSREnvelope.h | 1 + .../modulations/sources/ChannelAftertouch.cpp | 7 -- .../modulations/sources/ChannelAftertouch.h | 1 - .../modulations/sources/PolyAftertouch.cpp | 7 -- .../modulations/sources/PolyAftertouch.h | 1 - 12 files changed, 105 insertions(+), 66 deletions(-) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index daf0872ab..346525e32 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -122,7 +122,8 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept break; } while (count < size) { - currentValue = std::max(sustain, currentValue + transitionDelta); + if (currentValue > sustain) + currentValue = std::max(sustain, currentValue + transitionDelta); output[count++] = currentValue; } break; @@ -173,6 +174,14 @@ void ADSREnvelope::startRelease(int releaseDelay) noexcept this->releaseDelay = releaseDelay; } +void ADSREnvelope::cancelRelease(int delay) noexcept +{ + (void)delay; + currentState = State::Sustain; + shouldRelease = false; + this->releaseDelay = -1; +} + void ADSREnvelope::setReleaseTime(Float timeInSeconds) noexcept { releaseRate = secondsToExpRate(timeInSeconds); diff --git a/src/sfizz/ADSREnvelope.h b/src/sfizz/ADSREnvelope.h index f1d3d6686..ace1da7ba 100644 --- a/src/sfizz/ADSREnvelope.h +++ b/src/sfizz/ADSREnvelope.h @@ -49,6 +49,12 @@ class ADSREnvelope { * @param releaseDelay the delay before releasing in samples */ void startRelease(int releaseDelay) noexcept; + /** + * @brief Cancel a release and get back into sustain. + * + * @param delay + */ + void cancelRelease(int delay) noexcept; /** * @brief Is the envelope smoothing? * diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index fea3f196b..ed3623b2c 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -676,6 +676,12 @@ void Voice::registerCC(int delay, int ccNumber, float ccValue) noexcept if (impl.noteIsOff_ && region.loopMode != LoopMode::one_shot && sostenutoPedalReleaseCondition && sustainPedalReleaseCondition) release(delay); + + if (region.checkSustain && (impl.sustainState_ == Impl::SustainState::Sustaining) + && impl.released() && (region.trigger != Trigger::release && region.trigger != Trigger::release_key) ) { + ModMatrix& modMatrix = impl.resources_.getModMatrix(); + modMatrix.cancelRelease(impl.id_, impl.region_->getId(), delay); + } } void Voice::registerPitchWheel(int delay, float pitch) noexcept diff --git a/src/sfizz/modulations/ModGenerator.h b/src/sfizz/modulations/ModGenerator.h index 7bb6c3291..55fe46ba0 100644 --- a/src/sfizz/modulations/ModGenerator.h +++ b/src/sfizz/modulations/ModGenerator.h @@ -49,6 +49,15 @@ class ModGenerator { */ virtual void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) { (void)sourceKey; (void)voiceId; (void)delay; } + /** + * @brief Cancel the release and get back into sustain + * + * @param sourceKey identifier of the source to release + * @param voiceId the particular voice to initialize, if per-voice + * @param delay the frame time when it happens + */ + virtual void cancelRelease(const ModKey& sourceKey, NumericId voiceId, unsigned delay) { (void)sourceKey; (void)voiceId; (void)delay; } + /** * @brief Generate a cycle of the modulator * diff --git a/src/sfizz/modulations/ModMatrix.cpp b/src/sfizz/modulations/ModMatrix.cpp index 35ab05f7b..98a92c866 100644 --- a/src/sfizz/modulations/ModMatrix.cpp +++ b/src/sfizz/modulations/ModMatrix.cpp @@ -281,6 +281,19 @@ void ModMatrix::releaseVoice(NumericId voiceId, NumericId regionI } } +void ModMatrix::cancelRelease(NumericId voiceId, NumericId regionId, unsigned delay) +{ + Impl& impl = *impl_; + + ASSERT(regionId); + + const auto idNumber = static_cast(regionId.number()); + for (auto idx: impl.sourceIndicesForRegion_[idNumber]) { + const Impl::Source& source = impl.sources_[idx]; + source.gen->cancelRelease(source.key, voiceId, delay); + } +} + void ModMatrix::beginCycle(unsigned numFrames) { Impl& impl = *impl_; diff --git a/src/sfizz/modulations/ModMatrix.h b/src/sfizz/modulations/ModMatrix.h index 2cef2c0ed..2aa59fe4a 100644 --- a/src/sfizz/modulations/ModMatrix.h +++ b/src/sfizz/modulations/ModMatrix.h @@ -116,6 +116,11 @@ class ModMatrix { */ void releaseVoice(NumericId voiceId, NumericId regionId, unsigned delay); + /** + * @brief Cancel release for a given voice. + */ + void cancelRelease(NumericId voiceId, NumericId regionId, unsigned delay); + /** * @brief Start modulation processing for the entire cycle. * This clears all the buffers. diff --git a/src/sfizz/modulations/sources/ADSREnvelope.cpp b/src/sfizz/modulations/sources/ADSREnvelope.cpp index 1189791bb..0ec5af8ed 100644 --- a/src/sfizz/modulations/sources/ADSREnvelope.cpp +++ b/src/sfizz/modulations/sources/ADSREnvelope.cpp @@ -18,39 +18,65 @@ ADSREnvelopeSource::ADSREnvelopeSource(VoiceManager& manager, MidiState& state) { } -void ADSREnvelopeSource::init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) +ADSREnvelope* getEG(Voice* voice, const ModKey& key) { - Voice* voice = voiceManager_.getVoiceById(voiceId); - if (!voice) { - ASSERTFALSE; - return; + ADSREnvelope* eg = nullptr; + if (!voice) + return eg; + + switch (key.id()) { + case ModId::AmpEG: + eg = voice->getAmplitudeEG(); + break; + case ModId::PitchEG: + eg = voice->getPitchEG(); + break; + case ModId::FilEG: + eg = voice->getFilterEG(); + break; + default: + return eg; } - const Region* region = voice->getRegion(); - ADSREnvelope* eg = nullptr; + return eg; +} + +const EGDescription* getEGDescription(const Region* region, const ModKey& key) +{ const EGDescription* desc = nullptr; + if (!region) + return desc; - switch (sourceKey.id()) { + switch (key.id()) { case ModId::AmpEG: - eg = voice->getAmplitudeEG(); - ASSERT(eg); desc = ®ion->amplitudeEG; break; case ModId::PitchEG: - eg = voice->getPitchEG(); - ASSERT(eg); desc = &*region->pitchEG; break; case ModId::FilEG: - eg = voice->getFilterEG(); - ASSERT(eg); desc = &*region->filterEG; break; default: + return desc; + } + + return desc; +} + +void ADSREnvelopeSource::init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) +{ + Voice* voice = voiceManager_.getVoiceById(voiceId); + if (!voice) { ASSERTFALSE; return; } + const Region* region = voice->getRegion(); + ADSREnvelope* eg = getEG(voice, sourceKey); + const EGDescription* desc = getEGDescription(region, sourceKey); + ASSERT(eg); + const TriggerEvent& triggerEvent = voice->getTriggerEvent(); const float sampleRate = voice->getSampleRate(); eg->reset(*desc, *region, midiState_, delay, triggerEvent.value, sampleRate); @@ -64,30 +90,13 @@ void ADSREnvelopeSource::release(const ModKey& sourceKey, NumericId voice return; } - ADSREnvelope* eg = nullptr; - - switch (sourceKey.id()) { - case ModId::AmpEG: - eg = voice->getAmplitudeEG(); - ASSERT(eg); - break; - case ModId::PitchEG: - eg = voice->getPitchEG(); - ASSERT(eg); - break; - case ModId::FilEG: - eg = voice->getFilterEG(); - ASSERT(eg); - break; - default: - ASSERTFALSE; - return; - } + ADSREnvelope* eg = getEG(voice, sourceKey); + ASSERT(eg); eg->startRelease(delay); } -void ADSREnvelopeSource::generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) +void ADSREnvelopeSource::cancelRelease(const ModKey& sourceKey, NumericId voiceId, unsigned delay) { Voice* voice = voiceManager_.getVoiceById(voiceId); if (!voice) { @@ -95,26 +104,23 @@ void ADSREnvelopeSource::generate(const ModKey& sourceKey, NumericId voic return; } - ADSREnvelope* eg = nullptr; + ADSREnvelope* eg = getEG(voice, sourceKey); + ASSERT(eg); - switch (sourceKey.id()) { - case ModId::AmpEG: - eg = voice->getAmplitudeEG(); - ASSERT(eg); - break; - case ModId::PitchEG: - eg = voice->getPitchEG(); - ASSERT(eg); - break; - case ModId::FilEG: - eg = voice->getFilterEG(); - ASSERT(eg); - break; - default: + eg->cancelRelease(delay); +} + +void ADSREnvelopeSource::generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) +{ + Voice* voice = voiceManager_.getVoiceById(voiceId); + if (!voice) { ASSERTFALSE; return; } + ADSREnvelope* eg = getEG(voice, sourceKey); + ASSERT(eg); + eg->getBlock(buffer); } diff --git a/src/sfizz/modulations/sources/ADSREnvelope.h b/src/sfizz/modulations/sources/ADSREnvelope.h index d52a9ca0d..436127a76 100644 --- a/src/sfizz/modulations/sources/ADSREnvelope.h +++ b/src/sfizz/modulations/sources/ADSREnvelope.h @@ -17,6 +17,7 @@ class ADSREnvelopeSource : public ModGenerator { explicit ADSREnvelopeSource(VoiceManager &manager, MidiState& state); void init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; + void cancelRelease(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) override; private: diff --git a/src/sfizz/modulations/sources/ChannelAftertouch.cpp b/src/sfizz/modulations/sources/ChannelAftertouch.cpp index 96658aee0..4f54c67bb 100644 --- a/src/sfizz/modulations/sources/ChannelAftertouch.cpp +++ b/src/sfizz/modulations/sources/ChannelAftertouch.cpp @@ -23,13 +23,6 @@ void ChannelAftertouchSource::init(const ModKey& sourceKey, NumericId voi UNUSED(delay); } -void ChannelAftertouchSource::release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) -{ - UNUSED(sourceKey); - UNUSED(voiceId); - UNUSED(delay); -} - void ChannelAftertouchSource::generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) { UNUSED(sourceKey); diff --git a/src/sfizz/modulations/sources/ChannelAftertouch.h b/src/sfizz/modulations/sources/ChannelAftertouch.h index d162b7ecd..f6ddd5a7d 100644 --- a/src/sfizz/modulations/sources/ChannelAftertouch.h +++ b/src/sfizz/modulations/sources/ChannelAftertouch.h @@ -16,7 +16,6 @@ class ChannelAftertouchSource : public ModGenerator { public: explicit ChannelAftertouchSource(VoiceManager &manager, MidiState& state); void init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; - void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) override; private: diff --git a/src/sfizz/modulations/sources/PolyAftertouch.cpp b/src/sfizz/modulations/sources/PolyAftertouch.cpp index ce91f7bbb..5c4ef22c3 100644 --- a/src/sfizz/modulations/sources/PolyAftertouch.cpp +++ b/src/sfizz/modulations/sources/PolyAftertouch.cpp @@ -23,13 +23,6 @@ void PolyAftertouchSource::init(const ModKey& sourceKey, NumericId voiceI UNUSED(delay); } -void PolyAftertouchSource::release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) -{ - UNUSED(sourceKey); - UNUSED(voiceId); - UNUSED(delay); -} - void PolyAftertouchSource::generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) { UNUSED(sourceKey); diff --git a/src/sfizz/modulations/sources/PolyAftertouch.h b/src/sfizz/modulations/sources/PolyAftertouch.h index c70ea360b..5d37ee2a1 100644 --- a/src/sfizz/modulations/sources/PolyAftertouch.h +++ b/src/sfizz/modulations/sources/PolyAftertouch.h @@ -16,7 +16,6 @@ class PolyAftertouchSource : public ModGenerator { public: explicit PolyAftertouchSource(VoiceManager &manager, MidiState& state); void init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; - void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) override; private: From abfaf941283264eb217da595b3373fed89a0ff22 Mon Sep 17 00:00:00 2001 From: alexmitchell Date: Sun, 23 May 2021 11:37:21 +0930 Subject: [PATCH 072/193] Add info tab --- plugins/editor/layout/main.fl | 41 ++- plugins/editor/src/editor/Editor.cpp | 4 + plugins/editor/src/editor/layout/main.hpp | 363 +++++++++++----------- 3 files changed, 214 insertions(+), 194 deletions(-) diff --git a/plugins/editor/layout/main.fl b/plugins/editor/layout/main.fl index 08f9c9ae1..4f2d9d12e 100644 --- a/plugins/editor/layout/main.fl +++ b/plugins/editor/layout/main.fl @@ -6,16 +6,16 @@ widget_class mainView {open xywh {439 94 800 475} type Double class LogicalGroup visible } { - Fl_Box imageContainer_ { - image {../resources/background.png} xywh {190 110 600 280} + Fl_Box imageContainer_ {selected + image {../resources/background.png} xywh {5 110 790 285} class Background } Fl_Group {} { - comment {palette=invertedPalette} open selected + comment {palette=invertedPalette} open xywh {0 0 800 110} class LogicalGroup } { - Fl_Group {} {open + Fl_Group {} { xywh {5 4 175 101} box ROUNDED_BOX align 0 class RoundedGroup } { @@ -24,23 +24,28 @@ widget_class mainView {open image {../resources/logo_text_shaded.png} xywh {32 9 120 60} class AboutButton } - Fl_Button {panelButtons_[kPanelGeneral]} { - comment {tag=kTagFirstChangePanel+kPanelGeneral} - xywh {36 73 32 32} labelsize 30 - class HomeButton + Fl_Button {panelButtons_[kPanelInfo]} { + comment {tag=kTagFirstChangePanel+kPanelInfo} + xywh {56 73 32 32} labelsize 30 + class InfoButton } Fl_Button {panelButtons_[kPanelControls]} { comment {tag=kTagFirstChangePanel+kPanelControls} - xywh {76 73 32 32} labelsize 30 + xywh {97 73 32 32} labelsize 30 class CCButton } Fl_Button {panelButtons_[kPanelSettings]} { comment {tag=kTagFirstChangePanel+kPanelSettings} - xywh {116 73 32 32} labelsize 30 + xywh {137 73 32 32} labelsize 30 class SettingsButton } + Fl_Button {panelButtons_[kPanelGeneral]} { + comment {tag=kTagFirstChangePanel+kPanelGeneral} + xywh {16 73 32 32} labelsize 30 + class HomeButton + } } - Fl_Group {} {open + Fl_Group {} { xywh {185 5 418 100} box ROUNDED_BOX class RoundedGroup } { @@ -121,7 +126,7 @@ widget_class mainView {open class ChevronValueDropDown } } - Fl_Group {} {open + Fl_Group {} { xywh {608 5 187 100} box ROUNDED_BOX class RoundedGroup } { @@ -156,12 +161,12 @@ widget_class mainView {open } } } - Fl_Group {subPanels_[kPanelGeneral]} { + Fl_Group {subPanels_[kPanelInfo]} {open xywh {5 110 791 285} hide class LogicalGroup } { Fl_Group {} {open - xywh {5 110 175 280} box ROUNDED_BOX + xywh {5 110 790 280} box ROUNDED_BOX class RoundedGroup } { Fl_Box {} { @@ -216,7 +221,7 @@ widget_class mainView {open } } } - Fl_Group {subPanels_[kPanelControls]} {open + Fl_Group {subPanels_[kPanelControls]} { xywh {5 110 790 285} hide class LogicalGroup } { @@ -230,7 +235,7 @@ widget_class mainView {open } {} } } - Fl_Group {subPanels_[kPanelSettings]} {open + Fl_Group {subPanels_[kPanelSettings]} { xywh {5 109 790 316} class LogicalGroup } { @@ -381,4 +386,8 @@ widget_class mainView {open xywh {5 400 790 70} labelsize 12 class Piano } + Fl_Group {subPanels_[kPanelGeneral]} {open + xywh {25 130 791 285} hide + class LogicalGroup + } {} } diff --git a/plugins/editor/src/editor/Editor.cpp b/plugins/editor/src/editor/Editor.cpp index 276afbac4..676948563 100644 --- a/plugins/editor/src/editor/Editor.cpp +++ b/plugins/editor/src/editor/Editor.cpp @@ -66,6 +66,7 @@ struct Editor::Impl : EditorController::Receiver, enum { kPanelGeneral, + kPanelInfo, kPanelControls, kPanelSettings, kNumPanels, @@ -817,6 +818,9 @@ void Editor::Impl::createFrameContents() auto createHomeButton = [&createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { return createGlyphButton(u8"\ue1d6", bounds, tag, fontsize); }; + auto createInfoButton = [&createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + return createGlyphButton(u8"\ue1e7", bounds, tag, fontsize); + }; auto createCCButton = [&createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { // return createGlyphButton(u8"\ue240", bounds, tag, fontsize); return createGlyphButton(u8"\ue253", bounds, tag, fontsize); diff --git a/plugins/editor/src/editor/layout/main.hpp b/plugins/editor/src/editor/layout/main.hpp index 5e6b208e5..67198b745 100644 --- a/plugins/editor/src/editor/layout/main.hpp +++ b/plugins/editor/src/editor/layout/main.hpp @@ -1,7 +1,7 @@ /* This file is generated by the layout maker tool. */ auto* const view__0 = createLogicalGroup(CRect(0, 0, 800, 475), -1, "", kCenterText, 14); mainView = view__0; -auto* const view__1 = createBackground(CRect(190, 110, 790, 390), -1, "", kCenterText, 14); +auto* const view__1 = createBackground(CRect(5, 110, 795, 395), -1, "", kCenterText, 14); imageContainer_ = view__1; view__0->addView(view__1); enterPalette(invertedPalette); @@ -11,190 +11,197 @@ auto* const view__3 = createRoundedGroup(CRect(5, 4, 180, 105), -1, "", kCenterT view__2->addView(view__3); auto* const view__4 = createAboutButton(CRect(27, 5, 147, 65), kTagAbout, "", kCenterText, 14); view__3->addView(view__4); -auto* const view__5 = createHomeButton(CRect(31, 69, 63, 101), kTagFirstChangePanel+kPanelGeneral, "", kCenterText, 30); -panelButtons_[kPanelGeneral] = view__5; +auto* const view__5 = createInfoButton(CRect(51, 69, 83, 101), kTagFirstChangePanel+kPanelInfo, "", kCenterText, 30); +panelButtons_[kPanelInfo] = view__5; view__3->addView(view__5); -auto* const view__6 = createCCButton(CRect(71, 69, 103, 101), kTagFirstChangePanel+kPanelControls, "", kCenterText, 30); +auto* const view__6 = createCCButton(CRect(92, 69, 124, 101), kTagFirstChangePanel+kPanelControls, "", kCenterText, 30); panelButtons_[kPanelControls] = view__6; view__3->addView(view__6); -auto* const view__7 = createSettingsButton(CRect(111, 69, 143, 101), kTagFirstChangePanel+kPanelSettings, "", kCenterText, 30); +auto* const view__7 = createSettingsButton(CRect(132, 69, 164, 101), kTagFirstChangePanel+kPanelSettings, "", kCenterText, 30); panelButtons_[kPanelSettings] = view__7; view__3->addView(view__7); -auto* const view__8 = createRoundedGroup(CRect(185, 5, 603, 105), -1, "", kCenterText, 14); -view__2->addView(view__8); -auto* const view__9 = createHLine(CRect(10, 36, 405, 41), -1, "", kCenterText, 14); -view__8->addView(view__9); -auto* const view__10 = createHLine(CRect(10, 68, 405, 73), -1, "", kCenterText, 14); -view__8->addView(view__10); -auto* const view__11 = createClickableLabel(CRect(10, 8, 330, 38), kTagLoadSfzFile, "DefaultInstrument.sfz", kLeftText, 20); -sfzFileLabel_ = view__11; -view__8->addView(view__11); -auto* const view__12 = createLabel(CRect(80, 40, 405, 75), -1, "", kLeftText, 20); -keyswitchLabel_ = view__12; -view__8->addView(view__12); -auto* const view__13 = createBadge(CRect(10, 42, 70, 68), -1, "", kCenterText, 20); -keyswitchBadge_ = view__13; -view__8->addView(view__13); -auto* const view__14 = createInactiveLabel(CRect(10, 40, 370, 70), -1, "No key switch", kLeftText, 20); -keyswitchInactiveLabel_ = view__14; -view__8->addView(view__14); -view__14->setVisible(false); -auto* const view__15 = createLabel(CRect(10, 73, 70, 98), -1, "Voices:", kRightText, 12); -view__8->addView(view__15); -auto* const view__16 = createPreviousFileButton(CRect(330, 13, 355, 38), kTagPreviousSfzFile, "", kCenterText, 24); -view__8->addView(view__16); -auto* const view__17 = createNextFileButton(CRect(355, 13, 380, 38), kTagNextSfzFile, "", kCenterText, 24); -view__8->addView(view__17); -auto* const view__18 = createChevronDropDown(CRect(380, 13, 405, 38), kTagFileOperations, "", kCenterText, 24); -fileOperationsMenu_ = view__18; -view__8->addView(view__18); -auto* const view__19 = createLabel(CRect(75, 73, 115, 98), -1, "", kCenterText, 12); -infoVoicesLabel_ = view__19; -view__8->addView(view__19); -auto* const view__20 = createLabel(CRect(147, 73, 187, 98), -1, "Max:", kRightText, 12); -view__8->addView(view__20); -auto* const view__21 = createLabel(CRect(193, 73, 228, 98), -1, "", kCenterText, 12); -numVoicesLabel_ = view__21; -view__8->addView(view__21); -auto* const view__22 = createLabel(CRect(274, 73, 334, 98), -1, "Memory:", kRightText, 12); -view__8->addView(view__22); -auto* const view__23 = createLabel(CRect(340, 73, 400, 98), -1, "", kCenterText, 12); -memoryLabel_ = view__23; -view__8->addView(view__23); -auto* const view__24 = createChevronValueDropDown(CRect(235, 77, 255, 97), kTagSetNumVoices, "", kCenterText, 16); -numVoicesSlider_ = view__24; -view__8->addView(view__24); -auto* const view__25 = createRoundedGroup(CRect(608, 5, 795, 105), -1, "", kCenterText, 14); -view__2->addView(view__25); -auto* const view__26 = createKnob48(CRect(7, 15, 55, 63), -1, "", kCenterText, 14); -view__25->addView(view__26); -view__26->setVisible(false); -auto* const view__27 = createValueLabel(CRect(2, 65, 62, 70), -1, "Center", kCenterText, 12); -view__25->addView(view__27); +auto* const view__8 = createHomeButton(CRect(11, 69, 43, 101), kTagFirstChangePanel+kPanelGeneral, "", kCenterText, 30); +panelButtons_[kPanelGeneral] = view__8; +view__3->addView(view__8); +auto* const view__9 = createRoundedGroup(CRect(185, 5, 603, 105), -1, "", kCenterText, 14); +view__2->addView(view__9); +auto* const view__10 = createHLine(CRect(10, 36, 405, 41), -1, "", kCenterText, 14); +view__9->addView(view__10); +auto* const view__11 = createHLine(CRect(10, 68, 405, 73), -1, "", kCenterText, 14); +view__9->addView(view__11); +auto* const view__12 = createClickableLabel(CRect(10, 8, 330, 38), kTagLoadSfzFile, "DefaultInstrument.sfz", kLeftText, 20); +sfzFileLabel_ = view__12; +view__9->addView(view__12); +auto* const view__13 = createLabel(CRect(80, 40, 405, 75), -1, "", kLeftText, 20); +keyswitchLabel_ = view__13; +view__9->addView(view__13); +auto* const view__14 = createBadge(CRect(10, 42, 70, 68), -1, "", kCenterText, 20); +keyswitchBadge_ = view__14; +view__9->addView(view__14); +auto* const view__15 = createInactiveLabel(CRect(10, 40, 370, 70), -1, "No key switch", kLeftText, 20); +keyswitchInactiveLabel_ = view__15; +view__9->addView(view__15); +view__15->setVisible(false); +auto* const view__16 = createLabel(CRect(10, 73, 70, 98), -1, "Voices:", kRightText, 12); +view__9->addView(view__16); +auto* const view__17 = createPreviousFileButton(CRect(330, 13, 355, 38), kTagPreviousSfzFile, "", kCenterText, 24); +view__9->addView(view__17); +auto* const view__18 = createNextFileButton(CRect(355, 13, 380, 38), kTagNextSfzFile, "", kCenterText, 24); +view__9->addView(view__18); +auto* const view__19 = createChevronDropDown(CRect(380, 13, 405, 38), kTagFileOperations, "", kCenterText, 24); +fileOperationsMenu_ = view__19; +view__9->addView(view__19); +auto* const view__20 = createLabel(CRect(75, 73, 115, 98), -1, "", kCenterText, 12); +infoVoicesLabel_ = view__20; +view__9->addView(view__20); +auto* const view__21 = createLabel(CRect(147, 73, 187, 98), -1, "Max:", kRightText, 12); +view__9->addView(view__21); +auto* const view__22 = createLabel(CRect(193, 73, 228, 98), -1, "", kCenterText, 12); +numVoicesLabel_ = view__22; +view__9->addView(view__22); +auto* const view__23 = createLabel(CRect(274, 73, 334, 98), -1, "Memory:", kRightText, 12); +view__9->addView(view__23); +auto* const view__24 = createLabel(CRect(340, 73, 400, 98), -1, "", kCenterText, 12); +memoryLabel_ = view__24; +view__9->addView(view__24); +auto* const view__25 = createChevronValueDropDown(CRect(235, 77, 255, 97), kTagSetNumVoices, "", kCenterText, 16); +numVoicesSlider_ = view__25; +view__9->addView(view__25); +auto* const view__26 = createRoundedGroup(CRect(608, 5, 795, 105), -1, "", kCenterText, 14); +view__2->addView(view__26); +auto* const view__27 = createKnob48(CRect(7, 15, 55, 63), -1, "", kCenterText, 14); +view__26->addView(view__27); view__27->setVisible(false); -auto* const view__28 = createKnobCCBox(CRect(6, 5, 76, 95), kTagSetCCVolume, "Volume", kCenterText, 12); -volumeCCKnob_ = view__28; -view__25->addView(view__28); -auto* const view__29 = createKnobCCBox(CRect(83, 5, 153, 95), kTagSetCCPan, "Pan", kCenterText, 12); -panCCKnob_ = view__29; -view__25->addView(view__29); -auto* const view__30 = createVMeter(CRect(159, 5, 168, 95), -1, "", kCenterText, 14); -leftMeter_ = view__30; -view__25->addView(view__30); -auto* const view__31 = createVMeter(CRect(171, 5, 180, 95), -1, "", kCenterText, 14); -rightMeter_ = view__31; -view__25->addView(view__31); +auto* const view__28 = createValueLabel(CRect(2, 65, 62, 70), -1, "Center", kCenterText, 12); +view__26->addView(view__28); +view__28->setVisible(false); +auto* const view__29 = createKnobCCBox(CRect(6, 5, 76, 95), kTagSetCCVolume, "Volume", kCenterText, 12); +volumeCCKnob_ = view__29; +view__26->addView(view__29); +auto* const view__30 = createKnobCCBox(CRect(83, 5, 153, 95), kTagSetCCPan, "Pan", kCenterText, 12); +panCCKnob_ = view__30; +view__26->addView(view__30); +auto* const view__31 = createVMeter(CRect(159, 5, 168, 95), -1, "", kCenterText, 14); +leftMeter_ = view__31; +view__26->addView(view__31); +auto* const view__32 = createVMeter(CRect(171, 5, 180, 95), -1, "", kCenterText, 14); +rightMeter_ = view__32; +view__26->addView(view__32); enterPalette(defaultPalette); -auto* const view__32 = createLogicalGroup(CRect(5, 110, 796, 395), -1, "", kCenterText, 14); -subPanels_[kPanelGeneral] = view__32; -view__0->addView(view__32); -view__32->setVisible(false); -auto* const view__33 = createRoundedGroup(CRect(0, 0, 175, 280), -1, "", kCenterText, 14); -view__32->addView(view__33); -auto* const view__34 = createLabel(CRect(15, 10, 75, 35), -1, "Curves:", kLeftText, 14); +auto* const view__33 = createLogicalGroup(CRect(5, 110, 796, 395), -1, "", kCenterText, 14); +subPanels_[kPanelInfo] = view__33; +view__0->addView(view__33); +view__33->setVisible(false); +auto* const view__34 = createRoundedGroup(CRect(0, 0, 790, 280), -1, "", kCenterText, 14); view__33->addView(view__34); -auto* const view__35 = createLabel(CRect(15, 35, 75, 60), -1, "Masters:", kLeftText, 14); -view__33->addView(view__35); -auto* const view__36 = createLabel(CRect(15, 60, 75, 85), -1, "Groups:", kLeftText, 14); -view__33->addView(view__36); -auto* const view__37 = createLabel(CRect(15, 85, 75, 110), -1, "Regions:", kLeftText, 14); -view__33->addView(view__37); -auto* const view__38 = createLabel(CRect(15, 110, 75, 135), -1, "Samples:", kLeftText, 14); -view__33->addView(view__38); -auto* const view__39 = createLabel(CRect(115, 10, 155, 35), -1, "0", kCenterText, 14); -infoCurvesLabel_ = view__39; -view__33->addView(view__39); -auto* const view__40 = createLabel(CRect(115, 35, 155, 60), -1, "0", kCenterText, 14); -infoMastersLabel_ = view__40; -view__33->addView(view__40); -auto* const view__41 = createLabel(CRect(115, 60, 155, 85), -1, "0", kCenterText, 14); -infoGroupsLabel_ = view__41; -view__33->addView(view__41); -auto* const view__42 = createLabel(CRect(115, 85, 155, 110), -1, "0", kCenterText, 14); -infoRegionsLabel_ = view__42; -view__33->addView(view__42); -auto* const view__43 = createLabel(CRect(115, 110, 155, 135), -1, "0", kCenterText, 14); -infoSamplesLabel_ = view__43; -view__33->addView(view__43); -auto* const view__44 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14); -subPanels_[kPanelControls] = view__44; -view__0->addView(view__44); -view__44->setVisible(false); -auto* const view__45 = createRoundedGroup(CRect(0, 0, 790, 285), -1, "", kCenterText, 14); -view__44->addView(view__45); -auto* const view__46 = createControlsPanel(CRect(0, 0, 790, 285), -1, "", kCenterText, 12); -controlsPanel_ = view__46; +auto* const view__35 = createLabel(CRect(15, 10, 75, 35), -1, "Curves:", kLeftText, 14); +view__34->addView(view__35); +auto* const view__36 = createLabel(CRect(15, 35, 75, 60), -1, "Masters:", kLeftText, 14); +view__34->addView(view__36); +auto* const view__37 = createLabel(CRect(15, 60, 75, 85), -1, "Groups:", kLeftText, 14); +view__34->addView(view__37); +auto* const view__38 = createLabel(CRect(15, 85, 75, 110), -1, "Regions:", kLeftText, 14); +view__34->addView(view__38); +auto* const view__39 = createLabel(CRect(15, 110, 75, 135), -1, "Samples:", kLeftText, 14); +view__34->addView(view__39); +auto* const view__40 = createLabel(CRect(115, 10, 155, 35), -1, "0", kCenterText, 14); +infoCurvesLabel_ = view__40; +view__34->addView(view__40); +auto* const view__41 = createLabel(CRect(115, 35, 155, 60), -1, "0", kCenterText, 14); +infoMastersLabel_ = view__41; +view__34->addView(view__41); +auto* const view__42 = createLabel(CRect(115, 60, 155, 85), -1, "0", kCenterText, 14); +infoGroupsLabel_ = view__42; +view__34->addView(view__42); +auto* const view__43 = createLabel(CRect(115, 85, 155, 110), -1, "0", kCenterText, 14); +infoRegionsLabel_ = view__43; +view__34->addView(view__43); +auto* const view__44 = createLabel(CRect(115, 110, 155, 135), -1, "0", kCenterText, 14); +infoSamplesLabel_ = view__44; +view__34->addView(view__44); +auto* const view__45 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14); +subPanels_[kPanelControls] = view__45; +view__0->addView(view__45); +view__45->setVisible(false); +auto* const view__46 = createRoundedGroup(CRect(0, 0, 790, 285), -1, "", kCenterText, 14); view__45->addView(view__46); -auto* const view__47 = createLogicalGroup(CRect(5, 109, 795, 425), -1, "", kCenterText, 14); -subPanels_[kPanelSettings] = view__47; -view__0->addView(view__47); -auto* const view__48 = createTitleGroup(CRect(300, 26, 495, 126), -1, "Engine", kCenterText, 12); -view__47->addView(view__48); -auto* const view__49 = createValueMenu(CRect(25, 60, 85, 85), kTagSetOversampling, "", kCenterText, 12); -oversamplingSlider_ = view__49; +auto* const view__47 = createControlsPanel(CRect(0, 0, 790, 285), -1, "", kCenterText, 12); +controlsPanel_ = view__47; +view__46->addView(view__47); +auto* const view__48 = createLogicalGroup(CRect(5, 109, 795, 425), -1, "", kCenterText, 14); +subPanels_[kPanelSettings] = view__48; +view__0->addView(view__48); +auto* const view__49 = createTitleGroup(CRect(300, 26, 495, 126), -1, "Engine", kCenterText, 12); view__48->addView(view__49); -auto* const view__50 = createValueLabel(CRect(15, 20, 95, 45), -1, "Oversampling", kCenterText, 12); -view__48->addView(view__50); -auto* const view__51 = createValueLabel(CRect(100, 20, 180, 45), -1, "Preload size", kCenterText, 12); -view__48->addView(view__51); -auto* const view__52 = createValueMenu(CRect(110, 60, 170, 85), kTagSetPreloadSize, "", kCenterText, 12); -preloadSizeSlider_ = view__52; -view__48->addView(view__52); -auto* const view__53 = createTitleGroup(CRect(170, 161, 585, 261), -1, "Tuning", kCenterText, 12); -view__47->addView(view__53); -auto* const view__54 = createValueLabel(CRect(155, 20, 235, 45), -1, "Root key", kCenterText, 12); -view__53->addView(view__54); -auto* const view__55 = createValueMenu(CRect(250, 60, 310, 85), kTagSetTuningFrequency, "", kCenterText, 12); -tuningFrequencySlider_ = view__55; -view__53->addView(view__55); -auto* const view__56 = createValueLabel(CRect(240, 20, 320, 45), -1, "Frequency", kCenterText, 12); -view__53->addView(view__56); -auto* const view__57 = createStyledKnob(CRect(340, 45, 388, 93), kTagSetStretchedTuning, "", kCenterText, 14); -stretchedTuningSlider_ = view__57; -view__53->addView(view__57); -auto* const view__58 = createValueLabel(CRect(325, 20, 405, 45), -1, "Stretch", kCenterText, 12); -view__53->addView(view__58); -auto* const view__59 = createValueLabel(CRect(20, 20, 120, 45), -1, "Scala file", kCenterText, 12); -view__53->addView(view__59); -auto* const view__60 = createValueButton(CRect(20, 60, 120, 85), kTagLoadScalaFile, "DefaultScale", kCenterText, 12); -scalaFileButton_ = view__60; -view__53->addView(view__60); -auto* const view__61 = createValueMenu(CRect(165, 60, 200, 85), kTagSetScalaRootKey, "", kCenterText, 12); -scalaRootKeySlider_ = view__61; -view__53->addView(view__61); -auto* const view__62 = createValueMenu(CRect(200, 60, 230, 85), kTagSetScalaRootKey, "", kCenterText, 12); -scalaRootOctaveSlider_ = view__62; -view__53->addView(view__62); -auto* const view__63 = createResetSomethingButton(CRect(120, 60, 145, 85), kTagResetScalaFile, "", kCenterText, 12); -scalaResetButton_ = view__63; -view__53->addView(view__63); -auto* const view__64 = createTitleGroup(CRect(615, 161, 754, 261), -1, "Files", kCenterText, 12); -userFilesGroup_ = view__64; -view__47->addView(view__64); -auto* const view__65 = createValueLabel(CRect(20, 20, 120, 45), -1, "User SFZ folder", kCenterText, 12); -view__64->addView(view__65); -auto* const view__66 = createValueButton(CRect(20, 60, 120, 85), kTagChooseUserFilesDir, "DefaultPath", kCenterText, 12); -userFilesDirButton_ = view__66; -view__64->addView(view__66); -auto* const view__67 = createTitleGroup(CRect(525, 26, 720, 126), -1, "Quality", kCenterText, 12); -view__47->addView(view__67); -auto* const view__68 = createValueMenu(CRect(15, 60, 95, 85), kTagSetSampleQuality, "", kCenterText, 12); -sampleQualitySlider_ = view__68; -view__67->addView(view__68); -auto* const view__69 = createValueLabel(CRect(15, 20, 95, 45), -1, "Sample", kCenterText, 12); -view__67->addView(view__69); -auto* const view__70 = createValueLabel(CRect(100, 20, 180, 45), -1, "Oscillator", kCenterText, 12); -view__67->addView(view__70); -auto* const view__71 = createValueMenu(CRect(100, 60, 180, 85), kTagSetOscillatorQuality, "", kCenterText, 12); -oscillatorQualitySlider_ = view__71; -view__67->addView(view__71); -auto* const view__72 = createTitleGroup(CRect(35, 161, 140, 261), -1, "Appearance", kCenterText, 12); -view__47->addView(view__72); -auto* const view__73 = createOptionMenu(CRect(20, 60, 85, 85), kTagThemeMenu, "", kCenterText, 12); -themeMenu_ = view__73; -view__72->addView(view__73); -auto* const view__74 = createValueLabel(CRect(10, 25, 90, 50), -1, "Theme", kCenterText, 12); -view__72->addView(view__74); -auto* const view__75 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12); -piano_ = view__75; -view__0->addView(view__75); +auto* const view__50 = createValueMenu(CRect(25, 60, 85, 85), kTagSetOversampling, "", kCenterText, 12); +oversamplingSlider_ = view__50; +view__49->addView(view__50); +auto* const view__51 = createValueLabel(CRect(15, 20, 95, 45), -1, "Oversampling", kCenterText, 12); +view__49->addView(view__51); +auto* const view__52 = createValueLabel(CRect(100, 20, 180, 45), -1, "Preload size", kCenterText, 12); +view__49->addView(view__52); +auto* const view__53 = createValueMenu(CRect(110, 60, 170, 85), kTagSetPreloadSize, "", kCenterText, 12); +preloadSizeSlider_ = view__53; +view__49->addView(view__53); +auto* const view__54 = createTitleGroup(CRect(170, 161, 585, 261), -1, "Tuning", kCenterText, 12); +view__48->addView(view__54); +auto* const view__55 = createValueLabel(CRect(155, 20, 235, 45), -1, "Root key", kCenterText, 12); +view__54->addView(view__55); +auto* const view__56 = createValueMenu(CRect(250, 60, 310, 85), kTagSetTuningFrequency, "", kCenterText, 12); +tuningFrequencySlider_ = view__56; +view__54->addView(view__56); +auto* const view__57 = createValueLabel(CRect(240, 20, 320, 45), -1, "Frequency", kCenterText, 12); +view__54->addView(view__57); +auto* const view__58 = createStyledKnob(CRect(340, 45, 388, 93), kTagSetStretchedTuning, "", kCenterText, 14); +stretchedTuningSlider_ = view__58; +view__54->addView(view__58); +auto* const view__59 = createValueLabel(CRect(325, 20, 405, 45), -1, "Stretch", kCenterText, 12); +view__54->addView(view__59); +auto* const view__60 = createValueLabel(CRect(20, 20, 120, 45), -1, "Scala file", kCenterText, 12); +view__54->addView(view__60); +auto* const view__61 = createValueButton(CRect(20, 60, 120, 85), kTagLoadScalaFile, "DefaultScale", kCenterText, 12); +scalaFileButton_ = view__61; +view__54->addView(view__61); +auto* const view__62 = createValueMenu(CRect(165, 60, 200, 85), kTagSetScalaRootKey, "", kCenterText, 12); +scalaRootKeySlider_ = view__62; +view__54->addView(view__62); +auto* const view__63 = createValueMenu(CRect(200, 60, 230, 85), kTagSetScalaRootKey, "", kCenterText, 12); +scalaRootOctaveSlider_ = view__63; +view__54->addView(view__63); +auto* const view__64 = createResetSomethingButton(CRect(120, 60, 145, 85), kTagResetScalaFile, "", kCenterText, 12); +scalaResetButton_ = view__64; +view__54->addView(view__64); +auto* const view__65 = createTitleGroup(CRect(615, 161, 754, 261), -1, "Files", kCenterText, 12); +userFilesGroup_ = view__65; +view__48->addView(view__65); +auto* const view__66 = createValueLabel(CRect(20, 20, 120, 45), -1, "User SFZ folder", kCenterText, 12); +view__65->addView(view__66); +auto* const view__67 = createValueButton(CRect(20, 60, 120, 85), kTagChooseUserFilesDir, "DefaultPath", kCenterText, 12); +userFilesDirButton_ = view__67; +view__65->addView(view__67); +auto* const view__68 = createTitleGroup(CRect(525, 26, 720, 126), -1, "Quality", kCenterText, 12); +view__48->addView(view__68); +auto* const view__69 = createValueMenu(CRect(15, 60, 95, 85), kTagSetSampleQuality, "", kCenterText, 12); +sampleQualitySlider_ = view__69; +view__68->addView(view__69); +auto* const view__70 = createValueLabel(CRect(15, 20, 95, 45), -1, "Sample", kCenterText, 12); +view__68->addView(view__70); +auto* const view__71 = createValueLabel(CRect(100, 20, 180, 45), -1, "Oscillator", kCenterText, 12); +view__68->addView(view__71); +auto* const view__72 = createValueMenu(CRect(100, 60, 180, 85), kTagSetOscillatorQuality, "", kCenterText, 12); +oscillatorQualitySlider_ = view__72; +view__68->addView(view__72); +auto* const view__73 = createTitleGroup(CRect(35, 161, 140, 261), -1, "Appearance", kCenterText, 12); +view__48->addView(view__73); +auto* const view__74 = createOptionMenu(CRect(20, 60, 85, 85), kTagThemeMenu, "", kCenterText, 12); +themeMenu_ = view__74; +view__73->addView(view__74); +auto* const view__75 = createValueLabel(CRect(10, 25, 90, 50), -1, "Theme", kCenterText, 12); +view__73->addView(view__75); +auto* const view__76 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12); +piano_ = view__76; +view__0->addView(view__76); +auto* const view__77 = createLogicalGroup(CRect(25, 130, 816, 415), -1, "", kCenterText, 14); +subPanels_[kPanelGeneral] = view__77; +view__0->addView(view__77); +view__77->setVisible(false); From 41519fbb67b45a95c95f3f6c2aded4ce2921377d Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 23 May 2021 04:50:25 +0200 Subject: [PATCH 073/193] Reposition and resize --- plugins/editor/layout/main.fl | 18 +++++++++--------- plugins/editor/src/editor/layout/main.hpp | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/plugins/editor/layout/main.fl b/plugins/editor/layout/main.fl index 4f2d9d12e..e862d4322 100644 --- a/plugins/editor/layout/main.fl +++ b/plugins/editor/layout/main.fl @@ -1,12 +1,12 @@ # data file for the Fltk User Interface Designer (fluid) -version 1.0305 +version 1.0306 header_name {.h} code_name {.cxx} widget_class mainView {open - xywh {439 94 800 475} type Double + xywh {108 91 800 475} type Double class LogicalGroup visible } { - Fl_Box imageContainer_ {selected + Fl_Box imageContainer_ { image {../resources/background.png} xywh {5 110 790 285} class Background } @@ -162,11 +162,11 @@ widget_class mainView {open } } Fl_Group {subPanels_[kPanelInfo]} {open - xywh {5 110 791 285} hide + xywh {5 110 790 285} hide class LogicalGroup } { Fl_Group {} {open - xywh {5 110 790 280} box ROUNDED_BOX + xywh {5 110 790 285} box ROUNDED_BOX class RoundedGroup } { Fl_Box {} { @@ -221,7 +221,7 @@ widget_class mainView {open } } } - Fl_Group {subPanels_[kPanelControls]} { + Fl_Group {subPanels_[kPanelControls]} {open xywh {5 110 790 285} hide class LogicalGroup } { @@ -236,7 +236,7 @@ widget_class mainView {open } } Fl_Group {subPanels_[kPanelSettings]} { - xywh {5 109 790 316} + xywh {5 110 790 285} class LogicalGroup } { Fl_Group {} { @@ -386,8 +386,8 @@ widget_class mainView {open xywh {5 400 790 70} labelsize 12 class Piano } - Fl_Group {subPanels_[kPanelGeneral]} {open - xywh {25 130 791 285} hide + Fl_Group {subPanels_[kPanelGeneral]} {open selected + xywh {5 110 790 285} hide class LogicalGroup } {} } diff --git a/plugins/editor/src/editor/layout/main.hpp b/plugins/editor/src/editor/layout/main.hpp index 67198b745..a14cb9515 100644 --- a/plugins/editor/src/editor/layout/main.hpp +++ b/plugins/editor/src/editor/layout/main.hpp @@ -88,11 +88,11 @@ auto* const view__32 = createVMeter(CRect(171, 5, 180, 95), -1, "", kCenterText, rightMeter_ = view__32; view__26->addView(view__32); enterPalette(defaultPalette); -auto* const view__33 = createLogicalGroup(CRect(5, 110, 796, 395), -1, "", kCenterText, 14); +auto* const view__33 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14); subPanels_[kPanelInfo] = view__33; view__0->addView(view__33); view__33->setVisible(false); -auto* const view__34 = createRoundedGroup(CRect(0, 0, 790, 280), -1, "", kCenterText, 14); +auto* const view__34 = createRoundedGroup(CRect(0, 0, 790, 285), -1, "", kCenterText, 14); view__33->addView(view__34); auto* const view__35 = createLabel(CRect(15, 10, 75, 35), -1, "Curves:", kLeftText, 14); view__34->addView(view__35); @@ -128,10 +128,10 @@ view__45->addView(view__46); auto* const view__47 = createControlsPanel(CRect(0, 0, 790, 285), -1, "", kCenterText, 12); controlsPanel_ = view__47; view__46->addView(view__47); -auto* const view__48 = createLogicalGroup(CRect(5, 109, 795, 425), -1, "", kCenterText, 14); +auto* const view__48 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14); subPanels_[kPanelSettings] = view__48; view__0->addView(view__48); -auto* const view__49 = createTitleGroup(CRect(300, 26, 495, 126), -1, "Engine", kCenterText, 12); +auto* const view__49 = createTitleGroup(CRect(300, 25, 495, 125), -1, "Engine", kCenterText, 12); view__48->addView(view__49); auto* const view__50 = createValueMenu(CRect(25, 60, 85, 85), kTagSetOversampling, "", kCenterText, 12); oversamplingSlider_ = view__50; @@ -143,7 +143,7 @@ view__49->addView(view__52); auto* const view__53 = createValueMenu(CRect(110, 60, 170, 85), kTagSetPreloadSize, "", kCenterText, 12); preloadSizeSlider_ = view__53; view__49->addView(view__53); -auto* const view__54 = createTitleGroup(CRect(170, 161, 585, 261), -1, "Tuning", kCenterText, 12); +auto* const view__54 = createTitleGroup(CRect(170, 160, 585, 260), -1, "Tuning", kCenterText, 12); view__48->addView(view__54); auto* const view__55 = createValueLabel(CRect(155, 20, 235, 45), -1, "Root key", kCenterText, 12); view__54->addView(view__55); @@ -171,7 +171,7 @@ view__54->addView(view__63); auto* const view__64 = createResetSomethingButton(CRect(120, 60, 145, 85), kTagResetScalaFile, "", kCenterText, 12); scalaResetButton_ = view__64; view__54->addView(view__64); -auto* const view__65 = createTitleGroup(CRect(615, 161, 754, 261), -1, "Files", kCenterText, 12); +auto* const view__65 = createTitleGroup(CRect(615, 160, 754, 260), -1, "Files", kCenterText, 12); userFilesGroup_ = view__65; view__48->addView(view__65); auto* const view__66 = createValueLabel(CRect(20, 20, 120, 45), -1, "User SFZ folder", kCenterText, 12); @@ -179,7 +179,7 @@ view__65->addView(view__66); auto* const view__67 = createValueButton(CRect(20, 60, 120, 85), kTagChooseUserFilesDir, "DefaultPath", kCenterText, 12); userFilesDirButton_ = view__67; view__65->addView(view__67); -auto* const view__68 = createTitleGroup(CRect(525, 26, 720, 126), -1, "Quality", kCenterText, 12); +auto* const view__68 = createTitleGroup(CRect(525, 25, 720, 125), -1, "Quality", kCenterText, 12); view__48->addView(view__68); auto* const view__69 = createValueMenu(CRect(15, 60, 95, 85), kTagSetSampleQuality, "", kCenterText, 12); sampleQualitySlider_ = view__69; @@ -191,7 +191,7 @@ view__68->addView(view__71); auto* const view__72 = createValueMenu(CRect(100, 60, 180, 85), kTagSetOscillatorQuality, "", kCenterText, 12); oscillatorQualitySlider_ = view__72; view__68->addView(view__72); -auto* const view__73 = createTitleGroup(CRect(35, 161, 140, 261), -1, "Appearance", kCenterText, 12); +auto* const view__73 = createTitleGroup(CRect(35, 160, 140, 260), -1, "Appearance", kCenterText, 12); view__48->addView(view__73); auto* const view__74 = createOptionMenu(CRect(20, 60, 85, 85), kTagThemeMenu, "", kCenterText, 12); themeMenu_ = view__74; @@ -201,7 +201,7 @@ view__73->addView(view__75); auto* const view__76 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12); piano_ = view__76; view__0->addView(view__76); -auto* const view__77 = createLogicalGroup(CRect(25, 130, 816, 415), -1, "", kCenterText, 14); +auto* const view__77 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14); subPanels_[kPanelGeneral] = view__77; view__0->addView(view__77); view__77->setVisible(false); From ca93d723ce1001e9bc34e349e7eba81e6c29a2cb Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 23 May 2021 14:09:22 +0200 Subject: [PATCH 074/193] Don't publish the linux tarball on releases --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1feadfd70..b7abb7b5e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -429,9 +429,9 @@ jobs: - name: Set install name run: | echo "install_ref=${GITHUB_REF##*/}" >> "$GITHUB_ENV" - - uses: actions/download-artifact@v2 - with: - name: Linux tarball + # - uses: actions/download-artifact@v2 + # with: + # name: Linux tarball - uses: actions/download-artifact@v2 with: name: MOD devices tarball From 8721c34e1423a90590b026d188a5c13c4556bee8 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 23 May 2021 14:50:07 +0200 Subject: [PATCH 075/193] Add the FlexEG cancellation --- src/sfizz/FlexEnvelope.cpp | 36 +++++++++++++++---- src/sfizz/FlexEnvelope.h | 5 +++ .../modulations/sources/FlexEnvelope.cpp | 20 +++++++++++ src/sfizz/modulations/sources/FlexEnvelope.h | 1 + 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/sfizz/FlexEnvelope.cpp b/src/sfizz/FlexEnvelope.cpp index 4158978f4..98e095521 100644 --- a/src/sfizz/FlexEnvelope.cpp +++ b/src/sfizz/FlexEnvelope.cpp @@ -49,6 +49,7 @@ struct FlexEnvelope::Impl { // void process(absl::Span out); + bool advanceToStage(unsigned stageNumber); bool advanceToNextStage(); }; @@ -114,6 +115,25 @@ void FlexEnvelope::release(unsigned releaseDelay) impl.currentFramesUntilRelease_ = releaseDelay; } +void FlexEnvelope::cancelRelease(unsigned delay) +{ + Impl& impl = *impl_; + const FlexEGDescription& desc = *impl.desc_; + + if (impl.currentFramesUntilRelease_) { + // Cancel the future release + impl.currentFramesUntilRelease_ = absl::nullopt; + return; + } + + if (!impl.isReleased_) + return; + + impl.isReleased_ = false; + impl.advanceToStage(desc.sustain); + impl.stageTargetLevel_ = impl.currentLevel_; +} + unsigned FlexEnvelope::getRemainingDelay() const noexcept { const Impl& impl = *impl_; @@ -226,25 +246,29 @@ void FlexEnvelope::Impl::process(absl::Span out) } } -bool FlexEnvelope::Impl::advanceToNextStage() +bool FlexEnvelope::Impl::advanceToStage(unsigned stageNumber) { const FlexEGDescription& desc = *desc_; - unsigned nextStageNo = currentStageNumber_ + 1; - currentStageNumber_ = nextStageNo; + currentStageNumber_ = stageNumber; - if (nextStageNo >= desc.points.size()) + if (stageNumber >= desc.points.size()) return false; - const FlexEGPoint& point = desc.points[nextStageNo]; + const FlexEGPoint& point = desc.points[stageNumber]; stageSourceLevel_ = currentLevel_; stageTargetLevel_ = point.level; stageTime_ = point.time; - stageSustained_ = int(nextStageNo) == desc.sustain; + stageSustained_ = int(stageNumber) == desc.sustain; stageCurve_ = &point.curve(); currentTime_ = 0; return true; }; +bool FlexEnvelope::Impl::advanceToNextStage() +{ + return advanceToStage(currentStageNumber_ + 1); +} + } // namespace sfz diff --git a/src/sfizz/FlexEnvelope.h b/src/sfizz/FlexEnvelope.h index d8cc2bf07..6a737cf18 100644 --- a/src/sfizz/FlexEnvelope.h +++ b/src/sfizz/FlexEnvelope.h @@ -47,6 +47,11 @@ class FlexEnvelope { */ void release(unsigned releaseDelay); + /** + Cancel the release + */ + void cancelRelease(unsigned delay); + /** Get the remaining delay samples */ diff --git a/src/sfizz/modulations/sources/FlexEnvelope.cpp b/src/sfizz/modulations/sources/FlexEnvelope.cpp index 5184bc5ee..b3e85cfc2 100644 --- a/src/sfizz/modulations/sources/FlexEnvelope.cpp +++ b/src/sfizz/modulations/sources/FlexEnvelope.cpp @@ -66,6 +66,26 @@ void FlexEnvelopeSource::release(const ModKey& sourceKey, NumericId voice eg->release(delay); } +void FlexEnvelopeSource::cancelRelease(const ModKey& sourceKey, NumericId voiceId, unsigned delay) +{ + unsigned egIndex = sourceKey.parameters().N; + + Voice* voice = voiceManager_.getVoiceById(voiceId); + if (!voice) { + ASSERTFALSE; + return; + } + + const Region* region = voice->getRegion(); + if (egIndex >= region->flexEGs.size()) { + ASSERTFALSE; + return; + } + + FlexEnvelope* eg = voice->getFlexEG(egIndex); + eg->cancelRelease(delay); +} + void FlexEnvelopeSource::generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) { unsigned egIndex = sourceKey.parameters().N; diff --git a/src/sfizz/modulations/sources/FlexEnvelope.h b/src/sfizz/modulations/sources/FlexEnvelope.h index 1868ab109..81b7a2f78 100644 --- a/src/sfizz/modulations/sources/FlexEnvelope.h +++ b/src/sfizz/modulations/sources/FlexEnvelope.h @@ -16,6 +16,7 @@ class FlexEnvelopeSource : public ModGenerator { explicit FlexEnvelopeSource(VoiceManager& manager); void init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; + void cancelRelease(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) override; private: From 1153c1521171ca2a1f267a990ed6e00a7814cbb8 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 23 May 2021 15:23:12 +0200 Subject: [PATCH 076/193] Parse `hint_sustain_cancels_release` --- src/sfizz/Defaults.cpp | 1 + src/sfizz/Defaults.h | 1 + src/sfizz/Synth.cpp | 6 ++++++ src/sfizz/SynthConfig.h | 2 ++ src/sfizz/Voice.cpp | 1 + 5 files changed, 11 insertions(+) diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index 5908a3407..0cb3803c8 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -187,6 +187,7 @@ FloatSpec lofiBitred { 0.0f, {0.0f, 100.0f}, 0 }; FloatSpec lofiDecim { 0.0f, {0.0f, 100.0f}, 0 }; FloatSpec rectify { 0.0f, {0.0f, 100.0f}, 0 }; UInt32Spec stringsNumber { maxStrings, {0, maxStrings}, 0 }; +BoolSpec sustainCancelsRelease { false, {0, 1}, kEnforceBounds }; ESpec trigger { Trigger::attack, {Trigger::attack, Trigger::release_key}, 0}; ESpec crossfadeCurve { CrossfadeCurve::power, {CrossfadeCurve::gain, CrossfadeCurve::power}, 0}; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 71e3b0294..41a72607d 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -305,6 +305,7 @@ namespace Default extern const OpcodeSpec selfMask; extern const OpcodeSpec filter; extern const OpcodeSpec eq; + extern const OpcodeSpec sustainCancelsRelease; // Default/max count for objects constexpr int numEQs { 3 }; diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index c76de6166..54cca9046 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -437,6 +437,12 @@ void Synth::Impl::handleControlOpcodes(const std::vector& members) DBG("Unsupported value for hint_stealing: " << member.value); } break; + case hash("hint_sustain_cancels_release"): + { + SynthConfig& config = resources_.getSynthConfig(); + config.sustainCancelsRelease = member.read(Default::sustainCancelsRelease); + } + break; default: // Unsupported control opcode DBG("Unsupported control opcode: " << member.name); diff --git a/src/sfizz/SynthConfig.h b/src/sfizz/SynthConfig.h index dbd9414b5..f5f38d89c 100644 --- a/src/sfizz/SynthConfig.h +++ b/src/sfizz/SynthConfig.h @@ -28,5 +28,7 @@ struct SynthConfig { return freeWheeling ? freeWheelingOscillatorQuality : liveOscillatorQuality; } + + bool sustainCancelsRelease { Default::sustainCancelsRelease }; }; } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index ed3623b2c..78e96d643 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -678,6 +678,7 @@ void Voice::registerCC(int delay, int ccNumber, float ccValue) noexcept release(delay); if (region.checkSustain && (impl.sustainState_ == Impl::SustainState::Sustaining) + && impl.resources_.getSynthConfig().sustainCancelsRelease && impl.released() && (region.trigger != Trigger::release && region.trigger != Trigger::release_key) ) { ModMatrix& modMatrix = impl.resources_.getModMatrix(); modMatrix.cancelRelease(impl.id_, impl.region_->getId(), delay); From 8e13b232a30dd4357d53554b2e8be7b2197f32f7 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 23 May 2021 15:31:59 +0200 Subject: [PATCH 077/193] Add tests --- tests/SynthT.cpp | 90 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 3ca5db3fb..919163a62 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -1828,3 +1828,93 @@ TEST_CASE("[Synth] Short empty files are turned into *silence") }; REQUIRE(messageList == expected); } + +TEST_CASE("[Synth] Sustain cancels release (Flex EG)") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + hint_sustain_cancels_release=1 + sample=*sine + eg01_ampeg=1 eg01_sustain=2 + eg01_time1=0 eg01_level1=0.00 + eg01_time2=0.06 eg01_level2=0.92 + eg01_time3=1.00 eg01_level3=0.00 eg01_shape3=-3 + )"); + synth.noteOn(0, 60, 63 ); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine" } ); + synth.noteOff(0, 60, 0 ); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { } ); + synth.renderBlock(buffer); + synth.renderBlock(buffer); + synth.cc(0, 64, 127); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine" } ); +} + +TEST_CASE("[Synth] Sustain cancels release (Flex EG) is off by default") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + sample=*sine + eg01_ampeg=1 eg01_sustain=2 + eg01_time1=0 eg01_level1=0.00 + eg01_time2=0.06 eg01_level2=0.92 + eg01_time3=1.00 eg01_level3=0.00 eg01_shape3=-3 + )"); + synth.noteOn(0, 60, 63 ); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine" } ); + synth.noteOff(0, 60, 0 ); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { } ); + synth.renderBlock(buffer); + synth.renderBlock(buffer); + synth.cc(0, 64, 127); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { } ); +} + +TEST_CASE("[Synth] Sustain cancels release") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + hint_sustain_cancels_release=1 + sample=*sine ampeg_release=10 + )"); + synth.noteOn(0, 60, 63 ); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine" } ); + synth.noteOff(0, 60, 0 ); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { } ); + synth.renderBlock(buffer); + synth.renderBlock(buffer); + synth.cc(0, 64, 127); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine" } ); +} + +TEST_CASE("[Synth] Sustain cancels release is off by default") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + sample=*sine ampeg_release=10 + )"); + synth.noteOn(0, 60, 63 ); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine" } ); + synth.noteOff(0, 60, 0 ); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { } ); + synth.renderBlock(buffer); + synth.renderBlock(buffer); + synth.cc(0, 64, 127); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { } ); +} From 6b0c79b162d99f95f68750d28f4d5aa7f21ce2e4 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 23 May 2021 16:37:23 +0200 Subject: [PATCH 078/193] Remove some branches from the envelope --- src/sfizz/ADSREnvelope.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index 346525e32..873069ce4 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -123,7 +123,7 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept } while (count < size) { if (currentValue > sustain) - currentValue = std::max(sustain, currentValue + transitionDelta); + currentValue += transitionDelta; output[count++] = currentValue; } break; From 4defdce04a8fd716cdf8228e3264522b29eb8169 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 23 May 2021 17:31:48 +0200 Subject: [PATCH 079/193] Reset to default CC values --- src/sfizz/MidiState.cpp | 8 -------- src/sfizz/MidiState.h | 5 ----- src/sfizz/Synth.cpp | 9 ++++++--- tests/MidiStateT.cpp | 13 ------------- tests/SynthT.cpp | 26 ++++++++++++++++++++++++++ 5 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/sfizz/MidiState.cpp b/src/sfizz/MidiState.cpp index 2072b1fda..4abaffb83 100644 --- a/src/sfizz/MidiState.cpp +++ b/src/sfizz/MidiState.cpp @@ -230,14 +230,6 @@ void sfz::MidiState::reset() noexcept absl::c_fill(noteOffTimes, 0); } -void sfz::MidiState::resetAllControllers(int delay) noexcept -{ - for (int ccIdx = 0; ccIdx < config::numCCs; ++ccIdx) - ccEvent(delay, ccIdx, 0.0f); - - pitchBendEvent(delay, 0.0f); -} - const sfz::EventVector& sfz::MidiState::getCCEvents(int ccIdx) const noexcept { if (ccIdx < 0 || ccIdx >= config::numCCs) diff --git a/src/sfizz/MidiState.h b/src/sfizz/MidiState.h index c92f45a45..16197c2fd 100644 --- a/src/sfizz/MidiState.h +++ b/src/sfizz/MidiState.h @@ -180,11 +180,6 @@ class MidiState */ void reset() noexcept; - /** - * @brief Reset all the controllers - */ - void resetAllControllers(int delay) noexcept; - const EventVector& getCCEvents(int ccIdx) const noexcept; const EventVector& getPolyAftertouchEvents(int noteNumber) const noexcept; const EventVector& getPitchEvents() const noexcept; diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index c76de6166..c16b2e65d 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -1926,18 +1926,21 @@ void Synth::disableFreeWheeling() noexcept void Synth::Impl::resetAllControllers(int delay) noexcept { - resources_.getMidiState().resetAllControllers(delay); + MidiState& midiState = resources_.getMidiState(); + midiState.pitchBendEvent(delay, 0.0f); + for (int cc = 0; cc < config::numCCs; ++cc) + midiState.ccEvent(delay, cc, defaultCCValues_[cc]); for (auto& voice : voiceManager_) { voice.registerPitchWheel(delay, 0); for (int cc = 0; cc < config::numCCs; ++cc) - voice.registerCC(delay, cc, 0.0f); + voice.registerCC(delay, cc, defaultCCValues_[cc]); } for (const LayerPtr& layerPtr : layers_) { Layer& layer = *layerPtr; for (int cc = 0; cc < config::numCCs; ++cc) - layer.registerCC(cc, 0.0f); + layer.registerCC(cc, defaultCCValues_[cc]); } } diff --git a/tests/MidiStateT.cpp b/tests/MidiStateT.cpp index 8bb44d40a..f29c71983 100644 --- a/tests/MidiStateT.cpp +++ b/tests/MidiStateT.cpp @@ -57,19 +57,6 @@ TEST_CASE("[MidiState] Reset") REQUIRE(state.getCCValue(123) == 0_norm); } -TEST_CASE("[MidiState] Reset all controllers") -{ - sfz::MidiState state; - state.pitchBendEvent(20, 0.7f); - state.ccEvent(10, 122, 124_norm); - REQUIRE(state.getPitchBend() == 0.7f); - REQUIRE(state.getCCValue(122) == 124_norm); - state.resetAllControllers(30); - REQUIRE(state.getPitchBend() == 0.0f); - REQUIRE(state.getCCValue(122) == 0_norm); - REQUIRE(state.getCCValue(4) == 0_norm); -} - TEST_CASE("[MidiState] Set and get note velocities") { sfz::MidiState state; diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 3ca5db3fb..403dd6e52 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -1828,3 +1828,29 @@ TEST_CASE("[Synth] Short empty files are turned into *silence") }; REQUIRE(messageList == expected); } + + +TEST_CASE("[Synth] Resets all controllers to default values") +{ + sfz::Synth synth; + std::vector messageList; + sfz::Client client(&messageList); + client.setReceiveCallback(&simpleMessageReceiver); + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/default_cc.sfz", R"( + set_cc56=64 + sample=*sine + )"); + REQUIRE( synth.getHdcc(56) == 64_norm ); + REQUIRE( synth.getHdcc(78) == 0.0f ); + synth.cc(0, 56, 10); + synth.cc(0, 78, 100); + synth.renderBlock(buffer); + REQUIRE( synth.getHdcc(56) == 10_norm ); + REQUIRE( synth.getHdcc(78) == 100_norm ); + synth.cc(0, 121, 127); + synth.cc(0, 121, 0); + synth.renderBlock(buffer); + REQUIRE( synth.getHdcc(56) == 64_norm ); + REQUIRE( synth.getHdcc(78) == 0.0f ); +} From f304c0b9889535a2207e42833a00a4b33253ee7c Mon Sep 17 00:00:00 2001 From: Praash Date: Mon, 7 Jun 2021 14:51:29 +0300 Subject: [PATCH 080/193] Use group cuts with CC triggered (pedal) samples --- src/sfizz/Voice.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index fea3f196b..64ec01c8a 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -1648,7 +1648,9 @@ bool Voice::checkOffGroup(const Region* other, int delay, int noteNumber) noexce if (impl.released()) return false; - if (impl.triggerEvent_.type == TriggerEventType::NoteOn + if ( + (impl.triggerEvent_.type == TriggerEventType::NoteOn + || impl.triggerEvent_.type == TriggerEventType::CC) && region->offBy && *region->offBy == other->group && (region->group != other->group || noteNumber != impl.triggerEvent_.number)) { off(delay); From a65713cf4387611bc601ed7fd23ef2faefe27b7f Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 21 Jun 2021 20:18:15 +0200 Subject: [PATCH 081/193] Fix the build of demos --- demos/DemoParser.cpp | 1 + demos/PlotCurve.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/demos/DemoParser.cpp b/demos/DemoParser.cpp index 424dcceea..6875af7c1 100644 --- a/demos/DemoParser.cpp +++ b/demos/DemoParser.cpp @@ -1,5 +1,6 @@ #include "ui_DemoParser.h" #include "parser/Parser.h" +#include "parser/ParserListener.h" #include #include #include diff --git a/demos/PlotCurve.cpp b/demos/PlotCurve.cpp index 954a79468..f2e53d4be 100644 --- a/demos/PlotCurve.cpp +++ b/demos/PlotCurve.cpp @@ -17,6 +17,7 @@ #include "sfizz/Curve.h" #include "sfizz/parser/Parser.h" +#include "sfizz/parser/ParserListener.h" #include "absl/strings/numbers.h" #include "absl/types/span.h" #include From f4e0131ffb7f22f3370ec0630a5608da835c3ac8 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 21 Jun 2021 20:27:21 +0200 Subject: [PATCH 082/193] Allow filters to have Q less than 0dB --- src/sfizz/dsp/filters/sfz_filters.dsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sfizz/dsp/filters/sfz_filters.dsp b/src/sfizz/dsp/filters/sfz_filters.dsp index 72da63629..73cbed75b 100644 --- a/src/sfizz/dsp/filters/sfz_filters.dsp +++ b/src/sfizz/dsp/filters/sfz_filters.dsp @@ -154,7 +154,7 @@ sfz2chEqHshelf = par(i,2,sfzEqHshelf); // Filter parameters cutoff = hslider("[01] Cutoff [unit:Hz] [scale:log]", 440.0, 50.0, 10000.0, 1.0) : max(1.0) : min(20000.0); -Q = vslider("[02] Resonance [unit:dB]", 0.0, 0.0, 40.0, 0.1) : max(0.0) : min(60.0) : ba.db2linear; +Q = vslider("[02] Resonance [unit:dB]", 0.0, 0.0, 40.0, 0.1) : max(-60.0) : min(60.0) : ba.db2linear; pkShGain = vslider("[03] Peak/shelf gain [unit:dB]", 0.0, 0.0, 40.0, 0.1) : max(-120.0) : min(60.0); bandwidthOrSlope = vslider("[04] Bandwidth [unit:octave]", 1.0, 0.1, 10.0, 0.01); bandwidth = bandwidthOrSlope : max(1e-2) : min(12.0); From a1750cfe9e1f9b429c8c5d9d2ffbb1949e8247b4 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 21 Jun 2021 20:27:35 +0200 Subject: [PATCH 083/193] Generate faust files --- src/sfizz/gen/filters/sfz2chBpf2p.hxx | 2 +- src/sfizz/gen/filters/sfz2chBpf2pSv.hxx | 2 +- src/sfizz/gen/filters/sfz2chBpf4p.hxx | 2 +- src/sfizz/gen/filters/sfz2chBpf6p.hxx | 2 +- src/sfizz/gen/filters/sfz2chBrf2p.hxx | 2 +- src/sfizz/gen/filters/sfz2chBrf2pSv.hxx | 2 +- src/sfizz/gen/filters/sfz2chHpf2p.hxx | 2 +- src/sfizz/gen/filters/sfz2chHpf2pSv.hxx | 2 +- src/sfizz/gen/filters/sfz2chHpf4p.hxx | 2 +- src/sfizz/gen/filters/sfz2chHpf6p.hxx | 2 +- src/sfizz/gen/filters/sfz2chHsh.hxx | 2 +- src/sfizz/gen/filters/sfz2chLpf2p.hxx | 2 +- src/sfizz/gen/filters/sfz2chLpf2pSv.hxx | 2 +- src/sfizz/gen/filters/sfz2chLpf4p.hxx | 2 +- src/sfizz/gen/filters/sfz2chLpf6p.hxx | 2 +- src/sfizz/gen/filters/sfz2chLsh.hxx | 2 +- src/sfizz/gen/filters/sfz2chPeq.hxx | 2 +- src/sfizz/gen/filters/sfzBpf2p.hxx | 2 +- src/sfizz/gen/filters/sfzBpf2pSv.hxx | 2 +- src/sfizz/gen/filters/sfzBpf4p.hxx | 2 +- src/sfizz/gen/filters/sfzBpf6p.hxx | 2 +- src/sfizz/gen/filters/sfzBrf2p.hxx | 2 +- src/sfizz/gen/filters/sfzBrf2pSv.hxx | 2 +- src/sfizz/gen/filters/sfzHpf2p.hxx | 2 +- src/sfizz/gen/filters/sfzHpf2pSv.hxx | 2 +- src/sfizz/gen/filters/sfzHpf4p.hxx | 2 +- src/sfizz/gen/filters/sfzHpf6p.hxx | 2 +- src/sfizz/gen/filters/sfzHsh.hxx | 2 +- src/sfizz/gen/filters/sfzLpf2p.hxx | 2 +- src/sfizz/gen/filters/sfzLpf2pSv.hxx | 2 +- src/sfizz/gen/filters/sfzLpf4p.hxx | 2 +- src/sfizz/gen/filters/sfzLpf6p.hxx | 2 +- src/sfizz/gen/filters/sfzLsh.hxx | 2 +- src/sfizz/gen/filters/sfzPeq.hxx | 2 +- 34 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/sfizz/gen/filters/sfz2chBpf2p.hxx b/src/sfizz/gen/filters/sfz2chBpf2p.hxx index 5c591b9ec..9d6451e6f 100644 --- a/src/sfizz/gen/filters/sfz2chBpf2p.hxx +++ b/src/sfizz/gen/filters/sfz2chBpf2p.hxx @@ -171,7 +171,7 @@ class faust2chBpf2p : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow2 = std::sin(fSlow1); - double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))); + double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))); double fSlow4 = (0.5 * (fSlow2 / fSlow3)); double fSlow5 = (fSlow4 + 1.0); double fSlow6 = (0.5 * (fSlow2 / (fSlow3 * fSlow5))); diff --git a/src/sfizz/gen/filters/sfz2chBpf2pSv.hxx b/src/sfizz/gen/filters/sfz2chBpf2pSv.hxx index 398dfec1e..9feadd358 100644 --- a/src/sfizz/gen/filters/sfz2chBpf2pSv.hxx +++ b/src/sfizz/gen/filters/sfz2chBpf2pSv.hxx @@ -139,7 +139,7 @@ class faust2chBpf2pSv : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (1.0 - fSlow0); double fSlow2 = (std::tan((fConst2 * std::min(20000.0, std::max(1.0, double(fHslider0))))) * fSlow1); - double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))); + double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); diff --git a/src/sfizz/gen/filters/sfz2chBpf4p.hxx b/src/sfizz/gen/filters/sfz2chBpf4p.hxx index cb7113d0b..ca2bea90e 100644 --- a/src/sfizz/gen/filters/sfz2chBpf4p.hxx +++ b/src/sfizz/gen/filters/sfz2chBpf4p.hxx @@ -211,7 +211,7 @@ class faust2chBpf4p : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow2 = std::sin(fSlow1); - double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))); + double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))); double fSlow4 = (0.5 * (fSlow2 / fSlow3)); double fSlow5 = (fSlow4 + 1.0); double fSlow6 = (0.5 * (fSlow2 / (fSlow3 * fSlow5))); diff --git a/src/sfizz/gen/filters/sfz2chBpf6p.hxx b/src/sfizz/gen/filters/sfz2chBpf6p.hxx index 78495246b..14c89256e 100644 --- a/src/sfizz/gen/filters/sfz2chBpf6p.hxx +++ b/src/sfizz/gen/filters/sfz2chBpf6p.hxx @@ -251,7 +251,7 @@ class faust2chBpf6p : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow2 = std::sin(fSlow1); - double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))); + double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))); double fSlow4 = (0.5 * (fSlow2 / fSlow3)); double fSlow5 = (fSlow4 + 1.0); double fSlow6 = (0.5 * (fSlow2 / (fSlow3 * fSlow5))); diff --git a/src/sfizz/gen/filters/sfz2chBrf2p.hxx b/src/sfizz/gen/filters/sfz2chBrf2p.hxx index b802972f9..4b73e3e3e 100644 --- a/src/sfizz/gen/filters/sfz2chBrf2p.hxx +++ b/src/sfizz/gen/filters/sfz2chBrf2p.hxx @@ -162,7 +162,7 @@ class faust2chBrf2p : public sfzFilterDsp { FAUSTFLOAT* output1 = outputs[1]; double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); - double fSlow2 = (0.5 * (std::sin(fSlow1) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))))); + double fSlow2 = (0.5 * (std::sin(fSlow1) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))))); double fSlow3 = (fSlow2 + 1.0); double fSlow4 = (1.0 - fSlow0); double fSlow5 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow3) * fSlow4); diff --git a/src/sfizz/gen/filters/sfz2chBrf2pSv.hxx b/src/sfizz/gen/filters/sfz2chBrf2pSv.hxx index c9b75ee2f..abc664379 100644 --- a/src/sfizz/gen/filters/sfz2chBrf2pSv.hxx +++ b/src/sfizz/gen/filters/sfz2chBrf2pSv.hxx @@ -139,7 +139,7 @@ class faust2chBrf2pSv : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (1.0 - fSlow0); double fSlow2 = (std::tan((fConst2 * std::min(20000.0, std::max(1.0, double(fHslider0))))) * fSlow1); - double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))); + double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); diff --git a/src/sfizz/gen/filters/sfz2chHpf2p.hxx b/src/sfizz/gen/filters/sfz2chHpf2p.hxx index 490592935..48f45545d 100644 --- a/src/sfizz/gen/filters/sfz2chHpf2p.hxx +++ b/src/sfizz/gen/filters/sfz2chHpf2p.hxx @@ -167,7 +167,7 @@ class faust2chHpf2p : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow2 = std::cos(fSlow1); - double fSlow3 = (0.5 * (std::sin(fSlow1) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))))); + double fSlow3 = (0.5 * (std::sin(fSlow1) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))))); double fSlow4 = (fSlow3 + 1.0); double fSlow5 = (1.0 - fSlow0); double fSlow6 = (((-1.0 - fSlow2) / fSlow4) * fSlow5); diff --git a/src/sfizz/gen/filters/sfz2chHpf2pSv.hxx b/src/sfizz/gen/filters/sfz2chHpf2pSv.hxx index 6631a8988..ceb828404 100644 --- a/src/sfizz/gen/filters/sfz2chHpf2pSv.hxx +++ b/src/sfizz/gen/filters/sfz2chHpf2pSv.hxx @@ -139,7 +139,7 @@ class faust2chHpf2pSv : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (1.0 - fSlow0); double fSlow2 = (std::tan((fConst2 * std::min(20000.0, std::max(1.0, double(fHslider0))))) * fSlow1); - double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))); + double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); diff --git a/src/sfizz/gen/filters/sfz2chHpf4p.hxx b/src/sfizz/gen/filters/sfz2chHpf4p.hxx index 85ddb7853..950aa163f 100644 --- a/src/sfizz/gen/filters/sfz2chHpf4p.hxx +++ b/src/sfizz/gen/filters/sfz2chHpf4p.hxx @@ -207,7 +207,7 @@ class faust2chHpf4p : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow2 = std::cos(fSlow1); - double fSlow3 = (0.5 * (std::sin(fSlow1) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))))); + double fSlow3 = (0.5 * (std::sin(fSlow1) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))))); double fSlow4 = (fSlow3 + 1.0); double fSlow5 = (1.0 - fSlow0); double fSlow6 = (((-1.0 - fSlow2) / fSlow4) * fSlow5); diff --git a/src/sfizz/gen/filters/sfz2chHpf6p.hxx b/src/sfizz/gen/filters/sfz2chHpf6p.hxx index 4c848e818..c078bb41d 100644 --- a/src/sfizz/gen/filters/sfz2chHpf6p.hxx +++ b/src/sfizz/gen/filters/sfz2chHpf6p.hxx @@ -247,7 +247,7 @@ class faust2chHpf6p : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow2 = std::cos(fSlow1); - double fSlow3 = (0.5 * (std::sin(fSlow1) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))))); + double fSlow3 = (0.5 * (std::sin(fSlow1) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))))); double fSlow4 = (fSlow3 + 1.0); double fSlow5 = (1.0 - fSlow0); double fSlow6 = (((-1.0 - fSlow2) / fSlow4) * fSlow5); diff --git a/src/sfizz/gen/filters/sfz2chHsh.hxx b/src/sfizz/gen/filters/sfz2chHsh.hxx index 7bb9fde36..68a2a387a 100644 --- a/src/sfizz/gen/filters/sfz2chHsh.hxx +++ b/src/sfizz/gen/filters/sfz2chHsh.hxx @@ -175,7 +175,7 @@ class faust2chHsh : public sfzFilterDsp { double fSlow2 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow3 = std::cos(fSlow2); double fSlow4 = ((fSlow1 + 1.0) * fSlow3); - double fSlow5 = ((std::sqrt(fSlow1) * std::sin(fSlow2)) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider1))))))); + double fSlow5 = ((std::sqrt(fSlow1) * std::sin(fSlow2)) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider1))))))); double fSlow6 = ((fSlow1 + -1.0) * fSlow3); double fSlow7 = ((fSlow1 + fSlow5) + (1.0 - fSlow6)); double fSlow8 = (1.0 - fSlow0); diff --git a/src/sfizz/gen/filters/sfz2chLpf2p.hxx b/src/sfizz/gen/filters/sfz2chLpf2p.hxx index 1ee9a6f8c..d71352b77 100644 --- a/src/sfizz/gen/filters/sfz2chLpf2p.hxx +++ b/src/sfizz/gen/filters/sfz2chLpf2p.hxx @@ -166,7 +166,7 @@ class faust2chLpf2p : public sfzFilterDsp { FAUSTFLOAT* output1 = outputs[1]; double fSlow0 = (fConst1 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow1 = std::cos(fSlow0); - double fSlow2 = (0.5 * (std::sin(fSlow0) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))))); + double fSlow2 = (0.5 * (std::sin(fSlow0) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))))); double fSlow3 = (fSlow2 + 1.0); double fSlow4 = ((1.0 - fSlow1) / fSlow3); double fSlow5 = (fSmoothEnable ? fConst2 : 0.0); diff --git a/src/sfizz/gen/filters/sfz2chLpf2pSv.hxx b/src/sfizz/gen/filters/sfz2chLpf2pSv.hxx index 6c42c4ed8..a822345ff 100644 --- a/src/sfizz/gen/filters/sfz2chLpf2pSv.hxx +++ b/src/sfizz/gen/filters/sfz2chLpf2pSv.hxx @@ -139,7 +139,7 @@ class faust2chLpf2pSv : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (1.0 - fSlow0); double fSlow2 = (std::tan((fConst2 * std::min(20000.0, std::max(1.0, double(fHslider0))))) * fSlow1); - double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))); + double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); double fTemp1 = double(input1[i]); diff --git a/src/sfizz/gen/filters/sfz2chLpf4p.hxx b/src/sfizz/gen/filters/sfz2chLpf4p.hxx index 7fabed760..ba0d9c6d5 100644 --- a/src/sfizz/gen/filters/sfz2chLpf4p.hxx +++ b/src/sfizz/gen/filters/sfz2chLpf4p.hxx @@ -206,7 +206,7 @@ class faust2chLpf4p : public sfzFilterDsp { FAUSTFLOAT* output1 = outputs[1]; double fSlow0 = (fConst1 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow1 = std::cos(fSlow0); - double fSlow2 = (0.5 * (std::sin(fSlow0) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))))); + double fSlow2 = (0.5 * (std::sin(fSlow0) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))))); double fSlow3 = (fSlow2 + 1.0); double fSlow4 = ((1.0 - fSlow1) / fSlow3); double fSlow5 = (fSmoothEnable ? fConst2 : 0.0); diff --git a/src/sfizz/gen/filters/sfz2chLpf6p.hxx b/src/sfizz/gen/filters/sfz2chLpf6p.hxx index b2ce9fcaa..b3fca7358 100644 --- a/src/sfizz/gen/filters/sfz2chLpf6p.hxx +++ b/src/sfizz/gen/filters/sfz2chLpf6p.hxx @@ -246,7 +246,7 @@ class faust2chLpf6p : public sfzFilterDsp { FAUSTFLOAT* output1 = outputs[1]; double fSlow0 = (fConst1 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow1 = std::cos(fSlow0); - double fSlow2 = (0.5 * (std::sin(fSlow0) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))))); + double fSlow2 = (0.5 * (std::sin(fSlow0) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))))); double fSlow3 = (fSlow2 + 1.0); double fSlow4 = ((1.0 - fSlow1) / fSlow3); double fSlow5 = (fSmoothEnable ? fConst2 : 0.0); diff --git a/src/sfizz/gen/filters/sfz2chLsh.hxx b/src/sfizz/gen/filters/sfz2chLsh.hxx index b3ec808f0..3d32d1a51 100644 --- a/src/sfizz/gen/filters/sfz2chLsh.hxx +++ b/src/sfizz/gen/filters/sfz2chLsh.hxx @@ -176,7 +176,7 @@ class faust2chLsh : public sfzFilterDsp { double fSlow3 = std::cos(fSlow2); double fSlow4 = ((fSlow1 + 1.0) * fSlow3); double fSlow5 = ((fSlow1 + -1.0) * fSlow3); - double fSlow6 = ((std::sqrt(fSlow1) * std::sin(fSlow2)) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider1))))))); + double fSlow6 = ((std::sqrt(fSlow1) * std::sin(fSlow2)) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider1))))))); double fSlow7 = (fSlow5 + fSlow6); double fSlow8 = ((fSlow1 + fSlow7) + 1.0); double fSlow9 = (1.0 - fSlow0); diff --git a/src/sfizz/gen/filters/sfz2chPeq.hxx b/src/sfizz/gen/filters/sfz2chPeq.hxx index 6126a8fdf..4b5de6f3c 100644 --- a/src/sfizz/gen/filters/sfz2chPeq.hxx +++ b/src/sfizz/gen/filters/sfz2chPeq.hxx @@ -169,7 +169,7 @@ class faust2chPeq : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow2 = std::sin(fSlow1); - double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))); + double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))); double fSlow4 = std::pow(10.0, (0.025000000000000001 * std::min(60.0, std::max(-120.0, double(fVslider1))))); double fSlow5 = (0.5 * (fSlow2 / (fSlow3 * fSlow4))); double fSlow6 = (fSlow5 + 1.0); diff --git a/src/sfizz/gen/filters/sfzBpf2p.hxx b/src/sfizz/gen/filters/sfzBpf2p.hxx index 9a0c61dc0..e31555b3e 100644 --- a/src/sfizz/gen/filters/sfzBpf2p.hxx +++ b/src/sfizz/gen/filters/sfzBpf2p.hxx @@ -149,7 +149,7 @@ class faustBpf2p : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow2 = std::sin(fSlow1); - double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))); + double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))); double fSlow4 = (0.5 * (fSlow2 / fSlow3)); double fSlow5 = (fSlow4 + 1.0); double fSlow6 = (0.5 * (fSlow2 / (fSlow3 * fSlow5))); diff --git a/src/sfizz/gen/filters/sfzBpf2pSv.hxx b/src/sfizz/gen/filters/sfzBpf2pSv.hxx index 19b66dec1..995601515 100644 --- a/src/sfizz/gen/filters/sfzBpf2pSv.hxx +++ b/src/sfizz/gen/filters/sfzBpf2pSv.hxx @@ -129,7 +129,7 @@ class faustBpf2pSv : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (1.0 - fSlow0); double fSlow2 = (std::tan((fConst2 * std::min(20000.0, std::max(1.0, double(fHslider0))))) * fSlow1); - double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))); + double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow2); diff --git a/src/sfizz/gen/filters/sfzBpf4p.hxx b/src/sfizz/gen/filters/sfzBpf4p.hxx index e51efc1b1..4d74e7fea 100644 --- a/src/sfizz/gen/filters/sfzBpf4p.hxx +++ b/src/sfizz/gen/filters/sfzBpf4p.hxx @@ -169,7 +169,7 @@ class faustBpf4p : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow2 = std::sin(fSlow1); - double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))); + double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))); double fSlow4 = (0.5 * (fSlow2 / fSlow3)); double fSlow5 = (fSlow4 + 1.0); double fSlow6 = (0.5 * (fSlow2 / (fSlow3 * fSlow5))); diff --git a/src/sfizz/gen/filters/sfzBpf6p.hxx b/src/sfizz/gen/filters/sfzBpf6p.hxx index 98fc4b1ed..0ec70dd06 100644 --- a/src/sfizz/gen/filters/sfzBpf6p.hxx +++ b/src/sfizz/gen/filters/sfzBpf6p.hxx @@ -189,7 +189,7 @@ class faustBpf6p : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow2 = std::sin(fSlow1); - double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))); + double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))); double fSlow4 = (0.5 * (fSlow2 / fSlow3)); double fSlow5 = (fSlow4 + 1.0); double fSlow6 = (0.5 * (fSlow2 / (fSlow3 * fSlow5))); diff --git a/src/sfizz/gen/filters/sfzBrf2p.hxx b/src/sfizz/gen/filters/sfzBrf2p.hxx index 210366e51..49317e687 100644 --- a/src/sfizz/gen/filters/sfzBrf2p.hxx +++ b/src/sfizz/gen/filters/sfzBrf2p.hxx @@ -140,7 +140,7 @@ class faustBrf2p : public sfzFilterDsp { FAUSTFLOAT* output0 = outputs[0]; double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); - double fSlow2 = (0.5 * (std::sin(fSlow1) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))))); + double fSlow2 = (0.5 * (std::sin(fSlow1) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))))); double fSlow3 = (fSlow2 + 1.0); double fSlow4 = (1.0 - fSlow0); double fSlow5 = (((0.0 - (2.0 * std::cos(fSlow1))) / fSlow3) * fSlow4); diff --git a/src/sfizz/gen/filters/sfzBrf2pSv.hxx b/src/sfizz/gen/filters/sfzBrf2pSv.hxx index 4a50a451f..1024b9aa7 100644 --- a/src/sfizz/gen/filters/sfzBrf2pSv.hxx +++ b/src/sfizz/gen/filters/sfzBrf2pSv.hxx @@ -129,7 +129,7 @@ class faustBrf2pSv : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (1.0 - fSlow0); double fSlow2 = (std::tan((fConst2 * std::min(20000.0, std::max(1.0, double(fHslider0))))) * fSlow1); - double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))); + double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); fRec5[0] = ((fSlow0 * fRec5[1]) + fSlow2); diff --git a/src/sfizz/gen/filters/sfzHpf2p.hxx b/src/sfizz/gen/filters/sfzHpf2p.hxx index 9934aabf7..18e3f6484 100644 --- a/src/sfizz/gen/filters/sfzHpf2p.hxx +++ b/src/sfizz/gen/filters/sfzHpf2p.hxx @@ -145,7 +145,7 @@ class faustHpf2p : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow2 = std::cos(fSlow1); - double fSlow3 = (0.5 * (std::sin(fSlow1) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))))); + double fSlow3 = (0.5 * (std::sin(fSlow1) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))))); double fSlow4 = (fSlow3 + 1.0); double fSlow5 = (1.0 - fSlow0); double fSlow6 = (((-1.0 - fSlow2) / fSlow4) * fSlow5); diff --git a/src/sfizz/gen/filters/sfzHpf2pSv.hxx b/src/sfizz/gen/filters/sfzHpf2pSv.hxx index 2dc0420ac..7d0e4507a 100644 --- a/src/sfizz/gen/filters/sfzHpf2pSv.hxx +++ b/src/sfizz/gen/filters/sfzHpf2pSv.hxx @@ -129,7 +129,7 @@ class faustHpf2pSv : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (1.0 - fSlow0); double fSlow2 = (std::tan((fConst2 * std::min(20000.0, std::max(1.0, double(fHslider0))))) * fSlow1); - double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))); + double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); fRec4[0] = ((fSlow0 * fRec4[1]) + fSlow2); diff --git a/src/sfizz/gen/filters/sfzHpf4p.hxx b/src/sfizz/gen/filters/sfzHpf4p.hxx index 1ffeed51a..4f02e2882 100644 --- a/src/sfizz/gen/filters/sfzHpf4p.hxx +++ b/src/sfizz/gen/filters/sfzHpf4p.hxx @@ -165,7 +165,7 @@ class faustHpf4p : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow2 = std::cos(fSlow1); - double fSlow3 = (0.5 * (std::sin(fSlow1) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))))); + double fSlow3 = (0.5 * (std::sin(fSlow1) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))))); double fSlow4 = (fSlow3 + 1.0); double fSlow5 = (1.0 - fSlow0); double fSlow6 = (((-1.0 - fSlow2) / fSlow4) * fSlow5); diff --git a/src/sfizz/gen/filters/sfzHpf6p.hxx b/src/sfizz/gen/filters/sfzHpf6p.hxx index ab3763878..ffcd8bdf9 100644 --- a/src/sfizz/gen/filters/sfzHpf6p.hxx +++ b/src/sfizz/gen/filters/sfzHpf6p.hxx @@ -185,7 +185,7 @@ class faustHpf6p : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow2 = std::cos(fSlow1); - double fSlow3 = (0.5 * (std::sin(fSlow1) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))))); + double fSlow3 = (0.5 * (std::sin(fSlow1) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))))); double fSlow4 = (fSlow3 + 1.0); double fSlow5 = (1.0 - fSlow0); double fSlow6 = (((-1.0 - fSlow2) / fSlow4) * fSlow5); diff --git a/src/sfizz/gen/filters/sfzHsh.hxx b/src/sfizz/gen/filters/sfzHsh.hxx index fc80a9463..e4b69cc36 100644 --- a/src/sfizz/gen/filters/sfzHsh.hxx +++ b/src/sfizz/gen/filters/sfzHsh.hxx @@ -153,7 +153,7 @@ class faustHsh : public sfzFilterDsp { double fSlow2 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow3 = std::cos(fSlow2); double fSlow4 = ((fSlow1 + 1.0) * fSlow3); - double fSlow5 = ((std::sqrt(fSlow1) * std::sin(fSlow2)) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider1))))))); + double fSlow5 = ((std::sqrt(fSlow1) * std::sin(fSlow2)) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider1))))))); double fSlow6 = ((fSlow1 + -1.0) * fSlow3); double fSlow7 = ((fSlow1 + fSlow5) + (1.0 - fSlow6)); double fSlow8 = (1.0 - fSlow0); diff --git a/src/sfizz/gen/filters/sfzLpf2p.hxx b/src/sfizz/gen/filters/sfzLpf2p.hxx index 438804673..871be36a7 100644 --- a/src/sfizz/gen/filters/sfzLpf2p.hxx +++ b/src/sfizz/gen/filters/sfzLpf2p.hxx @@ -144,7 +144,7 @@ class faustLpf2p : public sfzFilterDsp { FAUSTFLOAT* output0 = outputs[0]; double fSlow0 = (fConst1 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow1 = std::cos(fSlow0); - double fSlow2 = (0.5 * (std::sin(fSlow0) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))))); + double fSlow2 = (0.5 * (std::sin(fSlow0) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))))); double fSlow3 = (fSlow2 + 1.0); double fSlow4 = ((1.0 - fSlow1) / fSlow3); double fSlow5 = (fSmoothEnable ? fConst2 : 0.0); diff --git a/src/sfizz/gen/filters/sfzLpf2pSv.hxx b/src/sfizz/gen/filters/sfzLpf2pSv.hxx index f303bb73a..ee51668a4 100644 --- a/src/sfizz/gen/filters/sfzLpf2pSv.hxx +++ b/src/sfizz/gen/filters/sfzLpf2pSv.hxx @@ -129,7 +129,7 @@ class faustLpf2pSv : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (1.0 - fSlow0); double fSlow2 = (std::tan((fConst2 * std::min(20000.0, std::max(1.0, double(fHslider0))))) * fSlow1); - double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))); + double fSlow3 = (1.0 / std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))); for (int i = 0; (i < count); i = (i + 1)) { double fTemp0 = double(input0[i]); fRec3[0] = ((fSlow0 * fRec3[1]) + fSlow2); diff --git a/src/sfizz/gen/filters/sfzLpf4p.hxx b/src/sfizz/gen/filters/sfzLpf4p.hxx index d0435d560..88e8ae7ce 100644 --- a/src/sfizz/gen/filters/sfzLpf4p.hxx +++ b/src/sfizz/gen/filters/sfzLpf4p.hxx @@ -164,7 +164,7 @@ class faustLpf4p : public sfzFilterDsp { FAUSTFLOAT* output0 = outputs[0]; double fSlow0 = (fConst1 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow1 = std::cos(fSlow0); - double fSlow2 = (0.5 * (std::sin(fSlow0) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))))); + double fSlow2 = (0.5 * (std::sin(fSlow0) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))))); double fSlow3 = (fSlow2 + 1.0); double fSlow4 = ((1.0 - fSlow1) / fSlow3); double fSlow5 = (fSmoothEnable ? fConst2 : 0.0); diff --git a/src/sfizz/gen/filters/sfzLpf6p.hxx b/src/sfizz/gen/filters/sfzLpf6p.hxx index 95f702288..1df6f79c9 100644 --- a/src/sfizz/gen/filters/sfzLpf6p.hxx +++ b/src/sfizz/gen/filters/sfzLpf6p.hxx @@ -184,7 +184,7 @@ class faustLpf6p : public sfzFilterDsp { FAUSTFLOAT* output0 = outputs[0]; double fSlow0 = (fConst1 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow1 = std::cos(fSlow0); - double fSlow2 = (0.5 * (std::sin(fSlow0) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))))); + double fSlow2 = (0.5 * (std::sin(fSlow0) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))))); double fSlow3 = (fSlow2 + 1.0); double fSlow4 = ((1.0 - fSlow1) / fSlow3); double fSlow5 = (fSmoothEnable ? fConst2 : 0.0); diff --git a/src/sfizz/gen/filters/sfzLsh.hxx b/src/sfizz/gen/filters/sfzLsh.hxx index b614b5589..92e93f9dc 100644 --- a/src/sfizz/gen/filters/sfzLsh.hxx +++ b/src/sfizz/gen/filters/sfzLsh.hxx @@ -154,7 +154,7 @@ class faustLsh : public sfzFilterDsp { double fSlow3 = std::cos(fSlow2); double fSlow4 = ((fSlow1 + 1.0) * fSlow3); double fSlow5 = ((fSlow1 + -1.0) * fSlow3); - double fSlow6 = ((std::sqrt(fSlow1) * std::sin(fSlow2)) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider1))))))); + double fSlow6 = ((std::sqrt(fSlow1) * std::sin(fSlow2)) / std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider1))))))); double fSlow7 = (fSlow5 + fSlow6); double fSlow8 = ((fSlow1 + fSlow7) + 1.0); double fSlow9 = (1.0 - fSlow0); diff --git a/src/sfizz/gen/filters/sfzPeq.hxx b/src/sfizz/gen/filters/sfzPeq.hxx index 50ce20845..bf7f770ce 100644 --- a/src/sfizz/gen/filters/sfzPeq.hxx +++ b/src/sfizz/gen/filters/sfzPeq.hxx @@ -147,7 +147,7 @@ class faustPeq : public sfzFilterDsp { double fSlow0 = (fSmoothEnable ? fConst1 : 0.0); double fSlow1 = (fConst2 * std::max(0.0, std::min(20000.0, std::max(1.0, double(fHslider0))))); double fSlow2 = std::sin(fSlow1); - double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(0.0, double(fVslider0)))))); + double fSlow3 = std::max(0.001, std::pow(10.0, (0.050000000000000003 * std::min(60.0, std::max(-60.0, double(fVslider0)))))); double fSlow4 = std::pow(10.0, (0.025000000000000001 * std::min(60.0, std::max(-120.0, double(fVslider1))))); double fSlow5 = (0.5 * (fSlow2 / (fSlow3 * fSlow4))); double fSlow6 = (fSlow5 + 1.0); From 2281c99d63d8efe6c1b5bb182386ab8576841e03 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 21 Jun 2021 20:42:27 +0200 Subject: [PATCH 084/193] Update gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index f1c7bda93..6b888b16a 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,8 @@ compile_commands.json *.autosave /Doxyfile .DS_Store +.cache/ +.cmake/ clients/sfizz_jack clients/sfzprint From 88b5f2d3334ea0d3014bd705c46251dfd7d104b2 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 21 Jun 2021 21:01:02 +0200 Subject: [PATCH 085/193] Update vstgui --- .gitmodules | 2 +- plugins/editor/external/vstgui4 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 7850d1b81..441fc8070 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,7 +17,7 @@ shallow = true [submodule "vst/external/VST_SDK/VST3_SDK/vstgui4"] path = plugins/editor/external/vstgui4 - url = https://github.com/sfztools/vstgui.git + url = https://github.com/steinbergmedia/vstgui.git shallow = true [submodule "external/st_audiofile/thirdparty/dr_libs"] path = external/st_audiofile/thirdparty/dr_libs diff --git a/plugins/editor/external/vstgui4 b/plugins/editor/external/vstgui4 index 9a116d181..c009dd294 160000 --- a/plugins/editor/external/vstgui4 +++ b/plugins/editor/external/vstgui4 @@ -1 +1 @@ -Subproject commit 9a116d1812aa274ccf61d68f4c2bae612b3871ad +Subproject commit c009dd294171cbb1bb5021830ff125a15966dea8 From 8d5826efd532b99cf33245c385363aca770e2c4a Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 21 Jun 2021 21:21:18 +0200 Subject: [PATCH 086/193] Do not use a hardcoded path in the installer --- scripts/innosetup.iss.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/innosetup.iss.in b/scripts/innosetup.iss.in index 87159e322..268bfa632 100644 --- a/scripts/innosetup.iss.in +++ b/scripts/innosetup.iss.in @@ -36,8 +36,8 @@ LicenseFile="sfizz.lv2\LICENSE.md" OutputBaseFileName={#MyAppName}-{#MyAppVersion}-msvc-{#Arch}-setup OutputDir=. UninstallFilesDir={app} -WizardImageFile="C:\Program Files (x86)\Inno Setup 6\WizModernImage-IS.bmp" -WizardSmallImageFile="C:\Program Files (x86)\Inno Setup 6\WizModernSmallImage-IS.bmp" +WizardImageFile=compiler:WizClassicImage-IS.bmp +WizardSmallImageFile=compiler:WizClassicSmallImage-IS.bmp [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" From 8804f4a515693caa2f05cdafcb05d1c65f610f58 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 21 Jun 2021 22:27:53 +0200 Subject: [PATCH 087/193] Other workaround to build with VST3 SDK 3.7.2 --- .github/workflows/build.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b7abb7b5e..31414eb92 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -251,13 +251,19 @@ jobs: run: | cp -vf "$GITHUB_WORKSPACE"/scripts/mingw_dwrite_3.h \ /usr/i686-w64-mingw32/include/dwrite_3.h - - name: Fix VST sources + - name: Fix VST sources (case) shell: bash # need to convert some includes to lower case (as of VST 3.7.1) run: | find "$GITHUB_WORKSPACE"/plugins/vst/external/VST_SDK -type d -name source -exec \ find {} -type f -name '*.[hc]' -o -name '*.[hc]pp' -print0 \; | \ xargs -0 sed -i 's///' + - name: Fix VST sources (includes) + shell: bash + # need to add include (as of VST 3.7.2) + run: | + sed -i '0,/#include \n#include //' + - name: Fix VST sources (includes) + shell: bash + # need to add include (as of VST 3.7.2) + run: | + sed -i '0,/#include \n#include Date: Tue, 22 Jun 2021 00:40:10 +0200 Subject: [PATCH 088/193] lv2: automation port does not receive OSC --- plugins/lv2/sfizz.ttl.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lv2/sfizz.ttl.in b/plugins/lv2/sfizz.ttl.in index 83bddf760..0357c7d42 100644 --- a/plugins/lv2/sfizz.ttl.in +++ b/plugins/lv2/sfizz.ttl.in @@ -102,7 +102,7 @@ midnam:update a lv2:Feature . lv2:port [ a lv2:InputPort, atom:AtomPort ; atom:bufferType atom:Sequence ; - atom:supports patch:Message, midi:MidiEvent, time:Position, <@LV2PLUGIN_URI@:OSCBlob> ; + atom:supports patch:Message, midi:MidiEvent, time:Position ; lv2:designation lv2:control ; lv2:index 0 ; lv2:symbol "control" ; From aa7edd7eb23714887b8d1ee476e9bc6c35a1933b Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 22 Jun 2021 00:41:19 +0200 Subject: [PATCH 089/193] Revert "lv2: automation port does not receive OSC" This reverts commit 05bdd428d2bfa2a4bd605272168e2b001cc1dbc6. --- plugins/lv2/sfizz.ttl.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lv2/sfizz.ttl.in b/plugins/lv2/sfizz.ttl.in index 0357c7d42..83bddf760 100644 --- a/plugins/lv2/sfizz.ttl.in +++ b/plugins/lv2/sfizz.ttl.in @@ -102,7 +102,7 @@ midnam:update a lv2:Feature . lv2:port [ a lv2:InputPort, atom:AtomPort ; atom:bufferType atom:Sequence ; - atom:supports patch:Message, midi:MidiEvent, time:Position ; + atom:supports patch:Message, midi:MidiEvent, time:Position, <@LV2PLUGIN_URI@:OSCBlob> ; lv2:designation lv2:control ; lv2:index 0 ; lv2:symbol "control" ; From a184dc62c18664f6b32578957879ead42d623790 Mon Sep 17 00:00:00 2001 From: redtide Date: Tue, 22 Jun 2021 02:50:19 +0200 Subject: [PATCH 090/193] Added sfz badges to README --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 845bbc625..079e2be50 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ [![AppVeyor Build Status]](https://ci.appveyor.com/project/sfztools/sfizz) [![Discord Badge Image]](https://discord.gg/3ArE9Mw) +[![SFZv1 Status Image]](https://sfz.tools/sfizz/development/status/opcodes/?v=1) +[![SFZv2 Status Image]](https://sfz.tools/sfizz/development/status/opcodes/?v=2) +[![ARIA Status Image]](https://sfz.tools/sfizz/development/status/opcodes/?v=aria) +[![Cakewalk Status Image]](https://sfz.tools/sfizz/development/status/opcodes/?v=cakewalk) SFZ parser and synth c++ library, providing AU / LV2 / VST3 plugins and JACK standalone client, please check [our website] for more details. @@ -98,3 +102,8 @@ The sfizz library also uses in some subprojects: [AppVeyor Build Status]: https://img.shields.io/appveyor/ci/sfztools/sfizz.svg?label=Windows&style=popout&logo=appveyor [Travis Build Status]: https://img.shields.io/travis/com/sfztools/sfizz.svg?label=Linux&style=popout&logo=travis [Discord Badge Image]: https://img.shields.io/discord/587748534321807416?label=discord&logo=discord + +[SFZv1 Status Image]: https://sfz.tools/assets/img/sfizz/badge_sfz1.svg +[SFZv2 Status Image]: https://sfz.tools/assets/img/sfizz/badge_sfz2.svg +[ARIA Status Image]: https://sfz.tools/assets/img/sfizz/badge_aria.svg +[Cakewalk Status Image]: https://sfz.tools/assets/img/sfizz/badge_cakewalk.svg From 99e99dd084b77ac9aa214b8d967c7e0061ea4a3a Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 22 Jun 2021 14:59:46 +0200 Subject: [PATCH 091/193] Fix unitialized default CC values, to pass the tests --- src/sfizz/SynthPrivate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sfizz/SynthPrivate.h b/src/sfizz/SynthPrivate.h index 4b2446491..2597f417b 100644 --- a/src/sfizz/SynthPrivate.h +++ b/src/sfizz/SynthPrivate.h @@ -327,7 +327,7 @@ struct Synth::Impl final: public Parser::Listener { Parser parser_; absl::optional modificationTime_ { }; - std::array defaultCCValues_; + std::array defaultCCValues_ { }; BitArray currentUsedCCs_; BitArray changedCCsThisCycle_; BitArray changedCCsLastCycle_; From 5ed281c6a420fc3d1765ee49faa3baf8db6b1116 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 22 Jun 2021 00:45:44 +0200 Subject: [PATCH 092/193] lv2: A6 workaround, single output atom port Previously, some `patch:Set` messages were directed to a distinct atom port, when it was for notification purposes rather than automation. Ardour does not support this, and the ports get assigned the same buffer, producing corruption. This commit returns to single-port behavior, and instead replaces `patch:Set` notification messages with `sfizz:Notify` custom objects; the UI processes these messages all the same, and they does not trigger automation in the host. --- plugins/lv2/sfizz.cpp | 44 +++++++++++++----------------- plugins/lv2/sfizz.ttl.in | 49 ++++++++++++++-------------------- plugins/lv2/sfizz_lv2.h | 40 +++++++++++++-------------- plugins/lv2/sfizz_lv2_plugin.h | 3 +-- plugins/lv2/sfizz_ui.cpp | 2 ++ 5 files changed, 62 insertions(+), 76 deletions(-) diff --git a/plugins/lv2/sfizz.cpp b/plugins/lv2/sfizz.cpp index 6c1d56a21..c29d9ebfb 100644 --- a/plugins/lv2/sfizz.cpp +++ b/plugins/lv2/sfizz.cpp @@ -123,6 +123,7 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self) self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus); self->sfizz_check_modification_uri = map->map(map->handle, SFIZZ__checkModification); self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob); + self->sfizz_notify_uri = map->map(map->handle, SFIZZ__Notify); self->sfizz_audio_level_uri = map->map(map->handle, SFIZZ__AudioLevel); self->time_position_uri = map->map(map->handle, LV2_TIME__Position); self->time_bar_uri = map->map(map->handle, LV2_TIME__bar); @@ -200,9 +201,6 @@ connect_port(LV2_Handle instance, case SFIZZ_CONTROL: self->control_port = (const LV2_Atom_Sequence *)data; break; - case SFIZZ_NOTIFY: - self->notify_port = (LV2_Atom_Sequence *)data; - break; case SFIZZ_AUTOMATE: self->automate_port = (LV2_Atom_Sequence *)data; break; @@ -331,7 +329,7 @@ sfizz_lv2_receive_message(void* data, int delay, const char* path, const char* s if (osc_size > OSC_TEMP_SIZE) return; - LV2_Atom_Forge* forge = &self->forge_notify; + LV2_Atom_Forge* forge = &self->forge_automate; bool write_ok = lv2_atom_forge_frame_time(forge, 0) && lv2_atom_forge_atom(forge, osc_size, self->sfizz_osc_blob_uri) && @@ -439,7 +437,6 @@ instantiate(const LV2_Descriptor *descriptor, sfizz_lv2_map_required_uris(self); // Initialize the forge - lv2_atom_forge_init(&self->forge_notify, self->map); lv2_atom_forge_init(&self->forge_automate, self->map); lv2_atom_forge_init(&self->forge_secondary, self->map); @@ -545,13 +542,14 @@ deactivate(LV2_Handle instance) } static void -sfizz_lv2_send_file_path(sfizz_plugin_t *self, LV2_Atom_Forge* forge, LV2_URID urid, const char *path) +sfizz_lv2_send_file_path(sfizz_plugin_t *self, LV2_URID verb_uri, LV2_URID urid, const char *path) { LV2_Atom_Forge_Frame frame; + LV2_Atom_Forge* forge = &self->forge_automate; bool write_ok = lv2_atom_forge_frame_time(forge, 0) && - lv2_atom_forge_object(forge, &frame, 0, self->patch_set_uri) && + lv2_atom_forge_object(forge, &frame, 0, verb_uri) && lv2_atom_forge_key(forge, self->patch_property_uri) && lv2_atom_forge_urid(forge, urid) && lv2_atom_forge_key(forge, self->patch_value_uri) && @@ -562,14 +560,15 @@ sfizz_lv2_send_file_path(sfizz_plugin_t *self, LV2_Atom_Forge* forge, LV2_URID u } static void -sfizz_lv2_send_controller(sfizz_plugin_t *self, LV2_Atom_Forge* forge, unsigned cc, float value) +sfizz_lv2_send_controller(sfizz_plugin_t *self, LV2_URID verb_uri, unsigned cc, float value) { LV2_URID urid = sfizz_lv2_ccmap_map(self->ccmap, int(cc)); LV2_Atom_Forge_Frame frame; + LV2_Atom_Forge* forge = &self->forge_automate; bool write_ok = lv2_atom_forge_frame_time(forge, 0) && - lv2_atom_forge_object(forge, &frame, 0, self->patch_set_uri) && + lv2_atom_forge_object(forge, &frame, 0, verb_uri) && lv2_atom_forge_key(forge, self->patch_property_uri) && lv2_atom_forge_urid(forge, urid) && lv2_atom_forge_key(forge, self->patch_value_uri) && @@ -581,11 +580,12 @@ sfizz_lv2_send_controller(sfizz_plugin_t *self, LV2_Atom_Forge* forge, unsigned #if defined(SFIZZ_LV2_UI) static void -sfizz_lv2_send_levels(sfizz_plugin_t *self, LV2_Atom_Forge* forge, float left, float right) +sfizz_lv2_send_levels(sfizz_plugin_t *self, float left, float right) { const float levels[] = {left, right}; uint32_t num_levels = sizeof(levels) / sizeof(levels[0]); + LV2_Atom_Forge* forge = &self->forge_automate; bool write_ok = lv2_atom_forge_frame_time(forge, 0); LV2_Atom_Vector *vector = nullptr; @@ -871,7 +871,7 @@ static void run(LV2_Handle instance, uint32_t sample_count) { sfizz_plugin_t *self = (sfizz_plugin_t *)instance; - assert(self->control_port && self->notify_port && self->automate_port); + assert(self->control_port && self->automate_port); if (!spin_mutex_trylock(self->synth_mutex)) { @@ -881,15 +881,10 @@ run(LV2_Handle instance, uint32_t sample_count) } // Set up dedicated forges to write on their respective ports. - const size_t notify_capacity = self->notify_port->atom.size; - lv2_atom_forge_set_buffer(&self->forge_notify, (uint8_t *)self->notify_port, notify_capacity); const size_t automate_capacity = self->automate_port->atom.size; lv2_atom_forge_set_buffer(&self->forge_automate, (uint8_t *)self->automate_port, automate_capacity); // Start sequences in the respective output ports. - LV2_Atom_Forge_Frame notify_frame; - if (!lv2_atom_forge_sequence_head(&self->forge_notify, ¬ify_frame, 0)) - assert(false); LV2_Atom_Forge_Frame automate_frame; if (!lv2_atom_forge_sequence_head(&self->forge_automate, &automate_frame, 0)) assert(false); @@ -913,25 +908,25 @@ run(LV2_Handle instance, uint32_t sample_count) lv2_atom_object_get(obj, self->patch_property_uri, &property, 0); if (!property) // Send the full state { - sfizz_lv2_send_file_path(self, &self->forge_notify, self->sfizz_sfz_file_uri, self->sfz_file_path); - sfizz_lv2_send_file_path(self, &self->forge_notify, self->sfizz_scala_file_uri, self->scala_file_path); + sfizz_lv2_send_file_path(self, self->sfizz_notify_uri, self->sfizz_sfz_file_uri, self->sfz_file_path); + sfizz_lv2_send_file_path(self, self->sfizz_notify_uri, self->sfizz_scala_file_uri, self->scala_file_path); for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) - sfizz_lv2_send_controller(self, &self->forge_notify, cc, self->cc_current[cc]); + sfizz_lv2_send_controller(self, self->sfizz_notify_uri, cc, self->cc_current[cc]); } else if (property->body == self->sfizz_sfz_file_uri) { - sfizz_lv2_send_file_path(self, &self->forge_notify, self->sfizz_sfz_file_uri, self->sfz_file_path); + sfizz_lv2_send_file_path(self, self->sfizz_notify_uri, self->sfizz_sfz_file_uri, self->sfz_file_path); } else if (property->body == self->sfizz_scala_file_uri) { - sfizz_lv2_send_file_path(self, &self->forge_notify, self->sfizz_scala_file_uri, self->scala_file_path); + sfizz_lv2_send_file_path(self, self->sfizz_notify_uri, self->sfizz_scala_file_uri, self->scala_file_path); } else { int cc = sfizz_lv2_ccmap_unmap(self->ccmap, property->body); if (cc != -1) - sfizz_lv2_send_controller(self, &self->forge_notify, unsigned(cc), self->cc_current[cc]); + sfizz_lv2_send_controller(self, self->sfizz_notify_uri, unsigned(cc), self->cc_current[cc]); } } else if (obj->body.otype == self->time_position_uri) @@ -1079,7 +1074,7 @@ run(LV2_Handle instance, uint32_t sample_count) for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { absl::optional value = self->ccauto[cc]; if (value) { - sfizz_lv2_send_controller(self, &self->forge_automate, cc, *value); + sfizz_lv2_send_controller(self, self->patch_set_uri, cc, *value); self->ccauto[cc] = absl::nullopt; } } @@ -1094,7 +1089,7 @@ run(LV2_Handle instance, uint32_t sample_count) self->rms_follower.process(self->output_buffers[0], self->output_buffers[1], sample_count); const simde__m128 rms = self->rms_follower.getRMS(); const float *levels = (const float *)&rms; - sfizz_lv2_send_levels(self, &self->forge_notify, levels[0], levels[1]); + sfizz_lv2_send_levels(self, levels[0], levels[1]); } else { @@ -1102,7 +1097,6 @@ run(LV2_Handle instance, uint32_t sample_count) } #endif - lv2_atom_forge_pop(&self->forge_notify, ¬ify_frame); lv2_atom_forge_pop(&self->forge_automate, &automate_frame); } diff --git a/plugins/lv2/sfizz.ttl.in b/plugins/lv2/sfizz.ttl.in index 83bddf760..fd8609903 100644 --- a/plugins/lv2/sfizz.ttl.in +++ b/plugins/lv2/sfizz.ttl.in @@ -102,7 +102,7 @@ midnam:update a lv2:Feature . lv2:port [ a lv2:InputPort, atom:AtomPort ; atom:bufferType atom:Sequence ; - atom:supports patch:Message, midi:MidiEvent, time:Position, <@LV2PLUGIN_URI@:OSCBlob> ; + atom:supports patch:Message, midi:MidiEvent, time:Position, <@LV2PLUGIN_URI@:OSCBlob>, <@LV2PLUGIN_URI@:Notify> ; lv2:designation lv2:control ; lv2:index 0 ; lv2:symbol "control" ; @@ -113,24 +113,15 @@ midnam:update a lv2:Feature . a lv2:OutputPort, atom:AtomPort ; atom:bufferType atom:Sequence ; atom:supports patch:Message, <@LV2PLUGIN_URI@:OSCBlob> ; - lv2:index 1 ; - lv2:symbol "notify" ; - lv2:name "Notify", - "Notification"@fr ; - rsz:minimumSize 524288 ; - ] , [ - a lv2:OutputPort, atom:AtomPort ; - atom:bufferType atom:Sequence ; - atom:supports patch:Message ; lv2:designation lv2:control ; - lv2:index 2 ; + lv2:index 1 ; lv2:symbol "automate" ; lv2:name "Automate", "Automatisation"@fr ; rsz:minimumSize 524288 ; ] , [ a lv2:AudioPort, lv2:OutputPort ; - lv2:index 3 ; + lv2:index 2 ; lv2:symbol "out_left" ; lv2:name "Left Output", "Sortie gauche"@fr , @@ -139,7 +130,7 @@ midnam:update a lv2:Feature . lv2:designation pg:left ] , [ a lv2:AudioPort, lv2:OutputPort ; - lv2:index 4 ; + lv2:index 3 ; lv2:symbol "out_right" ; lv2:name "Right Output", "Sortie droite"@fr , @@ -148,7 +139,7 @@ midnam:update a lv2:Feature . lv2:designation pg:right ] , [ a lv2:InputPort, lv2:ControlPort ; - lv2:index 5 ; + lv2:index 4 ; lv2:symbol "volume" ; lv2:name "Volume" ; lv2:default 0.0 ; @@ -157,7 +148,7 @@ midnam:update a lv2:Feature . units:unit units:db ] , [ a lv2:InputPort, lv2:ControlPort ; - lv2:index 6 ; + lv2:index 5 ; lv2:symbol "num_voices" ; lv2:name "Polyphony", "Polyphonie"@fr , @@ -202,7 +193,7 @@ midnam:update a lv2:Feature . ] ; ] , [ a lv2:InputPort, lv2:ControlPort ; - lv2:index 7 ; + lv2:index 6 ; lv2:symbol "oversampling" ; lv2:name "Oversampling factor", "Facteur de suréchantillonnage"@fr , @@ -237,7 +228,7 @@ midnam:update a lv2:Feature . ] ; ] , [ a lv2:InputPort, lv2:ControlPort ; - lv2:index 8 ; + lv2:index 7 ; lv2:symbol "preload_size" ; lv2:name "Preload size", "Taille préchargée"@fr , @@ -280,7 +271,7 @@ midnam:update a lv2:Feature . ] ; ] , [ a lv2:InputPort, lv2:ControlPort ; - lv2:index 9 ; + lv2:index 8 ; lv2:symbol "freewheeling" ; lv2:name "Freewheeling", "En roue libre (freewheeling)"@fr , @@ -292,7 +283,7 @@ midnam:update a lv2:Feature . lv2:maximum 1 ; ] , [ a lv2:InputPort, lv2:ControlPort ; - lv2:index 10 ; + lv2:index 9 ; lv2:symbol "scala_root_key" ; lv2:name "Scala root key", "Tonalité de base Scala"@fr , @@ -305,7 +296,7 @@ midnam:update a lv2:Feature . units:unit units:midiNote ] , [ a lv2:InputPort, lv2:ControlPort ; - lv2:index 11 ; + lv2:index 10 ; lv2:symbol "tuning_frequency" ; lv2:name "Tuning frequency", "Fréquence d'accordage"@fr , @@ -317,7 +308,7 @@ midnam:update a lv2:Feature . units:unit units:hz ] , [ a lv2:InputPort, lv2:ControlPort ; - lv2:index 12 ; + lv2:index 11 ; lv2:symbol "stretched_tuning" ; lv2:name "Stretched tuning", "Accordage étiré"@fr , @@ -329,7 +320,7 @@ midnam:update a lv2:Feature . units:unit units:coef ] , [ a lv2:InputPort, lv2:ControlPort ; - lv2:index 13 ; + lv2:index 12 ; lv2:symbol "sample_quality" ; lv2:name "Sample quality", "Qualité des échantillons"@fr , @@ -340,7 +331,7 @@ midnam:update a lv2:Feature . lv2:maximum 10.0 ] , [ a lv2:InputPort, lv2:ControlPort ; - lv2:index 14 ; + lv2:index 13 ; lv2:symbol "oscillator_quality" ; lv2:name "Oscillator quality", "Qualité des oscillateurs"@fr , @@ -351,7 +342,7 @@ midnam:update a lv2:Feature . lv2:maximum 3.0 ] , [ a lv2:OutputPort, lv2:ControlPort ; - lv2:index 15 ; + lv2:index 14 ; lv2:symbol "active_voices" ; lv2:name "Active voices", "Voix utilisées"@fr ; @@ -362,7 +353,7 @@ midnam:update a lv2:Feature . lv2:maximum 256 ; ] , [ a lv2:OutputPort, lv2:ControlPort ; - lv2:index 16 ; + lv2:index 15 ; lv2:symbol "num_curves" ; lv2:name "Number of curves", "Nombre de courbes"@fr ; @@ -373,7 +364,7 @@ midnam:update a lv2:Feature . lv2:maximum 65535 ; ] , [ a lv2:OutputPort, lv2:ControlPort ; - lv2:index 17 ; + lv2:index 16 ; lv2:symbol "num_masters" ; lv2:name "Number of masters", "Nombre de maîtres"@fr ; @@ -384,7 +375,7 @@ midnam:update a lv2:Feature . lv2:maximum 65535 ; ] , [ a lv2:OutputPort, lv2:ControlPort ; - lv2:index 18 ; + lv2:index 17 ; lv2:symbol "num_groups" ; lv2:name "Number of groups", "Nombre de groupes"@fr ; @@ -395,7 +386,7 @@ midnam:update a lv2:Feature . lv2:maximum 65535 ; ] , [ a lv2:OutputPort, lv2:ControlPort ; - lv2:index 19 ; + lv2:index 18 ; lv2:symbol "num_regions" ; lv2:name "Number of regions", "Nombre de régions"@fr ; @@ -406,7 +397,7 @@ midnam:update a lv2:Feature . lv2:maximum 65535 ; ] , [ a lv2:OutputPort, lv2:ControlPort ; - lv2:index 20 ; + lv2:index 19 ; lv2:symbol "num_samples" ; lv2:name "Number of samples", "Nombre d'échantillons"@fr ; diff --git a/plugins/lv2/sfizz_lv2.h b/plugins/lv2/sfizz_lv2.h index dfc3a1165..f48f2606f 100644 --- a/plugins/lv2/sfizz_lv2.h +++ b/plugins/lv2/sfizz_lv2.h @@ -44,32 +44,32 @@ #define SFIZZ__checkModification SFIZZ_URI ":" "check_modification" // OSC atoms #define SFIZZ__OSCBlob SFIZZ_URI ":" "OSCBlob" +#define SFIZZ__Notify SFIZZ_URI ":" "Notify" // Level atoms #define SFIZZ__AudioLevel SFIZZ_URI ":" "AudioLevel" enum { SFIZZ_CONTROL = 0, - SFIZZ_NOTIFY = 1, - SFIZZ_AUTOMATE = 2, - SFIZZ_LEFT = 3, - SFIZZ_RIGHT = 4, - SFIZZ_VOLUME = 5, - SFIZZ_POLYPHONY = 6, - SFIZZ_OVERSAMPLING = 7, - SFIZZ_PRELOAD = 8, - SFIZZ_FREEWHEELING = 9, - SFIZZ_SCALA_ROOT_KEY = 10, - SFIZZ_TUNING_FREQUENCY = 11, - SFIZZ_STRETCH_TUNING = 12, - SFIZZ_SAMPLE_QUALITY = 13, - SFIZZ_OSCILLATOR_QUALITY = 14, - SFIZZ_ACTIVE_VOICES = 15, - SFIZZ_NUM_CURVES = 16, - SFIZZ_NUM_MASTERS = 17, - SFIZZ_NUM_GROUPS = 18, - SFIZZ_NUM_REGIONS = 19, - SFIZZ_NUM_SAMPLES = 20, + SFIZZ_AUTOMATE = 1, + SFIZZ_LEFT = 2, + SFIZZ_RIGHT = 3, + SFIZZ_VOLUME = 4, + SFIZZ_POLYPHONY = 5, + SFIZZ_OVERSAMPLING = 6, + SFIZZ_PRELOAD = 7, + SFIZZ_FREEWHEELING = 8, + SFIZZ_SCALA_ROOT_KEY = 9, + SFIZZ_TUNING_FREQUENCY = 10, + SFIZZ_STRETCH_TUNING = 11, + SFIZZ_SAMPLE_QUALITY = 12, + SFIZZ_OSCILLATOR_QUALITY = 13, + SFIZZ_ACTIVE_VOICES = 14, + SFIZZ_NUM_CURVES = 15, + SFIZZ_NUM_MASTERS = 16, + SFIZZ_NUM_GROUPS = 17, + SFIZZ_NUM_REGIONS = 18, + SFIZZ_NUM_SAMPLES = 19, }; // For use with instance-access diff --git a/plugins/lv2/sfizz_lv2_plugin.h b/plugins/lv2/sfizz_lv2_plugin.h index d9c4fe434..903876844 100644 --- a/plugins/lv2/sfizz_lv2_plugin.h +++ b/plugins/lv2/sfizz_lv2_plugin.h @@ -34,7 +34,6 @@ struct sfizz_plugin_t // Ports const LV2_Atom_Sequence *control_port {}; - LV2_Atom_Sequence *notify_port {}; LV2_Atom_Sequence *automate_port {}; float *output_buffers[2] {}; const float *volume_port {}; @@ -55,7 +54,6 @@ struct sfizz_plugin_t float *num_samples_port {}; // Atom forge - LV2_Atom_Forge forge_notify {}; ///< Forge for writing notification atoms in run thread LV2_Atom_Forge forge_automate {}; ///< Forge for writing automation atoms in run thread LV2_Atom_Forge forge_secondary {}; ///< Forge for writing into other buffers @@ -92,6 +90,7 @@ struct sfizz_plugin_t LV2_URID sfizz_check_modification_uri {}; LV2_URID sfizz_active_voices_uri {}; LV2_URID sfizz_osc_blob_uri {}; + LV2_URID sfizz_notify_uri {}; LV2_URID sfizz_audio_level_uri {}; LV2_URID time_position_uri {}; LV2_URID time_bar_uri {}; diff --git a/plugins/lv2/sfizz_ui.cpp b/plugins/lv2/sfizz_ui.cpp index bd9438339..3756c7f83 100644 --- a/plugins/lv2/sfizz_ui.cpp +++ b/plugins/lv2/sfizz_ui.cpp @@ -116,6 +116,7 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface { LV2_URID sfizz_sfz_file_uri; LV2_URID sfizz_scala_file_uri; LV2_URID sfizz_osc_blob_uri; + LV2_URID sfizz_notify_uri; LV2_URID sfizz_audio_level_uri; std::unique_ptr ccmap; @@ -218,6 +219,7 @@ instantiate(const LV2UI_Descriptor *descriptor, self->sfizz_sfz_file_uri = map->map(map->handle, SFIZZ__sfzFile); self->sfizz_scala_file_uri = map->map(map->handle, SFIZZ__tuningfile); self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob); + self->sfizz_notify_uri = map->map(map->handle, SFIZZ__Notify); self->sfizz_audio_level_uri = map->map(map->handle, SFIZZ__AudioLevel); self->ccmap.reset(sfizz_lv2_ccmap_create(map)); From 3d8ff91f8c4812da4b6a6dce16680344783c7247 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 22 Jun 2021 20:14:32 +0200 Subject: [PATCH 093/193] macos: open SFZ files in the associated editor --- plugins/editor/src/editor/NativeHelpers.mm | 27 ++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/plugins/editor/src/editor/NativeHelpers.mm b/plugins/editor/src/editor/NativeHelpers.mm index da2eb1e48..e2d539908 100644 --- a/plugins/editor/src/editor/NativeHelpers.mm +++ b/plugins/editor/src/editor/NativeHelpers.mm @@ -24,11 +24,30 @@ static bool openFileWithApplication(const char *fileName, NSString *application) bool openFileInExternalEditor(const char *fileName) { - NSURL* applicationURL = (__bridge_transfer NSURL*)LSCopyDefaultApplicationURLForContentType( - kUTTypePlainText, kLSRolesEditor, nil); - if (!applicationURL || ![applicationURL isFileURL]) + NSURL* appURL = nil; + NSURL* fileURL = [NSURL fileURLWithPath:[NSString stringWithUTF8String:fileName]]; + const LSRolesMask roles = kLSRolesEditor; + + NSArray* editorApps = (__bridge_transfer NSArray*)LSCopyApplicationURLsForURL( + (__bridge CFURLRef)fileURL, roles); + + for (NSUInteger i = 0, n = [editorApps count]; i < n && !appURL; ++i) { + NSURL* url = [editorApps objectAtIndex:i]; + if (url && [url isFileURL]) + appURL = url; + } + + if (!appURL) { + NSURL* url = (__bridge_transfer NSURL*)LSCopyDefaultApplicationURLForContentType( + kUTTypePlainText, roles, nil); + if (url && [url isFileURL]) + appURL = url; + } + + if (!appURL) return false; - return openFileWithApplication(fileName, [applicationURL path]); + + return openFileWithApplication(fileName, [appURL path]); } bool openDirectoryInExplorer(const char *fileName) From 23a1ab268d9ad305d4ec48ff4daf582a30291f24 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 22 Jun 2021 21:18:16 +0200 Subject: [PATCH 094/193] Deduplicate load-or-import code from plugins --- plugins/lv2/sfizz.cpp | 17 ++-------------- plugins/vst/SfizzVstProcessor.cpp | 13 ++---------- src/CMakeLists.txt | 2 ++ src/sfizz/import/sfizz_import.cpp | 33 +++++++++++++++++++++++++++++++ src/sfizz/import/sfizz_import.h | 31 +++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 26 deletions(-) create mode 100644 src/sfizz/import/sfizz_import.cpp create mode 100644 src/sfizz/import/sfizz_import.h diff --git a/plugins/lv2/sfizz.cpp b/plugins/lv2/sfizz.cpp index c29d9ebfb..6f22f8fcb 100644 --- a/plugins/lv2/sfizz.cpp +++ b/plugins/lv2/sfizz.cpp @@ -35,7 +35,7 @@ #include "sfizz_lv2.h" #include "sfizz_lv2_plugin.h" -#include "sfizz/import/ForeignInstrument.h" +#include "sfizz/import/sfizz_import.h" #include "plugin/InstrumentDescription.h" #include @@ -1231,8 +1231,6 @@ sfizz_lv2_update_sfz_info(sfizz_plugin_t *self) static bool sfizz_lv2_load_file(sfizz_plugin_t *self, const char *file_path) { - bool status; - char buf[MAX_PATH_SIZE]; if (file_path[0] == '\0') { @@ -1241,18 +1239,7 @@ sfizz_lv2_load_file(sfizz_plugin_t *self, const char *file_path) } /// - const sfz::InstrumentFormatRegistry& formatRegistry = sfz::InstrumentFormatRegistry::getInstance(); - const sfz::InstrumentFormat* format = formatRegistry.getMatchingFormat(file_path); - - if (!format) - status = sfizz_load_file(self->synth, file_path); - else { - auto importer = format->createImporter(); - std::string virtual_path = std::string(file_path) + ".sfz"; - std::string sfz_text = importer->convertToSfz(file_path); - status = sfizz_load_string(self->synth, virtual_path.c_str(), sfz_text.c_str()); - } - + bool status = sfizz_load_or_import_file(self->synth, file_path, nullptr); sfizz_lv2_update_sfz_info(self); sfizz_lv2_update_file_info(self, file_path); return status; diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp index bb1bcac5e..cf01f04f5 100644 --- a/plugins/vst/SfizzVstProcessor.cpp +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -9,7 +9,7 @@ #include "SfizzVstState.h" #include "SfizzVstParameters.h" #include "SfizzVstIDs.h" -#include "sfizz/import/ForeignInstrument.h" +#include "sfizz/import/sfizz_import.h" #include "plugin/SfizzFileScan.h" #include "plugin/InstrumentDescription.h" #include "base/source/fstreamer.h" @@ -682,16 +682,7 @@ void SfizzVstProcessor::loadSfzFileOrDefault(const std::string& filePath, bool i sfz::Sfizz& synth = *_synth; if (!filePath.empty()) { - const sfz::InstrumentFormatRegistry& formatRegistry = sfz::InstrumentFormatRegistry::getInstance(); - const sfz::InstrumentFormat* format = formatRegistry.getMatchingFormat(filePath); - if (!format) - synth.loadSfzFile(filePath); - else { - auto importer = format->createImporter(); - std::string virtualPath = filePath + ".sfz"; - std::string sfzText = importer->convertToSfz(filePath); - synth.loadSfzString(virtualPath, sfzText); - } + sfizz_load_or_import_file(synth.handle(), filePath.c_str(), nullptr); } else { synth.loadSfzString("default.sfz", defaultSfzText); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 000c1eaa8..a1a2533b7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -251,11 +251,13 @@ endif() # Import library set(SFIZZ_IMPORT_HEADERS + sfizz/import/sfizz_import.h sfizz/import/ForeignInstrument.h sfizz/import/foreign_instruments/AudioFile.h sfizz/import/foreign_instruments/DecentSampler.h) set(SFIZZ_IMPORT_SOURCES + sfizz/import/sfizz_import.cpp sfizz/import/ForeignInstrument.cpp sfizz/import/foreign_instruments/AudioFile.cpp sfizz/import/foreign_instruments/DecentSampler.cpp) diff --git a/src/sfizz/import/sfizz_import.cpp b/src/sfizz/import/sfizz_import.cpp new file mode 100644 index 000000000..3048f7c3e --- /dev/null +++ b/src/sfizz/import/sfizz_import.cpp @@ -0,0 +1,33 @@ +// 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.h" +#include "sfizz_import.h" +#include "ForeignInstrument.h" + +bool sfizz_load_or_import_file(sfizz_synth_t* synth, const char* path, const char** format) +{ + const sfz::InstrumentFormatRegistry& ireg = sfz::InstrumentFormatRegistry::getInstance(); + const sfz::InstrumentFormat* ifmt = ireg.getMatchingFormat(path); + + if (!ifmt) { + if (!sfizz_load_file(synth, path)) + return false; + if (format) + *format = nullptr; + } + else { + auto importer = ifmt->createImporter(); + std::string virtualPath = std::string(path) + ".sfz"; + std::string sfzText = importer->convertToSfz(path); + if (!sfizz_load_string(synth, virtualPath.c_str(), sfzText.c_str())) + return false; + if (format) + *format = ifmt->name(); + } + + return true; +} diff --git a/src/sfizz/import/sfizz_import.h b/src/sfizz/import/sfizz_import.h new file mode 100644 index 000000000..7ea7312d7 --- /dev/null +++ b/src/sfizz/import/sfizz_import.h @@ -0,0 +1,31 @@ +// 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 + +typedef struct sfizz_synth_t sfizz_synth_t; + +/** + * @brief Loads or imports an instrument file. + * + * The file path can be absolute or relative. + * @since 1.0.1 + * + * @param synth The synth. + * @param path A null-terminated string representing a path to an instrument + * in SFZ format, or another format which can be imported. + * @param format An optional pointer to a string pointer, which receives the + * null-terminated name of the format if the file was imported, + * or null if the file was loaded directly as SFZ. + * + * @return @true when file loading went OK, + * @false if some error occured while loading. + * + * @par Thread-safety constraints + * - @b CT: the function must be invoked from the Control thread + * - @b OFF: the function cannot be invoked while a thread is calling @b RT functions + */ +bool sfizz_load_or_import_file(sfizz_synth_t* synth, const char* path, const char** format); From f9cc84b5f03eeeef1b7cbedfc0c6bbdc4d27a161 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 22 Jun 2021 21:25:39 +0200 Subject: [PATCH 095/193] Use the importer in the JACK client --- clients/CMakeLists.txt | 2 +- clients/jack_client.cpp | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/clients/CMakeLists.txt b/clients/CMakeLists.txt index 37e6ed774..a22bbe29c 100644 --- a/clients/CMakeLists.txt +++ b/clients/CMakeLists.txt @@ -1,6 +1,6 @@ if(SFIZZ_JACK) add_executable(sfizz_jack MidiHelpers.h jack_client.cpp) - target_link_libraries(sfizz_jack PRIVATE sfizz::sfizz sfizz::jack sfizz::spin_mutex absl::flags_parse) + target_link_libraries(sfizz_jack PRIVATE sfizz::import sfizz::sfizz sfizz::jack sfizz::spin_mutex absl::flags_parse) sfizz_enable_lto_if_needed(sfizz_jack) install(TARGETS sfizz_jack DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT "jack" OPTIONAL) diff --git a/clients/jack_client.cpp b/clients/jack_client.cpp index cd5f8fd74..806ddadb1 100644 --- a/clients/jack_client.cpp +++ b/clients/jack_client.cpp @@ -22,6 +22,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "sfizz.hpp" +#include "sfizz/import/sfizz_import.h" #include "MidiHelpers.h" #include #include @@ -188,7 +189,13 @@ int main(int argc, char** argv) sfz::Sfizz synth; synth.setOversamplingFactor(factor); synth.setPreloadSize(preload_size); - synth.loadSfzFile(filesToParse[0]); + + const char *importFormat = nullptr; + if (!sfizz_load_or_import_file(synth.handle(), filesToParse[0], &importFormat)) { + std::cout << "Could not load the instrument file: " << filesToParse[0] << '\n'; + return 1; + } + std::cout << "==========" << '\n'; std::cout << "Total:" << '\n'; std::cout << "\tMasters: " << synth.getNumMasters() << '\n'; @@ -211,6 +218,10 @@ int main(int argc, char** argv) for (auto& opcode : synth.getUnknownOpcodes()) std::cout << opcode << ','; std::cout << '\n'; + if (importFormat) { + std::cout << "==========" << '\n'; + std::cout << "Import format: " << importFormat << '\n'; + } // std::cout << std::flush; jack_status_t status; From fdf25d640738369dd8e057d252c6151d59807f38 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 22 Jun 2021 21:28:09 +0200 Subject: [PATCH 096/193] Enable the importer in Puredata --- plugins/puredata/CMakeLists.txt | 2 +- plugins/puredata/sfizz_puredata.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/puredata/CMakeLists.txt b/plugins/puredata/CMakeLists.txt index e861cf927..c8a33a65d 100644 --- a/plugins/puredata/CMakeLists.txt +++ b/plugins/puredata/CMakeLists.txt @@ -27,7 +27,7 @@ add_pd_external(sfizz_puredata "sfizz_puredata.c") target_compile_definitions(sfizz_puredata PRIVATE "SFIZZ_NUM_CCS=${SFIZZ_NUM_CCS}" "SFIZZ_VERSION=\"${CMAKE_PROJECT_VERSION}\"") -target_link_libraries(sfizz_puredata PRIVATE sfizz::sfizz) +target_link_libraries(sfizz_puredata PRIVATE sfizz::import sfizz::sfizz) set_target_properties(sfizz_puredata PROPERTIES OUTPUT_NAME "sfizz" diff --git a/plugins/puredata/sfizz_puredata.c b/plugins/puredata/sfizz_puredata.c index 94cfca220..4043b6cae 100644 --- a/plugins/puredata/sfizz_puredata.c +++ b/plugins/puredata/sfizz_puredata.c @@ -7,6 +7,7 @@ #include "GitBuildId.h" #include #include +#include #include #include #include @@ -62,7 +63,7 @@ static bool sfizz_tilde_do_load(t_sfizz_tilde* self) { bool loaded; if (self->filepath[0] != '\0') - loaded = sfizz_load_file(self->synth, self->filepath); + loaded = sfizz_load_or_import_file(self->synth, self->filepath, NULL); else loaded = sfizz_load_string(self->synth, "default.sfz", "sample=*sine"); return loaded; From 6c88b3f6636f2de07e376f6af4197462cae205ff Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 22 Jun 2021 21:30:16 +0200 Subject: [PATCH 097/193] Fix a warning with sfizz_import.h in C --- src/sfizz/import/sfizz_import.cpp | 1 - src/sfizz/import/sfizz_import.h | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sfizz/import/sfizz_import.cpp b/src/sfizz/import/sfizz_import.cpp index 3048f7c3e..f73fc67dd 100644 --- a/src/sfizz/import/sfizz_import.cpp +++ b/src/sfizz/import/sfizz_import.cpp @@ -4,7 +4,6 @@ // 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.h" #include "sfizz_import.h" #include "ForeignInstrument.h" diff --git a/src/sfizz/import/sfizz_import.h b/src/sfizz/import/sfizz_import.h index 7ea7312d7..1b4ddf365 100644 --- a/src/sfizz/import/sfizz_import.h +++ b/src/sfizz/import/sfizz_import.h @@ -5,8 +5,7 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #pragma once - -typedef struct sfizz_synth_t sfizz_synth_t; +#include /** * @brief Loads or imports an instrument file. From 7f49eab79f37958e3cb8aafe4fa5a5fa06fb212f Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 22 Jun 2021 21:38:54 +0200 Subject: [PATCH 098/193] Mark the importer API with extern C --- src/sfizz/import/sfizz_import.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sfizz/import/sfizz_import.h b/src/sfizz/import/sfizz_import.h index 1b4ddf365..7ae1184b1 100644 --- a/src/sfizz/import/sfizz_import.h +++ b/src/sfizz/import/sfizz_import.h @@ -7,6 +7,10 @@ #pragma once #include +#ifdef __cplusplus +extern "C" { +#endif + /** * @brief Loads or imports an instrument file. * @@ -28,3 +32,7 @@ * - @b OFF: the function cannot be invoked while a thread is calling @b RT functions */ bool sfizz_load_or_import_file(sfizz_synth_t* synth, const char* path, const char** format); + +#ifdef __cplusplus +} // extern "C" +#endif From d00115ed7daebe4e843378feab5a67261020841e Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 23 Jun 2021 00:12:05 +0200 Subject: [PATCH 099/193] Reformat --- src/sfizz/Voice.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 64ec01c8a..c93d4ecf9 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -1648,8 +1648,7 @@ bool Voice::checkOffGroup(const Region* other, int delay, int noteNumber) noexce if (impl.released()) return false; - if ( - (impl.triggerEvent_.type == TriggerEventType::NoteOn + if ((impl.triggerEvent_.type == TriggerEventType::NoteOn || impl.triggerEvent_.type == TriggerEventType::CC) && region->offBy && *region->offBy == other->group && (region->group != other->group || noteNumber != impl.triggerEvent_.number)) { From e4b4dcd6553edcf94e81c7bddbb38dda35be3023 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 23 Jun 2021 00:17:02 +0200 Subject: [PATCH 100/193] Add a test case --- tests/SynthT.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 3ca5db3fb..a7108eafa 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -1693,6 +1693,23 @@ TEST_CASE("[Synth] Off by a CC event") REQUIRE( playingSamples(synth) == std::vector { "*sine" }); } +TEST_CASE("[Synth] CC triggered off by a CC event") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + + synth.loadSfzString(fs::current_path(), R"( + group=1 off_by=2 sample=*saw hikey=-1 on_locc64=126 on_hicc64=127 + group=2 sample=*triangle hikey=-1 on_locc64=0 on_hicc64=1 + )"); + synth.cc(0, 64, 127); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*saw" }); + synth.cc(0, 64, 0); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*triangle" }); +} + TEST_CASE("[Synth] Off by a note-off event") { sfz::Synth synth; From 3318d02de9186e423c2ccef36d3b78297bce3401 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 23 Jun 2021 19:40:13 +0200 Subject: [PATCH 101/193] Update vstgui, fixes the compile-time pango check --- plugins/editor/external/vstgui4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/editor/external/vstgui4 b/plugins/editor/external/vstgui4 index c009dd294..2cf61f5e1 160000 --- a/plugins/editor/external/vstgui4 +++ b/plugins/editor/external/vstgui4 @@ -1 +1 @@ -Subproject commit c009dd294171cbb1bb5021830ff125a15966dea8 +Subproject commit 2cf61f5e1fefe4dcd3d19119ddf7b182719a2a5b From adccf5088d9f66bd0fdfba68956b329f7cc599a2 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 23 Jun 2021 22:22:22 +0200 Subject: [PATCH 102/193] Use a fork of VST public.sdk --- .github/workflows/build.yml | 26 ------------------- .gitmodules | 2 +- .../vst/external/VST_SDK/VST3_SDK/public.sdk | 2 +- 3 files changed, 2 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 31414eb92..8341c3907 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -251,19 +251,6 @@ jobs: run: | cp -vf "$GITHUB_WORKSPACE"/scripts/mingw_dwrite_3.h \ /usr/i686-w64-mingw32/include/dwrite_3.h - - name: Fix VST sources (case) - shell: bash - # need to convert some includes to lower case (as of VST 3.7.1) - run: | - find "$GITHUB_WORKSPACE"/plugins/vst/external/VST_SDK -type d -name source -exec \ - find {} -type f -name '*.[hc]' -o -name '*.[hc]pp' -print0 \; | \ - xargs -0 sed -i 's///' - - name: Fix VST sources (includes) - shell: bash - # need to add include (as of VST 3.7.2) - run: | - sed -i '0,/#include \n#include //' - - name: Fix VST sources (includes) - shell: bash - # need to add include (as of VST 3.7.2) - run: | - sed -i '0,/#include \n#include Date: Fri, 25 Jun 2021 17:59:48 +0200 Subject: [PATCH 103/193] Differentiate between releasing and "offed" The goal is to be able to off voices in their release stage, possibly shortening the release tail --- src/sfizz/Defaults.cpp | 2 +- src/sfizz/PolyphonyGroup.cpp | 2 +- src/sfizz/RegionSet.cpp | 2 +- src/sfizz/Voice.cpp | 17 +++++++++-- src/sfizz/Voice.h | 11 +++++-- src/sfizz/VoiceManager.cpp | 46 +++++++++++++++++------------ src/sfizz/VoiceManager.h | 1 + src/sfizz/VoiceStealing.cpp | 2 +- tests/PolyphonyT.cpp | 56 ++++++++++++++++++------------------ tests/SynthT.cpp | 8 +++--- tests/TestHelpers.cpp | 8 +++--- 11 files changed, 93 insertions(+), 62 deletions(-) diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index 0cb3803c8..5019f1fe7 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -44,7 +44,7 @@ Int32Spec oscillatorQuality { 1, {0, 3}, 0 }; UInt32Spec group { 0, {0, uint32_t_max}, 0 }; FloatSpec offTime { 6e-3f, {0.0f, 100.0f}, kPermissiveBounds }; UInt32Spec polyphony { config::maxVoices, {0, config::maxVoices}, kEnforceBounds }; -UInt32Spec notePolyphony { config::maxVoices, {0, config::maxVoices}, kEnforceBounds }; +UInt32Spec notePolyphony { config::maxVoices, {1, config::maxVoices}, kEnforceBounds }; UInt8Spec key { 60, {0, 127}, kCanBeNote }; UInt8Spec loKey { 0, {0, 127}, kCanBeNote }; UInt8Spec hiKey { 127, {0, 127}, kCanBeNote }; diff --git a/src/sfizz/PolyphonyGroup.cpp b/src/sfizz/PolyphonyGroup.cpp index 4d3ceb748..2e8ba56a4 100644 --- a/src/sfizz/PolyphonyGroup.cpp +++ b/src/sfizz/PolyphonyGroup.cpp @@ -30,6 +30,6 @@ void sfz::PolyphonyGroup::removeAllVoices() noexcept unsigned sfz::PolyphonyGroup::numPlayingVoices() const noexcept { return absl::c_count_if(voices, [](const Voice* v) { - return !v->releasedOrFree(); + return !v->offedOrFree(); }); } diff --git a/src/sfizz/RegionSet.cpp b/src/sfizz/RegionSet.cpp index dfd858724..35369faec 100644 --- a/src/sfizz/RegionSet.cpp +++ b/src/sfizz/RegionSet.cpp @@ -57,7 +57,7 @@ void sfz::RegionSet::removeVoiceFromHierarchy(const Region* region, const Voice* unsigned sfz::RegionSet::numPlayingVoices() const noexcept { return absl::c_count_if(voices, [](const Voice* v) { - return !v->releasedOrFree(); + return !v->offedOrFree(); }); } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 8a2ba35ba..467c09b4a 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -221,6 +221,7 @@ struct Voice::Impl State state_ { State::idle }; bool noteIsOff_ { false }; + bool offed_ { false }; enum class SustainState { Up, Sustaining }; SustainState sustainState_ { SustainState::Up }; enum class SostenutoState { Up, Sustaining, PreviouslyDown }; @@ -605,6 +606,7 @@ void Voice::Impl::off(int delay, bool fast) noexcept // TODO(jpc): Flex AmpEG } + offed_ = true; release(delay); } @@ -1637,8 +1639,18 @@ void Voice::Impl::fillWithGenerator(AudioSpan buffer) noexcept #endif } +bool Voice::released() const noexcept +{ + Impl& impl = *impl_; + return impl.released(); +} + bool Voice::Impl::released() const noexcept { + if (!region_ || state_ != State::playing) + return true; + + if (!region_->flexAmpEG) return egAmplitude_.isReleased(); else @@ -1678,6 +1690,7 @@ void Voice::reset() noexcept impl.floatPositionOffset_ = 0.0f; impl.noteIsOff_ = false; impl.sostenutoState_ = Impl::SostenutoState::Up; + impl.offed_ = false; impl.resetLoopInformation(); @@ -1756,13 +1769,13 @@ float Voice::getAveragePower() const noexcept return 0.0f; } -bool Voice::releasedOrFree() const noexcept +bool Voice::offedOrFree() const noexcept { Impl& impl = *impl_; if (impl.state_ != State::playing) return true; - return impl.released(); + return impl.offed_; } void Voice::setMaxFiltersPerVoice(size_t numFilters) diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index f8032a7d0..5b8d42c6e 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -206,12 +206,19 @@ class Voice { */ bool isFree() const noexcept; /** - * @brief Can the voice be reused (i.e. is it releasing or free) + * @brief Is the voice released? * * @return true * @return false */ - bool releasedOrFree() const noexcept; + bool released() const noexcept; + /** + * @brief Can the voice be reused (i.e. is it releasing after being killed or free) + * + * @return true + * @return false + */ + bool offedOrFree() const noexcept; /** * @brief Get the event that triggered the voice * diff --git a/src/sfizz/VoiceManager.cpp b/src/sfizz/VoiceManager.cpp index eb663fd6f..862626d74 100644 --- a/src/sfizz/VoiceManager.cpp +++ b/src/sfizz/VoiceManager.cpp @@ -164,6 +164,7 @@ void VoiceManager::requireNumVoices(int numVoices, Resources& resources) clear(); list_.reserve(numEffectiveVoices); + temp_.reserve(numEffectiveVoices); activeVoices_.reserve(numEffectiveVoices); for (int i = 0; i < numEffectiveVoices; ++i) { @@ -185,33 +186,42 @@ void VoiceManager::checkNotePolyphony(const Region* region, int delay, const Tri return; unsigned notePolyphonyCounter { 0 }; - Voice* selfMaskCandidate { nullptr }; + temp_.clear(); for (Voice* voice : activeVoices_) { const TriggerEvent& voiceTriggerEvent = voice->getTriggerEvent(); - if (!voice->releasedOrFree() + if (!voice->offedOrFree() && voice->getRegion()->group == region->group && voiceTriggerEvent.number == triggerEvent.number) { notePolyphonyCounter += 1; - switch (region->selfMask) { - case SelfMask::mask: - if (voiceTriggerEvent.value <= triggerEvent.value) { - if (!selfMaskCandidate - || selfMaskCandidate->getTriggerEvent().value > voiceTriggerEvent.value) { - selfMaskCandidate = voice; - } - } - break; - case SelfMask::dontMask: - if (!selfMaskCandidate || selfMaskCandidate->getAge() < voice->getAge()) - selfMaskCandidate = voice; - break; - } + if (region->selfMask == SelfMask::dontMask || voiceTriggerEvent.value <= triggerEvent.value) + temp_.push_back(voice); } } - if (notePolyphonyCounter >= *region->notePolyphony) { - SisterVoiceRing::offAllSisters(selfMaskCandidate, delay); + if (region->selfMask == SelfMask::mask) { + absl::c_sort(temp_, [](const Voice* lhs, const Voice* rhs) { + const auto lhsTrigger = lhs->getTriggerEvent(); + const auto rhsTrigger = rhs->getTriggerEvent(); + return lhsTrigger.value < rhsTrigger.value; + }); + } else if (region->selfMask == SelfMask::dontMask) { + absl::c_sort(temp_, [](const Voice* lhs, const Voice* rhs) { + return lhs->getAge() > rhs->getAge(); + }); + } else { + ASSERTFALSE; + } + + auto it = temp_.begin(); + unsigned targetPolyphony { *region->notePolyphony - 1 }; + while (notePolyphonyCounter > targetPolyphony && it < temp_.end()) { + Voice* voice = *it; + if (!voice->offedOrFree()) + SisterVoiceRing::offAllSisters(voice, delay); + + notePolyphonyCounter--; + it++; } } diff --git a/src/sfizz/VoiceManager.h b/src/sfizz/VoiceManager.h index e924ae96c..b4e22d4d5 100644 --- a/src/sfizz/VoiceManager.h +++ b/src/sfizz/VoiceManager.h @@ -136,6 +136,7 @@ struct VoiceManager final : public Voice::StateListener int getNumEffectiveVoices() const noexcept { return config::calculateActualVoices(numRequiredVoices_); } std::vector list_; std::vector activeVoices_; + std::vector temp_; // These are the `group=` groups where you can off voices std::vector polyphonyGroups_; std::unique_ptr stealer_ { absl::make_unique() }; diff --git a/src/sfizz/VoiceStealing.cpp b/src/sfizz/VoiceStealing.cpp index d99b447df..13c5dd565 100644 --- a/src/sfizz/VoiceStealing.cpp +++ b/src/sfizz/VoiceStealing.cpp @@ -42,7 +42,7 @@ Voice* genericPolyphonyCheck(absl::Span candidates, unsigned polyphony, */ constexpr bool ignoreVoice(const Voice* voice) { - return (voice == nullptr || voice->releasedOrFree()); + return (voice == nullptr || voice->offedOrFree()); } Voice* FirstStealer::checkRegionPolyphony(const Region* region, absl::Span candidates) diff --git a/tests/PolyphonyT.cpp b/tests/PolyphonyT.cpp index f2c56bf70..afa38523c 100644 --- a/tests/PolyphonyT.cpp +++ b/tests/PolyphonyT.cpp @@ -229,11 +229,11 @@ TEST_CASE("[Polyphony] Self-masking") REQUIRE( synth.getNumActiveVoices() == 3 ); // One of these is releasing REQUIRE( numPlayingVoices(synth) == 2 ); REQUIRE( synth.getVoiceView(0)->getTriggerEvent().value == 63_norm); - REQUIRE(!synth.getVoiceView(0)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(0)->offedOrFree()); REQUIRE( synth.getVoiceView(1)->getTriggerEvent().value == 62_norm); - REQUIRE( synth.getVoiceView(1)->releasedOrFree()); // The lowest velocity voice is the masking candidate + REQUIRE( synth.getVoiceView(1)->offedOrFree()); // The lowest velocity voice is the masking candidate REQUIRE( synth.getVoiceView(2)->getTriggerEvent().value == 64_norm); - REQUIRE(!synth.getVoiceView(2)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(2)->offedOrFree()); } TEST_CASE("[Polyphony] Not self-masking") @@ -250,11 +250,11 @@ TEST_CASE("[Polyphony] Not self-masking") REQUIRE( synth.getNumActiveVoices() == 3 ); // One of these is releasing REQUIRE( numPlayingVoices(synth) == 2 ); REQUIRE( synth.getVoiceView(0)->getTriggerEvent().value == 63_norm); - REQUIRE( synth.getVoiceView(0)->releasedOrFree()); + REQUIRE( synth.getVoiceView(0)->offedOrFree()); REQUIRE( synth.getVoiceView(1)->getTriggerEvent().value == 62_norm); - REQUIRE(!synth.getVoiceView(1)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(1)->offedOrFree()); REQUIRE( synth.getVoiceView(2)->getTriggerEvent().value == 64_norm); - REQUIRE(!synth.getVoiceView(2)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(2)->offedOrFree()); } TEST_CASE("[Polyphony] Self-masking with the exact same velocity") @@ -271,11 +271,11 @@ TEST_CASE("[Polyphony] Self-masking with the exact same velocity") REQUIRE( synth.getNumActiveVoices() == 3 ); // One of these is releasing REQUIRE( numPlayingVoices(synth) == 2 ); REQUIRE( synth.getVoiceView(0)->getTriggerEvent().value == 64_norm); - REQUIRE(!synth.getVoiceView(0)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(0)->offedOrFree()); REQUIRE( synth.getVoiceView(1)->getTriggerEvent().value == 63_norm); - REQUIRE( synth.getVoiceView(1)->releasedOrFree()); // The first one is the masking candidate since they have the same velocity + REQUIRE( synth.getVoiceView(1)->offedOrFree()); // The first one is the masking candidate since they have the same velocity REQUIRE( synth.getVoiceView(2)->getTriggerEvent().value == 63_norm); - REQUIRE(!synth.getVoiceView(2)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(2)->offedOrFree()); } TEST_CASE("[Polyphony] Self-masking only works from low to high") @@ -291,9 +291,9 @@ TEST_CASE("[Polyphony] Self-masking only works from low to high") REQUIRE( synth.getNumActiveVoices() == 2 ); // Both notes are playing REQUIRE( numPlayingVoices(synth) == 2 ); // id REQUIRE( synth.getVoiceView(0)->getTriggerEvent().value == 63_norm); - REQUIRE(!synth.getVoiceView(0)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(0)->offedOrFree()); REQUIRE( synth.getVoiceView(1)->getTriggerEvent().value == 62_norm); - REQUIRE(!synth.getVoiceView(1)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(1)->offedOrFree()); } TEST_CASE("[Polyphony] Note polyphony checks works across regions in the same polyphony group (default)") @@ -309,13 +309,13 @@ TEST_CASE("[Polyphony] Note polyphony checks works across regions in the same po synth.renderBlock(buffer); REQUIRE( numPlayingVoices(synth) == 1 ); REQUIRE( synth.getVoiceView(0)->getTriggerEvent().value == 62_norm); - REQUIRE( synth.getVoiceView(0)->releasedOrFree()); // got killed + REQUIRE( synth.getVoiceView(0)->offedOrFree()); // got killed REQUIRE( synth.getVoiceView(1)->getTriggerEvent().value == 62_norm); - REQUIRE( synth.getVoiceView(1)->releasedOrFree()); // got killed + REQUIRE( synth.getVoiceView(1)->offedOrFree()); // got killed REQUIRE( synth.getVoiceView(2)->getTriggerEvent().value == 63_norm); - REQUIRE( synth.getVoiceView(2)->releasedOrFree()); // got killed + REQUIRE( synth.getVoiceView(2)->offedOrFree()); // got killed REQUIRE( synth.getVoiceView(3)->getTriggerEvent().value == 63_norm); - REQUIRE(!synth.getVoiceView(3)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(3)->offedOrFree()); } TEST_CASE("[Polyphony] Note polyphony checks works across regions in the same polyphony group (default, with keyswitches)") @@ -338,9 +338,9 @@ TEST_CASE("[Polyphony] Note polyphony checks works across regions in the same po REQUIRE( synth.getNumActiveVoices() == 2 ); REQUIRE( numPlayingVoices(synth) == 1 ); REQUIRE( synth.getVoiceView(0)->getTriggerEvent().value == 63_norm); - REQUIRE( synth.getVoiceView(0)->releasedOrFree()); + REQUIRE( synth.getVoiceView(0)->offedOrFree()); REQUIRE( synth.getVoiceView(1)->getTriggerEvent().value == 64_norm); - REQUIRE(!synth.getVoiceView(1)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(1)->offedOrFree()); } @@ -358,13 +358,13 @@ TEST_CASE("[Polyphony] Note polyphony do not operate across polyphony groups") REQUIRE( synth.getNumActiveVoices() == 4); // Both notes are playing REQUIRE(numPlayingVoices(synth) == 2 ); REQUIRE( synth.getVoiceView(0)->getTriggerEvent().value == 62_norm); - REQUIRE( synth.getVoiceView(0)->releasedOrFree()); // got killed + REQUIRE( synth.getVoiceView(0)->offedOrFree()); // got killed REQUIRE( synth.getVoiceView(1)->getTriggerEvent().value == 62_norm); - REQUIRE( synth.getVoiceView(1)->releasedOrFree()); // got killed + REQUIRE( synth.getVoiceView(1)->offedOrFree()); // got killed REQUIRE( synth.getVoiceView(2)->getTriggerEvent().value == 63_norm); - REQUIRE(!synth.getVoiceView(2)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(2)->offedOrFree()); REQUIRE( synth.getVoiceView(3)->getTriggerEvent().value == 63_norm); - REQUIRE(!synth.getVoiceView(3)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(3)->offedOrFree()); } TEST_CASE("[Polyphony] Note polyphony do not operate across polyphony groups (with keyswitches)") @@ -387,9 +387,9 @@ TEST_CASE("[Polyphony] Note polyphony do not operate across polyphony groups (wi REQUIRE( synth.getNumActiveVoices() == 2 ); REQUIRE(numPlayingVoices(synth) == 2 ); REQUIRE( synth.getVoiceView(0)->getTriggerEvent().value == 63_norm); - REQUIRE(!synth.getVoiceView(0)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(0)->offedOrFree()); REQUIRE( synth.getVoiceView(1)->getTriggerEvent().value == 64_norm); - REQUIRE(!synth.getVoiceView(1)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(1)->offedOrFree()); } TEST_CASE("[Polyphony] Note polyphony operates on release voices") @@ -409,9 +409,9 @@ TEST_CASE("[Polyphony] Note polyphony operates on release voices") REQUIRE( synth.getNumActiveVoices() == 2 ); REQUIRE(numPlayingVoices(synth) == 1 ); REQUIRE( synth.getVoiceView(0)->getTriggerEvent().value == 63_norm); - REQUIRE( synth.getVoiceView(0)->releasedOrFree()); + REQUIRE( synth.getVoiceView(0)->offedOrFree()); REQUIRE( synth.getVoiceView(1)->getTriggerEvent().value == 65_norm); - REQUIRE(!synth.getVoiceView(1)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(1)->offedOrFree()); } TEST_CASE("[Polyphony] Note polyphony operates on release voices (masking works from low to high but takes into account the replaced velocity)") @@ -432,9 +432,9 @@ TEST_CASE("[Polyphony] Note polyphony operates on release voices (masking works REQUIRE( synth.getNumActiveVoices() == 2 ); REQUIRE( numPlayingVoices(synth) == 2 ); REQUIRE( synth.getVoiceView(0)->getTriggerEvent().value == 63_norm); - REQUIRE(!synth.getVoiceView(0)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(0)->offedOrFree()); REQUIRE( synth.getVoiceView(1)->getTriggerEvent().value == 61_norm); - REQUIRE(!synth.getVoiceView(1)->releasedOrFree()); + REQUIRE(!synth.getVoiceView(1)->offedOrFree()); } TEST_CASE("[Polyphony] Note polyphony operates on release voices and sustain pedal") @@ -459,8 +459,8 @@ TEST_CASE("[Polyphony] Note polyphony operates on release voices and sustain ped synth.renderBlock(buffer); std::vector expectedSamples2 { "*saw" }; std::vector expectedVelocities { 63_norm }; - REQUIRE( playingSamples(synth) == expectedSamples2 ); REQUIRE( playingVelocities(synth) == expectedVelocities ); + REQUIRE( playingSamples(synth) == expectedSamples2 ); } TEST_CASE("[Polyphony] Note polyphony operates on release voices and sustain pedal (masking)") diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index facec42e9..639acefa8 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -205,7 +205,7 @@ TEST_CASE("[Synth] Trigger=release and an envelope properly kills the voice at t synth.renderBlock(buffer); // Decay (0.02) synth.renderBlock(buffer); synth.renderBlock(buffer); // Release (0.1) - REQUIRE(synth.getVoiceView(0)->releasedOrFree()); + REQUIRE(synth.getVoiceView(0)->offedOrFree()); // Release is 0.1s for (int i = 0; i < 10; ++i) synth.renderBlock(buffer); @@ -232,7 +232,7 @@ TEST_CASE("[Synth] Trigger=release_key and an envelope properly kills the voice synth.renderBlock(buffer); // Decay (0.02) synth.renderBlock(buffer); synth.renderBlock(buffer); // Release (0.1) - REQUIRE(synth.getVoiceView(0)->releasedOrFree()); + REQUIRE(synth.getVoiceView(0)->released()); // Release is 0.1s for (int i = 0; i < 10; ++i) synth.renderBlock(buffer); @@ -259,7 +259,7 @@ TEST_CASE("[Synth] loopmode=one_shot and an envelope properly kills the voice at synth.renderBlock(buffer); // Decay (0.02) synth.renderBlock(buffer); synth.renderBlock(buffer); // Release (0.1) - REQUIRE(synth.getVoiceView(0)->releasedOrFree()); + REQUIRE(synth.getVoiceView(0)->released()); // Release is 0.1s for (int i = 0; i < 10; ++i) synth.renderBlock(buffer); @@ -1934,7 +1934,7 @@ TEST_CASE("[Synth] Sustain cancels release is off by default") synth.cc(0, 64, 127); synth.renderBlock(buffer); REQUIRE( playingSamples(synth) == std::vector { } ); -} +} TEST_CASE("[Synth] Resets all controllers to default values") { diff --git a/tests/TestHelpers.cpp b/tests/TestHelpers.cpp index 78e6cb854..92b526303 100644 --- a/tests/TestHelpers.cpp +++ b/tests/TestHelpers.cpp @@ -69,7 +69,7 @@ const std::vector getPlayingVoices(const sfz::Synth& synth) std::vector playingVoices; for (int i = 0; i < synth.getNumVoices(); ++i) { const auto* voice = synth.getVoiceView(i); - if (!voice->releasedOrFree()) + if (!voice->released()) playingVoices.push_back(voice); } return playingVoices; @@ -78,7 +78,7 @@ const std::vector getPlayingVoices(const sfz::Synth& synth) unsigned numPlayingVoices(const sfz::Synth& synth) { return absl::c_count_if(getActiveVoices(synth), [](const sfz::Voice* v) { - return !v->releasedOrFree(); + return !v->released(); }); } @@ -87,7 +87,7 @@ const std::vector playingSamples(const sfz::Synth& synth) std::vector samples; for (int i = 0; i < synth.getNumVoices(); ++i) { const auto* voice = synth.getVoiceView(i); - if (!voice->releasedOrFree()) { + if (!voice->released()) { if (auto region = voice->getRegion()) samples.push_back(region->sampleId->filename()); } @@ -100,7 +100,7 @@ const std::vector playingVelocities(const sfz::Synth& synth) std::vector velocities; for (int i = 0; i < synth.getNumVoices(); ++i) { const auto* voice = synth.getVoiceView(i); - if (!voice->releasedOrFree()) + if (!voice->released()) velocities.push_back(voice->getTriggerEvent().value); } return velocities; From c411d0ed61e48935ba6ce5a05de8686294ce6f97 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Fri, 25 Jun 2021 19:44:04 +0200 Subject: [PATCH 104/193] Add tests --- tests/PolyphonyT.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ tests/TestHelpers.cpp | 7 +++++++ tests/TestHelpers.h | 8 ++++++++ 3 files changed, 57 insertions(+) diff --git a/tests/PolyphonyT.cpp b/tests/PolyphonyT.cpp index afa38523c..0a966da9c 100644 --- a/tests/PolyphonyT.cpp +++ b/tests/PolyphonyT.cpp @@ -528,3 +528,45 @@ TEST_CASE("[Polyphony] Bi-directional choking (with note_polyphony)") synth.renderBlock(buffer); REQUIRE( playingSamples(synth) == std::vector { "kick.wav" } ); } + +TEST_CASE("[Polyphony] Choke long release tails") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + sample=*saw ampeg_attack=0.1 ampeg_release=10 polyphony=1 + )"); + int attackBlocks = static_cast(0.1f / synth.getSamplesPerBlock() * 48000.0f) + 1; + synth.noteOn(0, 60, 63 ); + for (int i = 0; i < attackBlocks; ++i) + synth.renderBlock(buffer); + synth.noteOff(10, 60, 63 ); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 0 ); // Released + REQUIRE( numActiveVoices(synth) == 1 ); + synth.noteOn(0, 60, 63 ); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 1 ); // Not released, attack phase + REQUIRE( numActiveVoices(synth) == 1 ); +} + +TEST_CASE("[Polyphony] Choke long release tails with note_polyphony") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + sample=*saw ampeg_attack=0.1 ampeg_release=10 note_polyphony=1 + )"); + int attackBlocks = static_cast(0.1f / synth.getSamplesPerBlock() * 48000.0f) + 1; + synth.noteOn(0, 60, 63 ); + for (int i = 0; i < attackBlocks; ++i) + synth.renderBlock(buffer); + synth.noteOff(10, 60, 63 ); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 0 ); // Released + REQUIRE( numActiveVoices(synth) == 1 ); + synth.noteOn(0, 60, 63 ); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 1 ); // Not released, attack phase + REQUIRE( numActiveVoices(synth) == 1 ); +} diff --git a/tests/TestHelpers.cpp b/tests/TestHelpers.cpp index 92b526303..4a81175e8 100644 --- a/tests/TestHelpers.cpp +++ b/tests/TestHelpers.cpp @@ -82,6 +82,13 @@ unsigned numPlayingVoices(const sfz::Synth& synth) }); } +unsigned numActiveVoices(const sfz::Synth& synth) +{ + return absl::c_count_if(getActiveVoices(synth), [](const sfz::Voice* v) { + return !v->offedOrFree(); + }); +} + const std::vector playingSamples(const sfz::Synth& synth) { std::vector samples; diff --git a/tests/TestHelpers.h b/tests/TestHelpers.h index 454f221da..2145555eb 100644 --- a/tests/TestHelpers.h +++ b/tests/TestHelpers.h @@ -78,6 +78,14 @@ const std::vector getPlayingVoices(const sfz::Synth& synth); */ unsigned numPlayingVoices(const sfz::Synth& synth); +/** + * @brief Count the number of active (not free or offed) voices from the synth + * + * @param synth + * @return unsigned + */ +unsigned numActiveVoices(const sfz::Synth& synth); + /** * @brief Get the playing samples * From 503ff04f9bb8a5507eda28a61fd92d7257d09e56 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 26 Jun 2021 18:56:42 +0200 Subject: [PATCH 105/193] Add modulations for Flex EG time and level --- src/sfizz/Defaults.cpp | 2 ++ src/sfizz/Defaults.h | 2 ++ src/sfizz/FlexEGDescription.h | 4 ++++ src/sfizz/FlexEnvelope.cpp | 15 ++++++++++++++- src/sfizz/FlexEnvelope.h | 3 ++- src/sfizz/Region.cpp | 20 ++++++++++++++++++++ src/sfizz/Synth.cpp | 6 ++++++ src/sfizz/Voice.cpp | 4 +++- tests/FlexEGT.cpp | 16 ++++++++-------- 9 files changed, 61 insertions(+), 11 deletions(-) diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index 5019f1fe7..40ce37c7f 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -152,7 +152,9 @@ BoolSpec flexEGAmpeg { false, {0, 1}, kEnforceBounds }; BoolSpec flexEGDynamic { 0, {0, 1}, kEnforceBounds }; Int32Spec flexEGSustain { 0, {0, 100}, kEnforceLowerBound|kPermissiveUpperBound }; FloatSpec flexEGPointTime { 0.0f, {0.0f, 100.0f}, kPermissiveBounds }; +FloatSpec flexEGPointTimeMod { 0.0f, {-100.0f, 100.0f}, kPermissiveBounds }; FloatSpec flexEGPointLevel { 0.0f, {-1.0f, 1.0f}, kPermissiveBounds }; +FloatSpec flexEGPointLevelMod { 0.0f, {-1.0f, 1.0f}, kPermissiveBounds }; FloatSpec flexEGPointShape { 0.0f, {-100.0f, 100.0f}, kPermissiveBounds }; Int32Spec sampleQuality { 2, {0, 10}, 0 }; Int32Spec octaveOffset { 0, {-10, 10}, kPermissiveBounds }; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 41a72607d..7d6c54fa9 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -264,7 +264,9 @@ namespace Default extern const OpcodeSpec flexEGDynamic; extern const OpcodeSpec flexEGSustain; extern const OpcodeSpec flexEGPointTime; + extern const OpcodeSpec flexEGPointTimeMod; extern const OpcodeSpec flexEGPointLevel; + extern const OpcodeSpec flexEGPointLevelMod; extern const OpcodeSpec flexEGPointShape; extern const OpcodeSpec sampleQuality; extern const OpcodeSpec octaveOffset; diff --git a/src/sfizz/FlexEGDescription.h b/src/sfizz/FlexEGDescription.h index a4cc75ef4..16a7e566c 100644 --- a/src/sfizz/FlexEGDescription.h +++ b/src/sfizz/FlexEGDescription.h @@ -6,6 +6,7 @@ #pragma once #include "Defaults.h" +#include "CCMap.h" #include #include @@ -21,6 +22,9 @@ struct FlexEGPoint { float time { Default::flexEGPointTime }; // duration until next step (s) float level { Default::flexEGPointLevel }; // normalized amplitude + CCMap ccTime; + CCMap ccLevel; + void setShape(float shape); float shape() const noexcept { return shape_; } const Curve& curve() const; diff --git a/src/sfizz/FlexEnvelope.cpp b/src/sfizz/FlexEnvelope.cpp index 98e095521..902735473 100644 --- a/src/sfizz/FlexEnvelope.cpp +++ b/src/sfizz/FlexEnvelope.cpp @@ -9,8 +9,11 @@ - [ ] egN_points (purpose unknown) - [x] egN_timeX +- [x] egN_timeX_onccY - [x] egN_levelX +- [x] egN_levelX_onccY - [x] egN_shapeX +- [ ] egN_shapeX_onccY - [x] egN_sustain - [ ] egN_dynamic - [ ] egN_loop @@ -21,6 +24,8 @@ #include "FlexEnvelope.h" #include "FlexEGDescription.h" #include "Curve.h" +#include "MidiState.h" +#include "Resources.h" #include "Config.h" #include "SIMDHelpers.h" #include @@ -28,6 +33,7 @@ namespace sfz { struct FlexEnvelope::Impl { + const Resources* resources_ { nullptr }; const FlexEGDescription* desc_ { nullptr }; float samplePeriod_ { 1.0 / config::defaultSampleRate }; size_t delayFramesLeft_ { 0 }; @@ -53,9 +59,11 @@ struct FlexEnvelope::Impl { bool advanceToNextStage(); }; -FlexEnvelope::FlexEnvelope() +FlexEnvelope::FlexEnvelope(Resources &resources) : impl_(new Impl) { + Impl& impl = *impl_; + impl.resources_ = &resources; } FlexEnvelope::~FlexEnvelope() @@ -249,6 +257,7 @@ void FlexEnvelope::Impl::process(absl::Span out) bool FlexEnvelope::Impl::advanceToStage(unsigned stageNumber) { const FlexEGDescription& desc = *desc_; + const MidiState& midiState = resources_->getMidiState(); currentStageNumber_ = stageNumber; @@ -258,7 +267,11 @@ bool FlexEnvelope::Impl::advanceToStage(unsigned stageNumber) const FlexEGPoint& point = desc.points[stageNumber]; stageSourceLevel_ = currentLevel_; stageTargetLevel_ = point.level; + for (const CCData& mod : point.ccLevel) + stageTargetLevel_ += midiState.getCCValue(mod.cc) * mod.data; stageTime_ = point.time; + for (const CCData& mod : point.ccTime) + stageTime_ += midiState.getCCValue(mod.cc) * mod.data; stageSustained_ = int(stageNumber) == desc.sustain; stageCurve_ = &point.curve(); diff --git a/src/sfizz/FlexEnvelope.h b/src/sfizz/FlexEnvelope.h index 6a737cf18..a72d280f1 100644 --- a/src/sfizz/FlexEnvelope.h +++ b/src/sfizz/FlexEnvelope.h @@ -10,13 +10,14 @@ namespace sfz { struct FlexEGDescription; +class Resources; /** Flex envelope generator (according to ARIA) */ class FlexEnvelope { public: - FlexEnvelope(); + explicit FlexEnvelope(Resources &resources); ~FlexEnvelope(); /** diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index dc0694132..344d7c6a4 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -1452,12 +1452,32 @@ bool sfz::Region::parseEGOpcodeV2(const Opcode& opcode) else return false; break; + case hash("eg&_time&_oncc&"): + if (FlexEGPoint* point = getOrCreateEGPoint()) { + auto ccNumber = opcode.parameters.back(); + if (ccNumber >= config::numCCs) + return false; + point->ccTime[ccNumber] = opcode.read(Default::flexEGPointTimeMod); + } + else + return false; + break; case hash("eg&_level&"): if (FlexEGPoint* point = getOrCreateEGPoint()) point->level = opcode.read(Default::flexEGPointLevel); else return false; break; + case hash("eg&_level&_oncc&"): + if (FlexEGPoint* point = getOrCreateEGPoint()) { + auto ccNumber = opcode.parameters.back(); + if (ccNumber >= config::numCCs) + return false; + point->ccLevel[ccNumber] = opcode.read(Default::flexEGPointLevelMod); + } + else + return false; + break; case hash("eg&_shape&"): if (FlexEGPoint* point = getOrCreateEGPoint()) point->setShape(opcode.read(Default::flexEGPointShape)); diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index f5d2a3859..0e7f4dbe7 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -2068,6 +2068,12 @@ void Synth::Impl::collectUsedCCsFromRegion(BitArray& usedCCs, co collectUsedCCsFromCCMap(usedCCs, lfo.delayCC); collectUsedCCsFromCCMap(usedCCs, lfo.fadeCC); } + for (const FlexEGDescription& flexEG : region.flexEGs) { + for (const FlexEGPoint& point : flexEG.points) { + collectUsedCCsFromCCMap(usedCCs, point.ccTime); + collectUsedCCsFromCCMap(usedCCs, point.ccLevel); + } + } collectUsedCCsFromCCMap(usedCCs, region.ccConditions); collectUsedCCsFromCCMap(usedCCs, region.ccTriggers); collectUsedCCsFromCCMap(usedCCs, region.crossfadeCCInRange); diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 467c09b4a..14f148817 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -1817,10 +1817,12 @@ void Voice::setMaxLFOsPerVoice(size_t numLFOs) void Voice::setMaxFlexEGsPerVoice(size_t numFlexEGs) { Impl& impl = *impl_; + Resources& resources = impl.resources_; + impl.flexEGs_.resize(numFlexEGs); for (size_t i = 0; i < numFlexEGs; ++i) { - auto eg = absl::make_unique(); + auto eg = absl::make_unique(resources); eg->setSampleRate(impl.sampleRate_); impl.flexEGs_[i] = std::move(eg); } diff --git a/tests/FlexEGT.cpp b/tests/FlexEGT.cpp index 76657eea4..d4e671c6b 100644 --- a/tests/FlexEGT.cpp +++ b/tests/FlexEGT.cpp @@ -107,7 +107,7 @@ TEST_CASE("[FlexEG] Coarse numerical envelope test (No release)") eg1_time2=0.5 eg1_level2=1 eg1_sustain=2 )"); - sfz::FlexEnvelope envelope; + sfz::FlexEnvelope envelope(synth.getResources()); REQUIRE(synth.getNumRegions() == 1); REQUIRE( synth.getRegionView(0)->flexEGs.size() == 1 ); envelope.configure(&synth.getRegionView(0)->flexEGs[0]); @@ -132,7 +132,7 @@ TEST_CASE("[FlexEG] Detailed numerical envelope test") eg1_time2=0.5 eg1_level2=1 eg1_sustain=2 )"); - sfz::FlexEnvelope envelope; + sfz::FlexEnvelope envelope(synth.getResources()); REQUIRE(synth.getNumRegions() == 1); REQUIRE( synth.getRegionView(0)->flexEGs.size() == 1 ); envelope.configure(&synth.getRegionView(0)->flexEGs[0]); @@ -155,7 +155,7 @@ TEST_CASE("[FlexEG] Coarse numerical envelope test (with release)") eg1_time2=0.5 eg1_level2=1 eg1_sustain=2 )"); - sfz::FlexEnvelope envelope; + sfz::FlexEnvelope envelope(synth.getResources()); REQUIRE(synth.getNumRegions() == 1); REQUIRE( synth.getRegionView(0)->flexEGs.size() == 1 ); envelope.configure(&synth.getRegionView(0)->flexEGs[0]); @@ -184,7 +184,7 @@ TEST_CASE("[FlexEG] Detailed numerical envelope test (with release and release r eg1_time3=0.5 eg1_level3=0 eg1_sustain=2 )"); - sfz::FlexEnvelope envelope; + sfz::FlexEnvelope envelope(synth.getResources()); REQUIRE(synth.getNumRegions() == 1); REQUIRE( synth.getRegionView(0)->flexEGs.size() == 1 ); envelope.configure(&synth.getRegionView(0)->flexEGs[0]); @@ -216,7 +216,7 @@ TEST_CASE("[FlexEG] Coarse numerical envelope test (with shapes)") eg1_sustain=2 eg1_time3=0.5 eg1_level3=0 eg1_shape3=4 )"); - sfz::FlexEnvelope envelope; + sfz::FlexEnvelope envelope(synth.getResources()); REQUIRE(synth.getNumRegions() == 1); REQUIRE( synth.getRegionView(0)->flexEGs.size() == 1 ); envelope.configure(&synth.getRegionView(0)->flexEGs[0]); @@ -244,7 +244,7 @@ TEST_CASE("[FlexEG] Detailed numerical envelope test (with shapes)") eg1_time3=0.5 eg1_level3=0 eg1_shape3=4 eg1_sustain=2 )"); - sfz::FlexEnvelope envelope; + sfz::FlexEnvelope envelope(synth.getResources()); REQUIRE(synth.getNumRegions() == 1); REQUIRE( synth.getRegionView(0)->flexEGs.size() == 1 ); envelope.configure(&synth.getRegionView(0)->flexEGs[0]); @@ -276,7 +276,7 @@ TEST_CASE("[FlexEG] Zero delay transitions") eg1_time3=1 eg1_level3=.5 eg1_sustain=3 eg1_time4=1 eg1_level4=1 )"); - sfz::FlexEnvelope envelope; + sfz::FlexEnvelope envelope(synth.getResources()); REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getRegionView(0)->flexEGs.size() == 1); envelope.configure(&synth.getRegionView(0)->flexEGs[0]); @@ -303,7 +303,7 @@ TEST_CASE("[FlexEG] Early release") eg1_time2=1.0 eg1_level2=1.0 eg1_sustain=2 eg1_time3=1.0 eg1_level3=0.0 )"); - sfz::FlexEnvelope envelope; + sfz::FlexEnvelope envelope(synth.getResources()); REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getRegionView(0)->flexEGs.size() == 1); envelope.configure(&synth.getRegionView(0)->flexEGs[0]); From 4de9b42427ee2c8ca8c6ff1304621f575644f388 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 26 Jun 2021 21:09:36 +0200 Subject: [PATCH 106/193] Add tests --- src/sfizz/FlexEGDescription.cpp | 18 +++++++++++++++ src/sfizz/FlexEGDescription.h | 4 ++++ src/sfizz/FlexEnvelope.cpp | 8 ++----- tests/FlexEGT.cpp | 40 +++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 6 deletions(-) diff --git a/src/sfizz/FlexEGDescription.cpp b/src/sfizz/FlexEGDescription.cpp index 18ea92e36..f92e20eb6 100644 --- a/src/sfizz/FlexEGDescription.cpp +++ b/src/sfizz/FlexEGDescription.cpp @@ -6,6 +6,7 @@ #include "FlexEGDescription.h" #include "Curve.h" +#include "MidiState.h" #include #include @@ -84,4 +85,21 @@ void FlexEGs::clearUnusedCurves() } } +/// +float FlexEGPoint::getTime(const MidiState& state) const noexcept +{ + float returnedValue { time }; + for (const CCData& mod : ccTime) + returnedValue += state.getCCValue(mod.cc) * mod.data; + return returnedValue; +} + +float FlexEGPoint::getLevel(const MidiState& state) const noexcept +{ + float returnedValue { level }; + for (const CCData& mod : ccLevel) + returnedValue += state.getCCValue(mod.cc) * mod.data; + return returnedValue; +} + } // namespace sfz diff --git a/src/sfizz/FlexEGDescription.h b/src/sfizz/FlexEGDescription.h index 16a7e566c..4c816befb 100644 --- a/src/sfizz/FlexEGDescription.h +++ b/src/sfizz/FlexEGDescription.h @@ -12,6 +12,7 @@ namespace sfz { class Curve; +class MidiState; namespace FlexEGs { std::shared_ptr getShapeCurve(float shape); @@ -25,6 +26,9 @@ struct FlexEGPoint { CCMap ccTime; CCMap ccLevel; + float getTime(const MidiState& state) const noexcept; + float getLevel(const MidiState& state) const noexcept; + void setShape(float shape); float shape() const noexcept { return shape_; } const Curve& curve() const; diff --git a/src/sfizz/FlexEnvelope.cpp b/src/sfizz/FlexEnvelope.cpp index 902735473..ae06c022e 100644 --- a/src/sfizz/FlexEnvelope.cpp +++ b/src/sfizz/FlexEnvelope.cpp @@ -266,12 +266,8 @@ bool FlexEnvelope::Impl::advanceToStage(unsigned stageNumber) const FlexEGPoint& point = desc.points[stageNumber]; stageSourceLevel_ = currentLevel_; - stageTargetLevel_ = point.level; - for (const CCData& mod : point.ccLevel) - stageTargetLevel_ += midiState.getCCValue(mod.cc) * mod.data; - stageTime_ = point.time; - for (const CCData& mod : point.ccTime) - stageTime_ += midiState.getCCValue(mod.cc) * mod.data; + stageTargetLevel_ = point.getLevel(midiState); + stageTime_ = point.getTime(midiState); stageSustained_ = int(stageNumber) == desc.sustain; stageCurve_ = &point.curve(); diff --git a/tests/FlexEGT.cpp b/tests/FlexEGT.cpp index d4e671c6b..a35711e65 100644 --- a/tests/FlexEGT.cpp +++ b/tests/FlexEGT.cpp @@ -417,3 +417,43 @@ TEST_CASE("[FlexEG] Free-running flex AmpEG (no sustain)") synth.renderBlock(buffer); REQUIRE( synth.getNumActiveVoices() == 0 ); } + +TEST_CASE("[FlexEG] Modulation of time and level") +{ + sfz::Synth synth; + + synth.loadSfzString(fs::current_path(), R"( + sample=*noise + eg1_time1=0 eg1_level1=1 + eg1_time2=0.7 eg1_level2=0.5 + eg1_time2_oncc1=-0.7 eg1_level2_oncc2=0.5 + eg1_time3=0.3 eg1_level3=0.0 + )"); + + REQUIRE( synth.getNumRegions() == 1 ); + const sfz::Region* region = synth.getRegionView(0); + REQUIRE( region->flexEGs.size() == 1 ); + const sfz::FlexEGDescription& desc = synth.getRegionView(0)->flexEGs[0]; + REQUIRE( desc.points.size() == 4 ); + + REQUIRE( desc.points[2].time == Approx(0.7f) ); + REQUIRE( desc.points[2].level == Approx(0.5f) ); + + sfz::MidiState state; + + REQUIRE( desc.points[2].getTime(state) == Approx(0.7f) ); + state.ccEvent(0, 1, 0.0f); + REQUIRE( desc.points[2].getTime(state) == Approx(0.7f) ); + state.ccEvent(0, 1, 0.5f); + REQUIRE( desc.points[2].getTime(state) == Approx(0.35f) ); + state.ccEvent(0, 1, 1.0f); + REQUIRE( desc.points[2].getTime(state) == Approx(0.0f) ); + + REQUIRE( desc.points[2].getLevel(state) == Approx(0.5f) ); + state.ccEvent(0, 2, 0.0f); + REQUIRE( desc.points[2].getLevel(state) == Approx(0.5f) ); + state.ccEvent(0, 2, 0.5f); + REQUIRE( desc.points[2].getLevel(state) == Approx(0.75f) ); + state.ccEvent(0, 2, 1.0f); + REQUIRE( desc.points[2].getLevel(state) == Approx(1.0f) ); +} From 5da331cf18e71512ac64b79f1a609ac51dabdb06 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 23 Jun 2021 12:32:54 +0200 Subject: [PATCH 107/193] Parse CC modifiers for flex EGs --- src/sfizz/Synth.cpp | 3 +++ src/sfizz/SynthMessaging.cpp | 44 +++++++++++++++++++++++++++++++ tests/RegionValuesT.cpp | 50 ++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 0e7f4dbe7..1e200b58d 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -2045,6 +2045,7 @@ void Synth::Impl::collectUsedCCsFromRegion(BitArray& usedCCs, co collectUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccHold); collectUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccStart); collectUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccSustain); + if (region.pitchEG) { collectUsedCCsFromCCMap(usedCCs, region.pitchEG->ccAttack); collectUsedCCsFromCCMap(usedCCs, region.pitchEG->ccRelease); @@ -2054,6 +2055,7 @@ void Synth::Impl::collectUsedCCsFromRegion(BitArray& usedCCs, co collectUsedCCsFromCCMap(usedCCs, region.pitchEG->ccStart); collectUsedCCsFromCCMap(usedCCs, region.pitchEG->ccSustain); } + if (region.filterEG) { collectUsedCCsFromCCMap(usedCCs, region.filterEG->ccAttack); collectUsedCCsFromCCMap(usedCCs, region.filterEG->ccRelease); @@ -2063,6 +2065,7 @@ void Synth::Impl::collectUsedCCsFromRegion(BitArray& usedCCs, co collectUsedCCsFromCCMap(usedCCs, region.filterEG->ccStart); collectUsedCCsFromCCMap(usedCCs, region.filterEG->ccSustain); } + for (const LFODescription& lfo : region.lfos) { collectUsedCCsFromCCMap(usedCCs, lfo.phaseCC); collectUsedCCsFromCCMap(usedCCs, lfo.delayCC); diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index b53cf1a09..3e6a87f2b 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -51,6 +51,16 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co break; \ const auto& lfo = region.lfos[idx]; + #define GET_EG_OR_BREAK(idx) \ + if (idx >= region.flexEGs.size()) \ + break; \ + auto& eg = region.flexEGs[idx]; + + #define GET_EG_POINT_OR_BREAK(idx) \ + if (idx >= eg.points.size()) \ + break; \ + auto& point = eg.points[idx]; + MATCH("/hello", "") { client.receive(delay, "/hello", "", nullptr); } break; @@ -1352,10 +1362,44 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co client.receive<'i'>(delay, path, static_cast(lfo.sub[0].wave)); } break; + MATCH("/region&/eg&/point&/time", "") { + GET_REGION_OR_BREAK(indices[0]) + GET_EG_OR_BREAK(indices[1]) + GET_EG_POINT_OR_BREAK(indices[2] + 1) + + client.receive<'f'>(delay, path, point.time); + } break; + + MATCH("/region&/eg&/point&/time_cc&", "") { + GET_REGION_OR_BREAK(indices[0]) + GET_EG_OR_BREAK(indices[1]) + GET_EG_POINT_OR_BREAK(indices[2] + 1) + + client.receive<'f'>(delay, path, point.ccTime.getWithDefault(indices[3])); + } break; + + MATCH("/region&/eg&/point&/level", "") { + GET_REGION_OR_BREAK(indices[0]) + GET_EG_OR_BREAK(indices[1]) + GET_EG_POINT_OR_BREAK(indices[2] + 1) + + client.receive<'f'>(delay, path, point.level); + } break; + + MATCH("/region&/eg&/point&/level_cc&", "") { + GET_REGION_OR_BREAK(indices[0]) + GET_EG_OR_BREAK(indices[1]) + GET_EG_POINT_OR_BREAK(indices[2] + 1) + + client.receive<'f'>(delay, path, point.ccLevel.getWithDefault(indices[3])); + } break; + #undef GET_REGION_OR_BREAK #undef GET_FILTER_OR_BREAK #undef GET_EQ_OR_BREAK #undef GET_LFO_OR_BREAK + #undef GET_EG_OR_BREAK + #undef GET_EG_POINT_OR_BREAK //---------------------------------------------------------------------- // Setting values diff --git a/tests/RegionValuesT.cpp b/tests/RegionValuesT.cpp index a6fc4dd7e..970a259ed 100644 --- a/tests/RegionValuesT.cpp +++ b/tests/RegionValuesT.cpp @@ -3251,3 +3251,53 @@ TEST_CASE("[Values] EQ value bounds") REQUIRE(messageList == expected); } } + +TEST_CASE("[Values] Flex EGs") +{ + Synth synth; + std::vector messageList; + Client client(&messageList); + client.setReceiveCallback(&simpleMessageReceiver); + + synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + sample=kick.wav eg1_time1=0.1 eg1_level1=0.5 eg1_time2=0.4 eg1_level2=2 eg2_time1=4 eg2_level1=0.1 + )"); + synth.dispatchMessage(client, 0, "/region0/eg0/point0/time", "", nullptr); + synth.dispatchMessage(client, 0, "/region0/eg0/point0/level", "", nullptr); + synth.dispatchMessage(client, 0, "/region0/eg0/point1/time", "", nullptr); + synth.dispatchMessage(client, 0, "/region0/eg0/point1/level", "", nullptr); + synth.dispatchMessage(client, 0, "/region0/eg1/point0/time", "", nullptr); + synth.dispatchMessage(client, 0, "/region0/eg1/point0/level", "", nullptr); + std::vector expected { + "/region0/eg0/point0/time,f : { 0.1 }", + "/region0/eg0/point0/level,f : { 0.5 }", + "/region0/eg0/point1/time,f : { 0.4 }", + "/region0/eg0/point1/level,f : { 2 }", + "/region0/eg1/point0/time,f : { 4 }", + "/region0/eg1/point0/level,f : { 0.1 }", + }; + REQUIRE(messageList == expected); +} + +TEST_CASE("[Values] Flex EGs CC") +{ + Synth synth; + std::vector messageList; + Client client(&messageList); + client.setReceiveCallback(&simpleMessageReceiver); + + synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + sample=kick.wav eg1_time1_cc2=0.1 eg1_level1_oncc3=0.5 + )"); + synth.dispatchMessage(client, 0, "/region0/eg0/point0/time_cc2", "", nullptr); + synth.dispatchMessage(client, 0, "/region0/eg0/point0/time_cc4", "", nullptr); + synth.dispatchMessage(client, 0, "/region0/eg0/point0/level_cc3", "", nullptr); + synth.dispatchMessage(client, 0, "/region0/eg0/point0/level_cc12", "", nullptr); + std::vector expected { + "/region0/eg0/point0/time_cc2,f : { 0.1 }", + "/region0/eg0/point0/time_cc4,f : { 0 }", + "/region0/eg0/point0/level_cc3,f : { 0.5 }", + "/region0/eg0/point0/level_cc12,f : { 0 }", + }; + REQUIRE(messageList == expected); +} From 8563a1735f9b32a3f160311c60cd404160e56337 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 23 Jun 2021 13:00:34 +0200 Subject: [PATCH 108/193] Merged implementations with dynamic updates --- src/sfizz/FlexEnvelope.cpp | 34 +++++++++++-------- src/sfizz/FlexEnvelope.h | 1 + src/sfizz/Synth.cpp | 2 +- .../modulations/sources/FlexEnvelope.cpp | 5 +-- src/sfizz/modulations/sources/FlexEnvelope.h | 4 ++- 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/sfizz/FlexEnvelope.cpp b/src/sfizz/FlexEnvelope.cpp index ae06c022e..2ae0e04a5 100644 --- a/src/sfizz/FlexEnvelope.cpp +++ b/src/sfizz/FlexEnvelope.cpp @@ -57,6 +57,7 @@ struct FlexEnvelope::Impl { void process(absl::Span out); bool advanceToStage(unsigned stageNumber); bool advanceToNextStage(); + void updateCurrentTimeAndLevel(); }; FlexEnvelope::FlexEnvelope(Resources &resources) @@ -97,18 +98,8 @@ void FlexEnvelope::start(unsigned triggerDelay) const FlexEGDescription& desc = *impl.desc_; impl.delayFramesLeft_ = triggerDelay; - - FlexEGPoint point; - if (!desc.points.empty()) - point = desc.points[0]; - - // - impl.stageSourceLevel_ = 0.0; - impl.stageTargetLevel_ = point.level; - impl.stageTime_ = point.time; - impl.stageSustained_ = desc.sustain == 0; - impl.stageCurve_ = &point.curve(); impl.currentFramesUntilRelease_ = absl::nullopt; + impl.advanceToStage(0); } void FlexEnvelope::setFreeRunning(bool freeRunning) @@ -172,6 +163,10 @@ void FlexEnvelope::Impl::process(absl::Span out) const FlexEGDescription& desc = *desc_; size_t numFrames = out.size(); const float samplePeriod = samplePeriod_; + + if (desc.dynamic) + updateCurrentTimeAndLevel(); + // Skip the initial delay, for frame-accurate trigger size_t skipFrames = std::min(numFrames, delayFramesLeft_); if (skipFrames > 0) { @@ -266,12 +261,11 @@ bool FlexEnvelope::Impl::advanceToStage(unsigned stageNumber) const FlexEGPoint& point = desc.points[stageNumber]; stageSourceLevel_ = currentLevel_; - stageTargetLevel_ = point.getLevel(midiState); - stageTime_ = point.getTime(midiState); + currentTime_ = 0.0f; + updateCurrentTimeAndLevel(); stageSustained_ = int(stageNumber) == desc.sustain; stageCurve_ = &point.curve(); - currentTime_ = 0; return true; }; @@ -280,4 +274,16 @@ bool FlexEnvelope::Impl::advanceToNextStage() return advanceToStage(currentStageNumber_ + 1); } +void FlexEnvelope::Impl::updateCurrentTimeAndLevel() +{ + const FlexEGDescription& desc = *desc_; + if (currentStageNumber_ >= desc.points.size()) + return; + + const FlexEGPoint& point = desc.points[currentStageNumber_]; + const MidiState& midiState = resources_->getMidiState(); + stageTargetLevel_ = point.getLevel(midiState); + stageTime_ = point.getTime(midiState); +} + } // namespace sfz diff --git a/src/sfizz/FlexEnvelope.h b/src/sfizz/FlexEnvelope.h index a72d280f1..c33bf7517 100644 --- a/src/sfizz/FlexEnvelope.h +++ b/src/sfizz/FlexEnvelope.h @@ -5,6 +5,7 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #pragma once +#include "MidiState.h" #include #include diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 1e200b58d..46b9f52df 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -70,7 +70,7 @@ Synth::Impl::Impl() MidiState& midiState = resources_.getMidiState(); genController_.reset(new ControllerSource(resources_, voiceManager_)); genLFO_.reset(new LFOSource(voiceManager_)); - genFlexEnvelope_.reset(new FlexEnvelopeSource(voiceManager_)); + genFlexEnvelope_.reset(new FlexEnvelopeSource(voiceManager_, midiState)); genADSREnvelope_.reset(new ADSREnvelopeSource(voiceManager_, midiState)); genChannelAftertouch_.reset(new ChannelAftertouchSource(voiceManager_, midiState)); genPolyAftertouch_.reset(new PolyAftertouchSource(voiceManager_, midiState)); diff --git a/src/sfizz/modulations/sources/FlexEnvelope.cpp b/src/sfizz/modulations/sources/FlexEnvelope.cpp index b3e85cfc2..1a2698ad4 100644 --- a/src/sfizz/modulations/sources/FlexEnvelope.cpp +++ b/src/sfizz/modulations/sources/FlexEnvelope.cpp @@ -14,9 +14,10 @@ namespace sfz { -FlexEnvelopeSource::FlexEnvelopeSource(VoiceManager& manager) - : voiceManager_(manager) +FlexEnvelopeSource::FlexEnvelopeSource(VoiceManager& manager, MidiState& midiState) + : voiceManager_(manager), midiState_(midiState) { + } void FlexEnvelopeSource::init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) diff --git a/src/sfizz/modulations/sources/FlexEnvelope.h b/src/sfizz/modulations/sources/FlexEnvelope.h index 81b7a2f78..60cd5b14e 100644 --- a/src/sfizz/modulations/sources/FlexEnvelope.h +++ b/src/sfizz/modulations/sources/FlexEnvelope.h @@ -7,13 +7,14 @@ #pragma once #include "../ModGenerator.h" #include "../../VoiceManager.h" +#include "../../MidiState.h" namespace sfz { class Synth; class FlexEnvelopeSource : public ModGenerator { public: - explicit FlexEnvelopeSource(VoiceManager& manager); + explicit FlexEnvelopeSource(VoiceManager& manager, MidiState& midiState); void init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void cancelRelease(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; @@ -21,6 +22,7 @@ class FlexEnvelopeSource : public ModGenerator { private: VoiceManager& voiceManager_; + MidiState& midiState_; }; } // namespace sfz From cbff8666c14527fc162368f2f7eac6df3be9e781 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 26 Jun 2021 23:03:29 +0200 Subject: [PATCH 109/193] Remove useless midistate injection --- src/sfizz/FlexEnvelope.h | 1 - src/sfizz/Synth.cpp | 2 +- src/sfizz/modulations/sources/FlexEnvelope.cpp | 4 ++-- src/sfizz/modulations/sources/FlexEnvelope.h | 4 +--- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/sfizz/FlexEnvelope.h b/src/sfizz/FlexEnvelope.h index c33bf7517..a72d280f1 100644 --- a/src/sfizz/FlexEnvelope.h +++ b/src/sfizz/FlexEnvelope.h @@ -5,7 +5,6 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #pragma once -#include "MidiState.h" #include #include diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 46b9f52df..1e200b58d 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -70,7 +70,7 @@ Synth::Impl::Impl() MidiState& midiState = resources_.getMidiState(); genController_.reset(new ControllerSource(resources_, voiceManager_)); genLFO_.reset(new LFOSource(voiceManager_)); - genFlexEnvelope_.reset(new FlexEnvelopeSource(voiceManager_, midiState)); + genFlexEnvelope_.reset(new FlexEnvelopeSource(voiceManager_)); genADSREnvelope_.reset(new ADSREnvelopeSource(voiceManager_, midiState)); genChannelAftertouch_.reset(new ChannelAftertouchSource(voiceManager_, midiState)); genPolyAftertouch_.reset(new PolyAftertouchSource(voiceManager_, midiState)); diff --git a/src/sfizz/modulations/sources/FlexEnvelope.cpp b/src/sfizz/modulations/sources/FlexEnvelope.cpp index 1a2698ad4..347886aaa 100644 --- a/src/sfizz/modulations/sources/FlexEnvelope.cpp +++ b/src/sfizz/modulations/sources/FlexEnvelope.cpp @@ -14,8 +14,8 @@ namespace sfz { -FlexEnvelopeSource::FlexEnvelopeSource(VoiceManager& manager, MidiState& midiState) - : voiceManager_(manager), midiState_(midiState) +FlexEnvelopeSource::FlexEnvelopeSource(VoiceManager& manager) + : voiceManager_(manager) { } diff --git a/src/sfizz/modulations/sources/FlexEnvelope.h b/src/sfizz/modulations/sources/FlexEnvelope.h index 60cd5b14e..81b7a2f78 100644 --- a/src/sfizz/modulations/sources/FlexEnvelope.h +++ b/src/sfizz/modulations/sources/FlexEnvelope.h @@ -7,14 +7,13 @@ #pragma once #include "../ModGenerator.h" #include "../../VoiceManager.h" -#include "../../MidiState.h" namespace sfz { class Synth; class FlexEnvelopeSource : public ModGenerator { public: - explicit FlexEnvelopeSource(VoiceManager& manager, MidiState& midiState); + explicit FlexEnvelopeSource(VoiceManager& manager); void init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void cancelRelease(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; @@ -22,7 +21,6 @@ class FlexEnvelopeSource : public ModGenerator { private: VoiceManager& voiceManager_; - MidiState& midiState_; }; } // namespace sfz From 83be87c449b040ae1adb8400217fe855c97ecb9b Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 27 Jun 2021 11:22:03 +0200 Subject: [PATCH 110/193] Removed the dynamic update --- src/sfizz/FlexEnvelope.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sfizz/FlexEnvelope.cpp b/src/sfizz/FlexEnvelope.cpp index 2ae0e04a5..3cbd41617 100644 --- a/src/sfizz/FlexEnvelope.cpp +++ b/src/sfizz/FlexEnvelope.cpp @@ -164,9 +164,6 @@ void FlexEnvelope::Impl::process(absl::Span out) size_t numFrames = out.size(); const float samplePeriod = samplePeriod_; - if (desc.dynamic) - updateCurrentTimeAndLevel(); - // Skip the initial delay, for frame-accurate trigger size_t skipFrames = std::min(numFrames, delayFramesLeft_); if (skipFrames > 0) { From e79e5cf04d0a59a72c2899f20431e3e0b8ad754b Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 27 Jun 2021 12:13:44 +0200 Subject: [PATCH 111/193] Flex envelope dynamic update --- src/sfizz/Config.h | 3 ++- src/sfizz/FilePool.cpp | 2 +- src/sfizz/FlexEGDescription.cpp | 8 ++++---- src/sfizz/FlexEGDescription.h | 4 ++-- src/sfizz/FlexEnvelope.cpp | 22 +++++++++++++++++----- src/sfizz/MidiState.cpp | 11 +++++++++++ src/sfizz/MidiState.h | 11 ++++++++++- src/sfizz/Oversampler.h | 2 +- 8 files changed, 48 insertions(+), 15 deletions(-) diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index 3631e6c7f..a458a8376 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -70,7 +70,8 @@ namespace config { constexpr float powerFollowerReleaseTime { 200e-3f }; constexpr uint16_t numCCs { 512 }; constexpr int maxCurves { 256 }; - constexpr int chunkSize { 1024 }; + constexpr int fileChunkSize { 1024 }; + constexpr int processChunkSize { 16 }; constexpr unsigned int defaultAlignment { 16 }; constexpr int filtersInPool { maxVoices * 2 }; constexpr int excessFileFrames { 64 }; diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index 6bd4e35ae..cce2941f9 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -101,7 +101,7 @@ void streamFromFile(sfz::AudioReader& reader, sfz::FileAudioBuffer& output, std: { const auto numFrames = static_cast(reader.frames()); const auto numChannels = reader.channels(); - const auto chunkSize = static_cast(sfz::config::chunkSize); + const auto chunkSize = static_cast(sfz::config::fileChunkSize); output.reset(); output.addChannels(reader.channels()); diff --git a/src/sfizz/FlexEGDescription.cpp b/src/sfizz/FlexEGDescription.cpp index f92e20eb6..98d99d219 100644 --- a/src/sfizz/FlexEGDescription.cpp +++ b/src/sfizz/FlexEGDescription.cpp @@ -86,19 +86,19 @@ void FlexEGs::clearUnusedCurves() } /// -float FlexEGPoint::getTime(const MidiState& state) const noexcept +float FlexEGPoint::getTime(const MidiState& state, int delay) const noexcept { float returnedValue { time }; for (const CCData& mod : ccTime) - returnedValue += state.getCCValue(mod.cc) * mod.data; + returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data; return returnedValue; } -float FlexEGPoint::getLevel(const MidiState& state) const noexcept +float FlexEGPoint::getLevel(const MidiState& state, int delay) const noexcept { float returnedValue { level }; for (const CCData& mod : ccLevel) - returnedValue += state.getCCValue(mod.cc) * mod.data; + returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data; return returnedValue; } diff --git a/src/sfizz/FlexEGDescription.h b/src/sfizz/FlexEGDescription.h index 4c816befb..8965746f4 100644 --- a/src/sfizz/FlexEGDescription.h +++ b/src/sfizz/FlexEGDescription.h @@ -26,8 +26,8 @@ struct FlexEGPoint { CCMap ccTime; CCMap ccLevel; - float getTime(const MidiState& state) const noexcept; - float getLevel(const MidiState& state) const noexcept; + float getTime(const MidiState& state, int delay = 0) const noexcept; + float getLevel(const MidiState& state, int delay = 0) const noexcept; void setShape(float shape); float shape() const noexcept { return shape_; } diff --git a/src/sfizz/FlexEnvelope.cpp b/src/sfizz/FlexEnvelope.cpp index 3cbd41617..2d75dd656 100644 --- a/src/sfizz/FlexEnvelope.cpp +++ b/src/sfizz/FlexEnvelope.cpp @@ -57,7 +57,7 @@ struct FlexEnvelope::Impl { void process(absl::Span out); bool advanceToStage(unsigned stageNumber); bool advanceToNextStage(); - void updateCurrentTimeAndLevel(); + void updateCurrentTimeAndLevel(int delay = 0); }; FlexEnvelope::FlexEnvelope(Resources &resources) @@ -155,7 +155,19 @@ bool FlexEnvelope::isFinished() const noexcept void FlexEnvelope::process(absl::Span out) { Impl& impl = *impl_; - impl.process(out); + if (impl.desc_->dynamic) { + int processed = 0; + int remaining = static_cast(out.size()); + while(remaining > 0) { + impl.updateCurrentTimeAndLevel(processed); + int chunkSize = min(config::processChunkSize, remaining); + impl.process(out.subspan(processed, chunkSize)); + processed += chunkSize; + remaining -= chunkSize; + } + } else { + impl.process(out); + } } void FlexEnvelope::Impl::process(absl::Span out) @@ -271,7 +283,7 @@ bool FlexEnvelope::Impl::advanceToNextStage() return advanceToStage(currentStageNumber_ + 1); } -void FlexEnvelope::Impl::updateCurrentTimeAndLevel() +void FlexEnvelope::Impl::updateCurrentTimeAndLevel(int delay) { const FlexEGDescription& desc = *desc_; if (currentStageNumber_ >= desc.points.size()) @@ -279,8 +291,8 @@ void FlexEnvelope::Impl::updateCurrentTimeAndLevel() const FlexEGPoint& point = desc.points[currentStageNumber_]; const MidiState& midiState = resources_->getMidiState(); - stageTargetLevel_ = point.getLevel(midiState); - stageTime_ = point.getTime(midiState); + stageTargetLevel_ = point.getLevel(midiState, delay); + stageTime_ = point.getTime(midiState, delay); } } // namespace sfz diff --git a/src/sfizz/MidiState.cpp b/src/sfizz/MidiState.cpp index 4abaffb83..4d0d45ebe 100644 --- a/src/sfizz/MidiState.cpp +++ b/src/sfizz/MidiState.cpp @@ -202,6 +202,17 @@ float sfz::MidiState::getCCValue(int ccNumber) const noexcept return ccEvents[ccNumber].back().value; } +float sfz::MidiState::getCCValueAt(int ccNumber, int delay) const noexcept +{ + ASSERT(ccNumber >= 0 && ccNumber < config::numCCs); + const auto ccEvent = absl::c_lower_bound( + ccEvents[ccNumber], delay, MidiEventDelayComparator {}); + if (ccEvent != ccEvents[ccNumber].end()) + return ccEvent->value; + else + return ccEvents[ccNumber].back().value; +} + void sfz::MidiState::reset() noexcept { for (auto& velocity: lastNoteVelocities) diff --git a/src/sfizz/MidiState.h b/src/sfizz/MidiState.h index 16197c2fd..eaf947c60 100644 --- a/src/sfizz/MidiState.h +++ b/src/sfizz/MidiState.h @@ -167,13 +167,22 @@ class MidiState bool isNotePressed(int noteNumber) const noexcept { return noteStates[noteNumber]; } /** - * @brief Get the CC value for CC number + * @brief Get the last CC value for CC number * * @param ccNumber * @return float */ float getCCValue(int ccNumber) const noexcept; + /** + * @brief Get the CC value for CC number + * + * @param ccNumber + * @param delay + * @return float + */ + float getCCValueAt(int ccNumber, int delay) const noexcept; + /** * @brief Reset the midi state (does not impact the last note on time) * diff --git a/src/sfizz/Oversampler.h b/src/sfizz/Oversampler.h index e109384c2..745a44790 100644 --- a/src/sfizz/Oversampler.h +++ b/src/sfizz/Oversampler.h @@ -39,7 +39,7 @@ class Oversampler * @param factor * @param chunkSize */ - Oversampler(Oversampling factor = Oversampling::x1, size_t chunkSize = config::chunkSize); + Oversampler(Oversampling factor = Oversampling::x1, size_t chunkSize = config::fileChunkSize); /** * @brief Stream the oversampling of an input AudioBuffer into an output * one, possibly signaling the caller along the way of the number of From d2ae2a0fe0713669bb9648987af9f09ed66f723a Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 27 Jun 2021 16:51:29 +0200 Subject: [PATCH 112/193] Add dynamic to the basic envelope --- src/sfizz/ADSREnvelope.cpp | 44 ++++++++++++----- src/sfizz/ADSREnvelope.h | 13 +++-- src/sfizz/Defaults.cpp | 1 + src/sfizz/Defaults.h | 1 + src/sfizz/EGDescription.h | 47 +++++++------------ src/sfizz/Region.cpp | 4 ++ src/sfizz/Synth.cpp | 2 +- src/sfizz/SynthMessaging.cpp | 27 +++++++++++ src/sfizz/Voice.cpp | 6 +-- .../modulations/sources/ADSREnvelope.cpp | 6 +-- src/sfizz/modulations/sources/ADSREnvelope.h | 4 +- tests/EGDescriptionT.cpp | 2 +- tests/PolyphonyT.cpp | 4 +- tests/RegionValuesT.cpp | 28 +++++++++++ 14 files changed, 130 insertions(+), 59 deletions(-) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index 873069ce4..cda4ba312 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -38,20 +38,13 @@ Float ADSREnvelope::secondsToExpRate(Float timeInSeconds) const noexcept return std::exp(Float(-9.0) / (timeInSeconds * sampleRate)); }; -void ADSREnvelope::reset(const EGDescription& desc, const Region& region, const MidiState& state, int delay, float velocity, float sampleRate) noexcept +void ADSREnvelope::reset(const EGDescription& desc, const Region& region, int delay, float velocity, float sampleRate) noexcept { this->sampleRate = sampleRate; - - this->delay = delay + secondsToSamples(desc.getDelay(state, velocity)); - this->attackStep = secondsToLinRate(desc.getAttack(state, velocity)); - this->decayRate = secondsToExpRate(desc.getDecay(state, velocity)); - this->releaseRate = secondsToExpRate(desc.getRelease(state, velocity)); - this->hold = secondsToSamples(desc.getHold(state, velocity)); - this->sustain = clamp(desc.getSustain(state, velocity), 0.0f, 1.0f); - this->start = clamp(desc.getStart(state, velocity), 0.0f, 1.0f); - + desc_ = &desc; + triggerVelocity_ = velocity; + updateValues(delay); releaseDelay = 0; - sustainThreshold = this->sustain + config::virtuallyZero; shouldRelease = false; freeRunning = ( (this->sustain <= Float(config::sustainFreeRunningThreshold)) @@ -61,7 +54,36 @@ void ADSREnvelope::reset(const EGDescription& desc, const Region& region, const currentState = State::Delay; } +void ADSREnvelope::updateValues(int delay) noexcept +{ + this->delay = delay + secondsToSamples(desc_->getDelay(midiState_, triggerVelocity_, delay)); + this->attackStep = secondsToLinRate(desc_->getAttack(midiState_, triggerVelocity_, delay)); + this->decayRate = secondsToExpRate(desc_->getDecay(midiState_, triggerVelocity_, delay)); + this->releaseRate = secondsToExpRate(desc_->getRelease(midiState_, triggerVelocity_, delay)); + this->hold = secondsToSamples(desc_->getHold(midiState_, triggerVelocity_, delay)); + this->sustain = clamp(desc_->getSustain(midiState_, triggerVelocity_, delay), 0.0f, 1.0f); + this->start = clamp(desc_->getStart(midiState_, triggerVelocity_, delay), 0.0f, 1.0f); + sustainThreshold = this->sustain + config::virtuallyZero; +} + void ADSREnvelope::getBlock(absl::Span output) noexcept +{ + if (desc_ && desc_->dynamic) { + int processed = 0; + int remaining = static_cast(output.size()); + while(remaining > 0) { + updateValues(processed); + int chunkSize = min(config::processChunkSize, remaining); + getBlockInternal(output.subspan(processed, chunkSize)); + processed += chunkSize; + remaining -= chunkSize; + } + } else { + getBlockInternal(output); + } +} + +void ADSREnvelope::getBlockInternal(absl::Span output) noexcept { State currentState = this->currentState; Float currentValue = this->currentValue; diff --git a/src/sfizz/ADSREnvelope.h b/src/sfizz/ADSREnvelope.h index ace1da7ba..a970a1d42 100644 --- a/src/sfizz/ADSREnvelope.h +++ b/src/sfizz/ADSREnvelope.h @@ -18,7 +18,8 @@ class ADSREnvelope { public: using Float = float; - ADSREnvelope() = default; + ADSREnvelope(const MidiState& state) + : midiState_(state) {} /** * @brief Resets the ADSR envelope given a Region, the current midi state, and a delay and * trigger velocity @@ -29,10 +30,9 @@ class ADSREnvelope { * @param delay * @param velocity */ - void reset(const EGDescription& desc, const Region& region, const MidiState& state, int delay, float velocity, float sampleRate) noexcept; + void reset(const EGDescription& desc, const Region& region, int delay, float velocity, float sampleRate) noexcept; /** - * @brief Get a block of values for the envelope. This method tries hard to be efficient - * and hopefully it is. + * @brief Get the next block of values for the envelope. * * @param output */ @@ -81,6 +81,8 @@ class ADSREnvelope { int secondsToSamples(Float timeInSeconds) const noexcept; Float secondsToLinRate(Float timeInSeconds) const noexcept; Float secondsToExpRate(Float timeInSeconds) const noexcept; + void updateValues(int delay = 0) noexcept; + void getBlockInternal(absl::Span output) noexcept; enum class State { Delay, @@ -94,6 +96,9 @@ class ADSREnvelope { }; State currentState { State::Done }; Float currentValue { 0.0 }; + const EGDescription* desc_ { nullptr }; + const MidiState& midiState_; + float triggerVelocity_ { 0.0f }; int delay { 0 }; Float attackStep { 0 }; Float decayRate { 0 }; diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index 40ce37c7f..18844897c 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -148,6 +148,7 @@ FloatSpec egPercent { 0.0f, {0.0f, 100.0f}, kNormalizePercent|kPermissiveBounds FloatSpec egPercentMod { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec egDepth { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds }; FloatSpec egVel2Depth { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds }; +BoolSpec egDynamic { 0, {0, 1}, kEnforceBounds }; BoolSpec flexEGAmpeg { false, {0, 1}, kEnforceBounds }; BoolSpec flexEGDynamic { 0, {0, 1}, kEnforceBounds }; Int32Spec flexEGSustain { 0, {0, 100}, kEnforceLowerBound|kPermissiveUpperBound }; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 7d6c54fa9..db73157a5 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -260,6 +260,7 @@ namespace Default extern const OpcodeSpec egPercentMod; extern const OpcodeSpec egDepth; extern const OpcodeSpec egVel2Depth; + extern const OpcodeSpec egDynamic; extern const OpcodeSpec flexEGAmpeg; extern const OpcodeSpec flexEGDynamic; extern const OpcodeSpec flexEGSustain; diff --git a/src/sfizz/EGDescription.h b/src/sfizz/EGDescription.h index 26202ff14..fce5fc757 100644 --- a/src/sfizz/EGDescription.h +++ b/src/sfizz/EGDescription.h @@ -42,22 +42,6 @@ namespace sfz { * */ -/** - * @brief If a cc switch exists for the value, returns the value with the CC modifier, otherwise returns the value alone. - * - * @param ccValues - * @param ccSwitch - * @param value - * @return float - */ -inline float ccSwitchedValue(const MidiState& state, const absl::optional>& ccSwitch, float value) noexcept -{ - if (ccSwitch) - return value + ccSwitch->data * state.getCCValue(ccSwitch->cc); - else - return value; -} - struct EGDescription { EGDescription() = default; EGDescription(const EGDescription&) = default; @@ -89,6 +73,7 @@ struct EGDescription { CCMap ccRelease; CCMap ccStart; CCMap ccSustain; + bool dynamic { false }; /** * @brief Get the attack with possibly a CC modifier and a velocity modifier @@ -97,12 +82,12 @@ struct EGDescription { * @param velocity * @return float */ - float getAttack(const MidiState& state, float velocity) const noexcept + float getAttack(const MidiState& state, float velocity, int delay = 0) const noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); float returnedValue { attack + velocity * vel2attack }; for (auto& mod: ccAttack) { - returnedValue += state.getCCValue(mod.cc) * mod.data; + returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data; } return returnedValue; } @@ -113,12 +98,12 @@ struct EGDescription { * @param velocity * @return float */ - float getDecay(const MidiState& state, float velocity) const noexcept + float getDecay(const MidiState& state, float velocity, int delay = 0) const noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); float returnedValue { decay + velocity * vel2decay }; for (auto& mod: ccDecay) { - returnedValue += state.getCCValue(mod.cc) * mod.data; + returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data; } return returnedValue; } @@ -129,12 +114,12 @@ struct EGDescription { * @param velocity * @return float */ - float getDelay(const MidiState& state, float velocity) const noexcept + float getDelay(const MidiState& state, float velocity, int delay = 0) const noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); - float returnedValue { delay + velocity * vel2delay }; + float returnedValue { this->delay + velocity * vel2delay }; for (auto& mod: ccDelay) { - returnedValue += state.getCCValue(mod.cc) * mod.data; + returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data; } return returnedValue; } @@ -145,12 +130,12 @@ struct EGDescription { * @param velocity * @return float */ - float getHold(const MidiState& state, float velocity) const noexcept + float getHold(const MidiState& state, float velocity, int delay = 0) const noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); float returnedValue { hold + velocity * vel2hold }; for (auto& mod: ccHold) { - returnedValue += state.getCCValue(mod.cc) * mod.data; + returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data; } return returnedValue; } @@ -161,12 +146,12 @@ struct EGDescription { * @param velocity * @return float */ - float getRelease(const MidiState& state, float velocity) const noexcept + float getRelease(const MidiState& state, float velocity, int delay = 0) const noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); float returnedValue { release + velocity * vel2release }; for (auto& mod: ccRelease) { - returnedValue += state.getCCValue(mod.cc) * mod.data; + returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data; } return returnedValue; } @@ -177,12 +162,12 @@ struct EGDescription { * @param velocity * @return float */ - float getStart(const MidiState& state, float velocity) const noexcept + float getStart(const MidiState& state, float velocity, int delay = 0) const noexcept { UNUSED(velocity); float returnedValue { start }; for (auto& mod: ccStart) { - returnedValue += state.getCCValue(mod.cc) * mod.data; + returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data; } return returnedValue; } @@ -193,12 +178,12 @@ struct EGDescription { * @param velocity * @return float */ - float getSustain(const MidiState& state, float velocity) const noexcept + float getSustain(const MidiState& state, float velocity, int delay = 0) const noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); float returnedValue { sustain + velocity * vel2sustain }; for (auto& mod: ccSustain) { - returnedValue += state.getCCValue(mod.cc) * mod.data; + returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data; } return returnedValue; } diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 344d7c6a4..de7661a12 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -1091,6 +1091,10 @@ bool sfz::Region::parseEGOpcode(const Opcode& opcode, EGDescription& eg) break; + case_any_eg("dynamic"): + eg.dynamic = opcode.read(Default::egDynamic); + break; + case hash("pitcheg_depth"): getOrCreateConnection( ModKey::createNXYZ(ModId::PitchEG, id), diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 1e200b58d..0bf23838d 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -71,7 +71,7 @@ Synth::Impl::Impl() genController_.reset(new ControllerSource(resources_, voiceManager_)); genLFO_.reset(new LFOSource(voiceManager_)); genFlexEnvelope_.reset(new FlexEnvelopeSource(voiceManager_)); - genADSREnvelope_.reset(new ADSREnvelopeSource(voiceManager_, midiState)); + genADSREnvelope_.reset(new ADSREnvelopeSource(voiceManager_)); genChannelAftertouch_.reset(new ChannelAftertouchSource(voiceManager_, midiState)); genPolyAftertouch_.reset(new PolyAftertouchSource(voiceManager_, midiState)); } diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index 3e6a87f2b..a9d97ed9c 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -1085,6 +1085,33 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co client.receive<'f'>(delay, path, region.amplitudeEG.vel2depth); } break; + MATCH("/region&/ampeg_dynamic", "") { + GET_REGION_OR_BREAK(indices[0]) + if (region.amplitudeEG.dynamic) { + client.receive<'T'>(delay, path, {}); + } else { + client.receive<'F'>(delay, path, {}); + } + } break; + + MATCH("/region&/fileg_dynamic", "") { + GET_REGION_OR_BREAK(indices[0]) + if (region.filterEG && region.filterEG->dynamic) { + client.receive<'T'>(delay, path, {}); + } else { + client.receive<'F'>(delay, path, {}); + } + } break; + + MATCH("/region&/pitcheg_dynamic", "") { + GET_REGION_OR_BREAK(indices[0]) + if (region.pitchEG && region.pitchEG->dynamic) { + client.receive<'T'>(delay, path, {}); + } else { + client.receive<'F'>(delay, path, {}); + } + } break; + MATCH("/region&/note_polyphony", "") { GET_REGION_OR_BREAK(indices[0]) if (region.notePolyphony) { diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 14f148817..7be76b3b9 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -271,7 +271,7 @@ struct Voice::Impl std::unique_ptr lfoPitch_; std::unique_ptr lfoFilter_; - ADSREnvelope egAmplitude_; + ADSREnvelope egAmplitude_ { resources_.getMidiState() }; std::unique_ptr egPitch_; std::unique_ptr egFilter_; @@ -1832,7 +1832,7 @@ void Voice::setPitchEGEnabledPerVoice(bool havePitchEG) { Impl& impl = *impl_; if (havePitchEG) - impl.egPitch_.reset(new ADSREnvelope); + impl.egPitch_.reset(new ADSREnvelope(impl.resources_.getMidiState())); else impl.egPitch_.reset(); } @@ -1841,7 +1841,7 @@ void Voice::setFilterEGEnabledPerVoice(bool haveFilterEG) { Impl& impl = *impl_; if (haveFilterEG) - impl.egFilter_.reset(new ADSREnvelope); + impl.egFilter_.reset(new ADSREnvelope(impl.resources_.getMidiState())); else impl.egFilter_.reset(); } diff --git a/src/sfizz/modulations/sources/ADSREnvelope.cpp b/src/sfizz/modulations/sources/ADSREnvelope.cpp index 0ec5af8ed..703b5d433 100644 --- a/src/sfizz/modulations/sources/ADSREnvelope.cpp +++ b/src/sfizz/modulations/sources/ADSREnvelope.cpp @@ -13,8 +13,8 @@ namespace sfz { -ADSREnvelopeSource::ADSREnvelopeSource(VoiceManager& manager, MidiState& state) - : voiceManager_(manager), midiState_(state) +ADSREnvelopeSource::ADSREnvelopeSource(VoiceManager& manager) + : voiceManager_(manager) { } @@ -79,7 +79,7 @@ void ADSREnvelopeSource::init(const ModKey& sourceKey, NumericId voiceId, const TriggerEvent& triggerEvent = voice->getTriggerEvent(); const float sampleRate = voice->getSampleRate(); - eg->reset(*desc, *region, midiState_, delay, triggerEvent.value, sampleRate); + eg->reset(*desc, *region, delay, triggerEvent.value, sampleRate); } void ADSREnvelopeSource::release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) diff --git a/src/sfizz/modulations/sources/ADSREnvelope.h b/src/sfizz/modulations/sources/ADSREnvelope.h index 436127a76..f43722b91 100644 --- a/src/sfizz/modulations/sources/ADSREnvelope.h +++ b/src/sfizz/modulations/sources/ADSREnvelope.h @@ -7,14 +7,13 @@ #pragma once #include "../ModGenerator.h" #include "../../VoiceManager.h" -#include "../../MidiState.h" namespace sfz { class Synth; class ADSREnvelopeSource : public ModGenerator { public: - explicit ADSREnvelopeSource(VoiceManager &manager, MidiState& state); + explicit ADSREnvelopeSource(VoiceManager &manager); void init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void cancelRelease(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; @@ -22,7 +21,6 @@ class ADSREnvelopeSource : public ModGenerator { private: VoiceManager& voiceManager_; - MidiState& midiState_; }; } // namespace sfz diff --git a/tests/EGDescriptionT.cpp b/tests/EGDescriptionT.cpp index ba0902e21..5813ce972 100644 --- a/tests/EGDescriptionT.cpp +++ b/tests/EGDescriptionT.cpp @@ -45,7 +45,7 @@ TEST_CASE("[EGDescription] Delay range") //REQUIRE(eg.getDelay(state, 127_norm) == 0.0f); state.ccEvent(0, 63, 127_norm); REQUIRE(eg.getDelay(state, 127_norm) == 1.0f); - REQUIRE(eg.getDelay(state, 0_norm) == 2.27f); + REQUIRE(eg.getDelay(state, 0_norm, 1) == 2.27f); //eg.ccDelay[63] = 127.0f; //REQUIRE(eg.getDelay(state, 0_norm) == 100.0f); eg.ccDelay[63] = 1.27f; diff --git a/tests/PolyphonyT.cpp b/tests/PolyphonyT.cpp index 0a966da9c..3034372ac 100644 --- a/tests/PolyphonyT.cpp +++ b/tests/PolyphonyT.cpp @@ -222,8 +222,8 @@ TEST_CASE("[Polyphony] Self-masking") synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( sample=*sine key=64 note_polyphony=2 )"); - synth.noteOn(0, 64, 63 ); - synth.noteOn(1, 64, 62 ); + synth.noteOn(0, 64, 63); + synth.noteOn(1, 64, 62); synth.noteOn(2, 64, 64); synth.renderBlock(buffer); REQUIRE( synth.getNumActiveVoices() == 3 ); // One of these is releasing diff --git a/tests/RegionValuesT.cpp b/tests/RegionValuesT.cpp index 970a259ed..df7e64d6b 100644 --- a/tests/RegionValuesT.cpp +++ b/tests/RegionValuesT.cpp @@ -3301,3 +3301,31 @@ TEST_CASE("[Values] Flex EGs CC") }; REQUIRE(messageList == expected); } + +TEST_CASE("[Values] Dynamic EGs") +{ + Synth synth; + std::vector messageList; + Client client(&messageList); + client.setReceiveCallback(&simpleMessageReceiver); + + synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + sample=kick.wav + sample=kick.wav ampeg_dynamic=1 pitcheg_dynamic=1 fileg_dynamic=1 + )"); + synth.dispatchMessage(client, 0, "/region0/ampeg_dynamic", "", nullptr); + synth.dispatchMessage(client, 0, "/region0/pitcheg_dynamic", "", nullptr); + synth.dispatchMessage(client, 0, "/region0/fileg_dynamic", "", nullptr); + synth.dispatchMessage(client, 0, "/region1/ampeg_dynamic", "", nullptr); + synth.dispatchMessage(client, 0, "/region1/pitcheg_dynamic", "", nullptr); + synth.dispatchMessage(client, 0, "/region1/fileg_dynamic", "", nullptr); + std::vector expected { + "/region0/ampeg_dynamic,F : { }", + "/region0/pitcheg_dynamic,F : { }", + "/region0/fileg_dynamic,F : { }", + "/region1/ampeg_dynamic,T : { }", + "/region1/pitcheg_dynamic,T : { }", + "/region1/fileg_dynamic,T : { }", + }; + REQUIRE(messageList == expected); +} From 788a4301a10e2640dfed6b6d416dceabc768163a Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 27 Jun 2021 19:35:20 +0200 Subject: [PATCH 113/193] Don't update the delay after the phase has passed --- src/sfizz/ADSREnvelope.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index cda4ba312..6167b6896 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -43,6 +43,7 @@ void ADSREnvelope::reset(const EGDescription& desc, const Region& region, int de this->sampleRate = sampleRate; desc_ = &desc; triggerVelocity_ = velocity; + currentState = State::Delay; // Has to be before the update updateValues(delay); releaseDelay = 0; shouldRelease = false; @@ -51,12 +52,13 @@ void ADSREnvelope::reset(const EGDescription& desc, const Region& region, int de || (region.loopMode == LoopMode::one_shot && region.isOscillator()) ); currentValue = this->start; - currentState = State::Delay; } void ADSREnvelope::updateValues(int delay) noexcept { - this->delay = delay + secondsToSamples(desc_->getDelay(midiState_, triggerVelocity_, delay)); + if (currentState == State::Delay) + this->delay = delay + secondsToSamples(desc_->getDelay(midiState_, triggerVelocity_, delay)); + this->attackStep = secondsToLinRate(desc_->getAttack(midiState_, triggerVelocity_, delay)); this->decayRate = secondsToExpRate(desc_->getDecay(midiState_, triggerVelocity_, delay)); this->releaseRate = secondsToExpRate(desc_->getRelease(midiState_, triggerVelocity_, delay)); From c382b8894ee1cdc292e55cc384dcb9d0bff4a10a Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 27 Jun 2021 22:35:41 +0200 Subject: [PATCH 114/193] Negative group IDs --- src/sfizz/Defaults.cpp | 2 +- src/sfizz/Defaults.h | 2 +- src/sfizz/FlexEnvelope.cpp | 5 +---- src/sfizz/Region.h | 4 ++-- src/sfizz/VoiceManager.cpp | 27 ++++++++++++++------------- src/sfizz/VoiceManager.h | 9 +++++---- tests/PolyphonyT.cpp | 4 ++-- tests/RegionValuesT.cpp | 8 ++++---- tests/SynthT.cpp | 23 +++++++++++++++++++++++ 9 files changed, 53 insertions(+), 31 deletions(-) diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index 40ce37c7f..d70f3f83e 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -41,7 +41,7 @@ FloatSpec oscillatorDetuneMod { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds } FloatSpec oscillatorModDepth { 0.0f, {0.0f, 10000.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec oscillatorModDepthMod { 0.0f, {0.0f, 10000.0f}, kNormalizePercent|kPermissiveBounds }; Int32Spec oscillatorQuality { 1, {0, 3}, 0 }; -UInt32Spec group { 0, {0, uint32_t_max}, 0 }; +Int64Spec group { 0, {-int32_t_max, uint32_t_max}, 0 }; FloatSpec offTime { 6e-3f, {0.0f, 100.0f}, kPermissiveBounds }; UInt32Spec polyphony { config::maxVoices, {0, config::maxVoices}, kEnforceBounds }; UInt32Spec notePolyphony { config::maxVoices, {1, config::maxVoices}, kEnforceBounds }; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 7d6c54fa9..05fc5a379 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -153,7 +153,7 @@ namespace Default extern const OpcodeSpec oscillatorModDepth; extern const OpcodeSpec oscillatorModDepthMod; extern const OpcodeSpec oscillatorQuality; - extern const OpcodeSpec group; + extern const OpcodeSpec group; extern const OpcodeSpec offTime; extern const OpcodeSpec polyphony; extern const OpcodeSpec notePolyphony; diff --git a/src/sfizz/FlexEnvelope.cpp b/src/sfizz/FlexEnvelope.cpp index 3cbd41617..39a387afe 100644 --- a/src/sfizz/FlexEnvelope.cpp +++ b/src/sfizz/FlexEnvelope.cpp @@ -95,8 +95,6 @@ void FlexEnvelope::configure(const FlexEGDescription* desc) void FlexEnvelope::start(unsigned triggerDelay) { Impl& impl = *impl_; - const FlexEGDescription& desc = *impl.desc_; - impl.delayFramesLeft_ = triggerDelay; impl.currentFramesUntilRelease_ = absl::nullopt; impl.advanceToStage(0); @@ -116,6 +114,7 @@ void FlexEnvelope::release(unsigned releaseDelay) void FlexEnvelope::cancelRelease(unsigned delay) { + UNUSED(delay); Impl& impl = *impl_; const FlexEGDescription& desc = *impl.desc_; @@ -249,8 +248,6 @@ void FlexEnvelope::Impl::process(absl::Span out) bool FlexEnvelope::Impl::advanceToStage(unsigned stageNumber) { const FlexEGDescription& desc = *desc_; - const MidiState& midiState = resources_->getMidiState(); - currentStageNumber_ = stageNumber; if (stageNumber >= desc.points.size()) diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index fe4070652..95dfec54c 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -327,8 +327,8 @@ struct Region { absl::optional oscillatorQuality; // Instrument settings: voice lifecycle - uint32_t group { Default::group }; // group - absl::optional offBy {}; // off_by + int64_t group { Default::group }; // group + absl::optional offBy {}; // off_by OffMode offMode { Default::offMode }; // off_mode float offTime { Default::offTime }; // off_mode absl::optional notePolyphony {}; // note_polyphony diff --git a/src/sfizz/VoiceManager.cpp b/src/sfizz/VoiceManager.cpp index 862626d74..7440f5cf8 100644 --- a/src/sfizz/VoiceManager.cpp +++ b/src/sfizz/VoiceManager.cpp @@ -19,7 +19,7 @@ void VoiceManager::onVoiceStateChanging(NumericId id, Voice::State state) const uint32_t group = region->group; RegionSet::removeVoiceFromHierarchy(region, voice); swapAndPopFirst(activeVoices_, [voice](const Voice* v) { return v == voice; }); - ASSERT(group < polyphonyGroups_.size()); + ASSERT(polyphonyGroups_.contains(group)); polyphonyGroups_[group].removeVoice(voice); } else if (state == Voice::State::playing) { Voice* voice = getVoiceById(id); @@ -27,7 +27,7 @@ void VoiceManager::onVoiceStateChanging(NumericId id, Voice::State state) const uint32_t group = region->group; activeVoices_.push_back(voice); RegionSet::registerVoiceInHierarchy(region, voice); - ASSERT(group < polyphonyGroups_.size()); + ASSERT(polyphonyGroups_.contains(group)); polyphonyGroups_[group].registerVoice(voice); } } @@ -61,8 +61,7 @@ void VoiceManager::reset() voice.reset(); polyphonyGroups_.clear(); - polyphonyGroups_.emplace_back(); - polyphonyGroups_.back().setPolyphonyLimit(config::maxVoices); + polyphonyGroups_.emplace(0, PolyphonyGroup{}); setStealingAlgorithm(StealingAlgorithm::Oldest); } @@ -84,29 +83,31 @@ bool VoiceManager::playingAttackVoice(const Region* releaseRegion) noexcept return true; } -void VoiceManager::ensureNumPolyphonyGroups(unsigned groupIdx) noexcept +void VoiceManager::ensureNumPolyphonyGroups(int groupIdx) noexcept { - size_t neededSize = static_cast(groupIdx) + 1; - if (polyphonyGroups_.size() < neededSize) - polyphonyGroups_.resize(neededSize); + if (!polyphonyGroups_.contains(groupIdx)) + polyphonyGroups_.emplace(groupIdx, PolyphonyGroup{}); } -void VoiceManager::setGroupPolyphony(unsigned groupIdx, unsigned polyphony) noexcept +void VoiceManager::setGroupPolyphony(int groupIdx, unsigned polyphony) noexcept { ensureNumPolyphonyGroups(groupIdx); polyphonyGroups_[groupIdx].setPolyphonyLimit(polyphony); } -const PolyphonyGroup* VoiceManager::getPolyphonyGroupView(int idx) const noexcept +const PolyphonyGroup* VoiceManager::getPolyphonyGroupView(int idx) noexcept { - return (size_t)idx < polyphonyGroups_.size() ? &polyphonyGroups_[idx] : nullptr; + if (!polyphonyGroups_.contains(idx)) + return {}; + + return &polyphonyGroups_[idx]; } void VoiceManager::clear() { - for (PolyphonyGroup& pg : polyphonyGroups_) - pg.removeAllVoices(); + for (auto& pg : polyphonyGroups_) + pg.second.removeAllVoices(); list_.clear(); activeVoices_.clear(); } diff --git a/src/sfizz/VoiceManager.h b/src/sfizz/VoiceManager.h index b4e22d4d5..785f271cd 100644 --- a/src/sfizz/VoiceManager.h +++ b/src/sfizz/VoiceManager.h @@ -6,6 +6,7 @@ #pragma once +#include "absl/container/flat_hash_map.h" #include "Config.h" #include "PolyphonyGroup.h" #include "Region.h" @@ -59,7 +60,7 @@ struct VoiceManager final : public Voice::StateListener * * @param groupIdx */ - void ensureNumPolyphonyGroups(unsigned groupIdx) noexcept; + void ensureNumPolyphonyGroups(int groupIdx) noexcept; /** * @brief Set the polyphony for a given group @@ -69,7 +70,7 @@ struct VoiceManager final : public Voice::StateListener * @param groupIdx * @param polyphony */ - void setGroupPolyphony(unsigned groupIdx, unsigned polyphony) noexcept; + void setGroupPolyphony(int groupIdx, unsigned polyphony) noexcept; /** * @brief Get a view into a given polyphony group @@ -77,7 +78,7 @@ struct VoiceManager final : public Voice::StateListener * @param idx * @return const PolyphonyGroup* */ - const PolyphonyGroup* getPolyphonyGroupView(int idx) const noexcept; + const PolyphonyGroup* getPolyphonyGroupView(int idx) noexcept; /** * @brief Clear all voices and polyphony groups. @@ -138,7 +139,7 @@ struct VoiceManager final : public Voice::StateListener std::vector activeVoices_; std::vector temp_; // These are the `group=` groups where you can off voices - std::vector polyphonyGroups_; + absl::flat_hash_map polyphonyGroups_; std::unique_ptr stealer_ { absl::make_unique() }; /** diff --git a/tests/PolyphonyT.cpp b/tests/PolyphonyT.cpp index 0a966da9c..f07e6c919 100644 --- a/tests/PolyphonyT.cpp +++ b/tests/PolyphonyT.cpp @@ -57,7 +57,7 @@ TEST_CASE("[Polyphony] Polyphony groups") group=4 key=62 sample=*sine )"); - REQUIRE( synth.getNumPolyphonyGroups() == 5 ); + REQUIRE( synth.getNumPolyphonyGroups() == 4 ); REQUIRE( synth.getNumRegions() == 5 ); REQUIRE( synth.getRegionView(0)->group == 0 ); REQUIRE( synth.getRegionView(1)->group == 1 ); @@ -67,7 +67,7 @@ TEST_CASE("[Polyphony] Polyphony groups") REQUIRE( synth.getRegionView(4)->group == 4 ); REQUIRE( synth.getPolyphonyGroupView(1)->getPolyphonyLimit() == 3 ); REQUIRE( synth.getPolyphonyGroupView(2)->getPolyphonyLimit() == 4 ); - REQUIRE( synth.getPolyphonyGroupView(3)->getPolyphonyLimit() == sfz::config::maxVoices ); + REQUIRE( !synth.getPolyphonyGroupView(3) ); REQUIRE( synth.getPolyphonyGroupView(4)->getPolyphonyLimit() == 5 ); } diff --git a/tests/RegionValuesT.cpp b/tests/RegionValuesT.cpp index 970a259ed..633c6afe1 100644 --- a/tests/RegionValuesT.cpp +++ b/tests/RegionValuesT.cpp @@ -426,7 +426,7 @@ TEST_CASE("[Values] Group") synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( sample=kick.wav sample=kick.wav group=5 - sample=kick.wav group=-1 + sample=kick.wav group=-2 )"); synth.dispatchMessage(client, 0, "/region0/group", "", nullptr); synth.dispatchMessage(client, 0, "/region1/group", "", nullptr); @@ -434,7 +434,7 @@ TEST_CASE("[Values] Group") std::vector expected { "/region0/group,h : { 0 }", "/region1/group,h : { 5 }", - "/region2/group,h : { 0 }", + "/region2/group,h : { -2 }", }; REQUIRE(messageList == expected); } @@ -448,7 +448,7 @@ TEST_CASE("[Values] Off by") synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( sample=kick.wav sample=kick.wav off_by=5 - sample=kick.wav off_by=-1 + sample=kick.wav off_by=-2 )"); synth.dispatchMessage(client, 0, "/region0/off_by", "", nullptr); synth.dispatchMessage(client, 0, "/region1/off_by", "", nullptr); @@ -456,7 +456,7 @@ TEST_CASE("[Values] Off by") std::vector expected { "/region0/off_by,N : { }", "/region1/off_by,h : { 5 }", - "/region2/off_by,N : { }", + "/region2/off_by,h : { -2 }", }; REQUIRE(messageList == expected); } diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 639acefa8..ad099fded 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -1595,6 +1595,29 @@ TEST_CASE("[Synth] Off by standard") REQUIRE( playingVoices.front()->getRegion()->keyRange.containsWithEnd(60) ); } +TEST_CASE("[Synth] Off by negative groups") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + + synth.loadSfzString(fs::current_path(), R"( + group=-1 off_by=-2 sample=*saw transpose=12 key=60 + group=-2 off_by=-1 sample=*triangle key=62 + )"); + synth.noteOn(0, 60, 85); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 1 ); + synth.noteOn(10, 62, 85); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 1 ); + auto playingVoices = getPlayingVoices(synth); + REQUIRE( playingVoices.front()->getRegion()->keyRange.containsWithEnd(62) ); + synth.noteOn(10, 60, 85); + synth.renderBlock(buffer); + playingVoices = getPlayingVoices(synth); + REQUIRE( playingVoices.front()->getRegion()->keyRange.containsWithEnd(60) ); +} + TEST_CASE("[Synth] Off by same group") { sfz::Synth synth; From 5527282159b7e001f4b021d7002e163c7b777f28 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 29 Jun 2021 10:48:53 +0200 Subject: [PATCH 115/193] Parse the relevant inputs --- src/sfizz/Defaults.cpp | 3 + src/sfizz/Defaults.h | 3 + src/sfizz/FilterDescription.h | 1 + src/sfizz/Region.cpp | 50 +++++++++++++++ src/sfizz/Region.h | 2 + src/sfizz/SfzHelpers.h | 7 +++ src/sfizz/SynthMessaging.cpp | 62 ++++++++++++++++++ tests/RegionValuesT.cpp | 115 +++++++++++++++++++++++++--------- 8 files changed, 215 insertions(+), 28 deletions(-) diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index 40ce37c7f..a70f3602b 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -90,6 +90,7 @@ FloatSpec width { 100.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds FloatSpec widthMod { 0.0f, {-200.0f, 200.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec ampKeytrack { 0.0f, {-96.0f, 12.0f}, kPermissiveBounds }; FloatSpec ampVeltrack { 100.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; +FloatSpec ampVeltrackMod { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec ampVelcurve { 0.0f, {0.0f, 1.0f}, kPermissiveBounds }; FloatSpec ampRandom { 0.0f, {-24.0f, 24.0f}, kPermissiveBounds }; BoolSpec rtDead { false, {false, true}, kEnforceBounds }; @@ -103,6 +104,7 @@ FloatSpec filterGainMod { 0.0f, {-96.0f, 96.0f}, kPermissiveBounds }; FloatSpec filterRandom { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds }; FloatSpec filterKeytrack { 0, {0, 1200}, kPermissiveBounds }; FloatSpec filterVeltrack { 0, {-12000, 12000}, kPermissiveBounds }; +FloatSpec filterVeltrackMod { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec eqBandwidth { 1.0f, {0.001f, 4.0f}, kPermissiveBounds }; FloatSpec eqBandwidthMod { 0.0f, {-4.0f, 4.0f}, kPermissiveBounds }; FloatSpec eqFrequency { 0.0f, {0.0f, 20000.0f}, kPermissiveBounds }; @@ -114,6 +116,7 @@ FloatSpec eqVel2Gain { 0.0f, {-96.0f, 96.0f}, kPermissiveBounds }; FloatSpec pitchKeytrack { 100, {-1200, 1200}, kPermissiveBounds }; FloatSpec pitchRandom { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds }; FloatSpec pitchVeltrack { 0, {-12000, 12000}, kPermissiveBounds }; +FloatSpec pitchVeltrackMod { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec transpose { 0, {-127, 127}, kPermissiveBounds }; FloatSpec pitch { 0.0f, {-2400.0f, 2400.0f}, kPermissiveBounds }; FloatSpec pitchMod { 0.0f, {-2400.0f, 2400.0f}, kPermissiveBounds }; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 7d6c54fa9..2a2719207 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -202,6 +202,7 @@ namespace Default extern const OpcodeSpec widthMod; extern const OpcodeSpec ampKeytrack; extern const OpcodeSpec ampVeltrack; + extern const OpcodeSpec ampVeltrackMod; extern const OpcodeSpec ampVelcurve; extern const OpcodeSpec ampRandom; extern const OpcodeSpec rtDead; @@ -215,6 +216,7 @@ namespace Default extern const OpcodeSpec filterRandom; extern const OpcodeSpec filterKeytrack; extern const OpcodeSpec filterVeltrack; + extern const OpcodeSpec filterVeltrackMod; extern const OpcodeSpec eqBandwidth; extern const OpcodeSpec eqBandwidthMod; extern const OpcodeSpec eqFrequency; @@ -226,6 +228,7 @@ namespace Default extern const OpcodeSpec pitchKeytrack; extern const OpcodeSpec pitchRandom; extern const OpcodeSpec pitchVeltrack; + extern const OpcodeSpec pitchVeltrackMod; extern const OpcodeSpec transpose; extern const OpcodeSpec pitch; extern const OpcodeSpec pitchMod; diff --git a/src/sfizz/FilterDescription.h b/src/sfizz/FilterDescription.h index 4674477c1..59eadab9c 100644 --- a/src/sfizz/FilterDescription.h +++ b/src/sfizz/FilterDescription.h @@ -20,6 +20,7 @@ struct FilterDescription float keytrack { Default::filterKeytrack }; uint8_t keycenter { Default::key }; float veltrack { Default::filterVeltrack }; + CCMap> veltrackCC { ModifierCurvePair{ Default::filterVeltrackMod, Default::curveCC } }; float random { Default::filterRandom }; FilterType type { FilterType::kFilterLpf2p }; }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 344d7c6a4..0dc7fc3d9 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -449,6 +449,18 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode, bool cleanOpcode) case hash("amp_veltrack"): ampVeltrack = opcode.read(Default::ampVeltrack); break; + case hash("amp_veltrack_oncc&"): + if (opcode.parameters.back() >= config::numCCs) + return false; + + ampVeltrackCC[opcode.parameters.back()].modifier = opcode.read(Default::ampVeltrackMod); + break; + case hash("amp_veltrack_curvecc&"): + if (opcode.parameters.back() >= config::numCCs) + return false; + + ampVeltrackCC[opcode.parameters.back()].curve = opcode.read(Default::curveCC); + break; case hash("amp_random"): ampRandom = opcode.read(Default::ampRandom); break; @@ -625,6 +637,32 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode, bool cleanOpcode) filters[filterIndex].veltrack = opcode.read(Default::filterVeltrack); } break; + case hash("fil&_veltrack_oncc&"): + { + const auto filterIndex = opcode.parameters.front() - 1; + if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) + return false; + + const auto cc = opcode.parameters.back(); + if (cc >= config::numCCs) + return false; + + filters[filterIndex].veltrackCC[cc].modifier = opcode.read(Default::ampVeltrackMod); + } + break; + case hash("fil&_veltrack_curvecc&"): + { + const auto filterIndex = opcode.parameters.front() - 1; + if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) + return false; + + const auto cc = opcode.parameters.back(); + if (cc >= config::numCCs) + return false; + + filters[filterIndex].veltrackCC[cc].curve = opcode.read(Default::curveCC); + } + break; case hash("fil&_random"): // also fil_random, cutoff_random, cutoff&_random { const auto filterIndex = opcode.parameters.front() - 1; @@ -755,6 +793,18 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode, bool cleanOpcode) case hash("pitch_veltrack"): pitchVeltrack = opcode.read(Default::pitchVeltrack); break; + case hash("pitch_veltrack_oncc&"): + if (opcode.parameters.back() >= config::numCCs) + return false; + + pitchVeltrackCC[opcode.parameters.back()].modifier = opcode.read(Default::pitchVeltrackMod); + break; + case hash("pitch_veltrack_curvecc&"): + if (opcode.parameters.back() >= config::numCCs) + return false; + + pitchVeltrackCC[opcode.parameters.back()].curve = opcode.read(Default::curveCC); + break; case hash("pitch_random"): pitchRandom = opcode.read(Default::pitchRandom); break; diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index fe4070652..11a5757b7 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -384,6 +384,7 @@ struct Region { uint8_t ampKeycenter { Default::key }; // amp_keycenter float ampKeytrack { Default::ampKeytrack }; // amp_keytrack float ampVeltrack { Default::ampVeltrack }; // amp_veltrack + CCMap> ampVeltrackCC { ModifierCurvePair{ Default::ampVeltrackMod, Default::curveCC } }; std::vector> velocityPoints; // amp_velcurve_N absl::optional velCurve {}; float ampRandom { Default::ampRandom }; // amp_random @@ -415,6 +416,7 @@ struct Region { float pitchKeytrack { Default::pitchKeytrack }; // pitch_keytrack float pitchRandom { Default::pitchRandom }; // pitch_random float pitchVeltrack { Default::pitchVeltrack }; // pitch_veltrack + CCMap> pitchVeltrackCC { ModifierCurvePair{ Default::pitchVeltrackMod, Default::curveCC } }; float transpose { Default::transpose }; // transpose float pitch { Default::pitch }; // tune float bendUp { Default::bendUp }; diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index 46a7e1661..8fce77fad 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -22,6 +22,13 @@ namespace sfz { using CCNamePair = std::pair; using NoteNamePair = std::pair; +template +struct ModifierCurvePair +{ + T modifier {}; + uint8_t curve {}; +}; + template using MidiNoteArray = std::array; template diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index 3e6a87f2b..ce0c62b38 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -782,6 +782,26 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co client.receive<'f'>(delay, path, region.ampVeltrack * 100.0f); } break; + MATCH("/region&/amp_veltrack_cc&", "") { + GET_REGION_OR_BREAK(indices[0]) + if (region.ampVeltrackCC.contains(indices[1])) { + const auto& cc = region.ampVeltrackCC.getWithDefault(indices[1]); + client.receive<'f'>(delay, path, cc.modifier * 100.0f); + } else { + client.receive<'N'>(delay, path, {}); + } + } break; + + MATCH("/region&/amp_veltrack_curvecc&", "") { + GET_REGION_OR_BREAK(indices[0]) + if (region.ampVeltrackCC.contains(indices[1])) { + const auto& cc = region.ampVeltrackCC.getWithDefault(indices[1]); + client.receive<'i'>(delay, path, cc.curve ); + } else { + client.receive<'N'>(delay, path, {}); + } + } break; + MATCH("/region&/amp_random", "") { GET_REGION_OR_BREAK(indices[0]) client.receive<'f'>(delay, path, region.ampRandom); @@ -921,6 +941,26 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co client.receive<'i'>(delay, path, region.pitchVeltrack); } break; + MATCH("/region&/pitch_veltrack_cc&", "") { + GET_REGION_OR_BREAK(indices[0]) + if (region.pitchVeltrackCC.contains(indices[1])) { + const auto& cc = region.pitchVeltrackCC.getWithDefault(indices[1]); + client.receive<'f'>(delay, path, cc.modifier * 100.0f); + } else { + client.receive<'N'>(delay, path, {}); + } + } break; + + MATCH("/region&/pitch_veltrack_curvecc&", "") { + GET_REGION_OR_BREAK(indices[0]) + if (region.pitchVeltrackCC.contains(indices[1])) { + const auto& cc = region.pitchVeltrackCC.getWithDefault(indices[1]); + client.receive<'i'>(delay, path, cc.curve ); + } else { + client.receive<'N'>(delay, path, {}); + } + } break; + MATCH("/region&/pitch_random", "") { GET_REGION_OR_BREAK(indices[0]) client.receive<'f'>(delay, path, region.pitchRandom); @@ -1277,6 +1317,28 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co client.receive<'i'>(delay, path, filter.veltrack); } break; + MATCH("/region&/filter&/veltrack_cc&", "") { + GET_REGION_OR_BREAK(indices[0]) + GET_FILTER_OR_BREAK(indices[1]) + if (filter.veltrackCC.contains(indices[2])) { + const auto& cc = filter.veltrackCC.getWithDefault(indices[2]); + client.receive<'f'>(delay, path, cc.modifier * 100.0f); + } else { + client.receive<'N'>(delay, path, {}); + } + } break; + + MATCH("/region&/filter&/veltrack_curvecc&", "") { + GET_REGION_OR_BREAK(indices[0]) + GET_FILTER_OR_BREAK(indices[1]) + if (filter.veltrackCC.contains(indices[2])) { + const auto& cc = filter.veltrackCC.getWithDefault(indices[2]); + client.receive<'i'>(delay, path, cc.curve ); + } else { + client.receive<'N'>(delay, path, {}); + } + } break; + MATCH("/region&/filter&/type", "") { GET_REGION_OR_BREAK(indices[0]) GET_FILTER_OR_BREAK(indices[1]) diff --git a/tests/RegionValuesT.cpp b/tests/RegionValuesT.cpp index 970a259ed..dfbb3f51d 100644 --- a/tests/RegionValuesT.cpp +++ b/tests/RegionValuesT.cpp @@ -1629,20 +1629,47 @@ TEST_CASE("[Values] Amp Veltrack") Client client(&messageList); client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav amp_veltrack=10.1 - sample=kick.wav amp_veltrack=-132 - )"); - synth.dispatchMessage(client, 0, "/region0/amp_veltrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/amp_veltrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/amp_veltrack", "", nullptr); - std::vector expected { - "/region0/amp_veltrack,f : { 100 }", - "/region1/amp_veltrack,f : { 10.1 }", - "/region2/amp_veltrack,f : { -132 }", - }; - REQUIRE(messageList == expected); + SECTION("Basic") + { + synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + sample=kick.wav + sample=kick.wav amp_veltrack=10.1 + sample=kick.wav amp_veltrack=-132 + )"); + synth.dispatchMessage(client, 0, "/region0/amp_veltrack", "", nullptr); + synth.dispatchMessage(client, 0, "/region1/amp_veltrack", "", nullptr); + synth.dispatchMessage(client, 0, "/region2/amp_veltrack", "", nullptr); + std::vector expected { + "/region0/amp_veltrack,f : { 100 }", + "/region1/amp_veltrack,f : { 10.1 }", + "/region2/amp_veltrack,f : { -132 }", + }; + REQUIRE(messageList == expected); + } + + SECTION("CC") + { + synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + sample=kick.wav + sample=kick.wav amp_veltrack_cc1=10.1 amp_veltrack_curvecc1=3 + sample=kick.wav amp_veltrack_oncc2=-40 amp_veltrack_curvecc3=4 + )"); + synth.dispatchMessage(client, 0, "/region0/amp_veltrack_cc1", "", nullptr); + synth.dispatchMessage(client, 0, "/region1/amp_veltrack_cc1", "", nullptr); + synth.dispatchMessage(client, 0, "/region1/amp_veltrack_curvecc1", "", nullptr); + synth.dispatchMessage(client, 0, "/region2/amp_veltrack_cc2", "", nullptr); + synth.dispatchMessage(client, 0, "/region2/amp_veltrack_curvecc3", "", nullptr); + // TODO: activate for the new region parser ; accept oob + // synth.dispatchMessage(client, 0, "/region2/amp_veltrack", "", nullptr); + std::vector expected { + "/region0/amp_veltrack_cc1,N : { }", + "/region1/amp_veltrack_cc1,f : { 10.1 }", + "/region1/amp_veltrack_curvecc1,i : { 3 }", + "/region2/amp_veltrack_cc2,f : { -40 }", + "/region2/amp_veltrack_curvecc3,i : { 4 }", + }; + REQUIRE(messageList == expected); + } } TEST_CASE("[Values] Amp Random") @@ -1987,20 +2014,47 @@ TEST_CASE("[Values] Pitch Veltrack") Client client(&messageList); client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav pitch_veltrack=10 - sample=kick.wav pitch_veltrack=-132 - )"); - synth.dispatchMessage(client, 0, "/region0/pitch_veltrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/pitch_veltrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/pitch_veltrack", "", nullptr); - std::vector expected { - "/region0/pitch_veltrack,i : { 0 }", - "/region1/pitch_veltrack,i : { 10 }", - "/region2/pitch_veltrack,i : { -132 }", - }; - REQUIRE(messageList == expected); + SECTION("Basic") + { + synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + sample=kick.wav + sample=kick.wav pitch_veltrack=10 + sample=kick.wav pitch_veltrack=-132 + )"); + synth.dispatchMessage(client, 0, "/region0/pitch_veltrack", "", nullptr); + synth.dispatchMessage(client, 0, "/region1/pitch_veltrack", "", nullptr); + synth.dispatchMessage(client, 0, "/region2/pitch_veltrack", "", nullptr); + std::vector expected { + "/region0/pitch_veltrack,i : { 0 }", + "/region1/pitch_veltrack,i : { 10 }", + "/region2/pitch_veltrack,i : { -132 }", + }; + REQUIRE(messageList == expected); + } + + SECTION("CC") + { + synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + sample=kick.wav + sample=kick.wav pitch_veltrack_cc1=10.1 pitch_veltrack_curvecc1=3 + sample=kick.wav pitch_veltrack_oncc2=-40 pitch_veltrack_curvecc3=4 + )"); + synth.dispatchMessage(client, 0, "/region0/pitch_veltrack_cc1", "", nullptr); + synth.dispatchMessage(client, 0, "/region1/pitch_veltrack_cc1", "", nullptr); + synth.dispatchMessage(client, 0, "/region1/pitch_veltrack_curvecc1", "", nullptr); + synth.dispatchMessage(client, 0, "/region2/pitch_veltrack_cc2", "", nullptr); + synth.dispatchMessage(client, 0, "/region2/pitch_veltrack_curvecc3", "", nullptr); + // TODO: activate for the new region parser ; accept oob + // synth.dispatchMessage(client, 0, "/region2/pitch_veltrack", "", nullptr); + std::vector expected { + "/region0/pitch_veltrack_cc1,N : { }", + "/region1/pitch_veltrack_cc1,f : { 10.1 }", + "/region1/pitch_veltrack_curvecc1,i : { 3 }", + "/region2/pitch_veltrack_cc2,f : { -40 }", + "/region2/pitch_veltrack_curvecc3,i : { 4 }", + }; + REQUIRE(messageList == expected); + } } TEST_CASE("[Values] Pitch Random") @@ -2992,6 +3046,7 @@ TEST_CASE("[Values] Filter dispatching") sample=kick.wav cutoff3=50 resonance2=3 fil2_gain=-5 fil3_keytrack=100 fil_gain=5 fil1_gain=-5 fil2_veltrack=-100 + fil4_veltrack_cc7=-100 fil5_veltrack_curvecc2=2 )"); synth.dispatchMessage(client, 0, "/region0/filter2/cutoff", "", nullptr); @@ -3000,6 +3055,8 @@ TEST_CASE("[Values] Filter dispatching") synth.dispatchMessage(client, 0, "/region0/filter2/keytrack", "", nullptr); synth.dispatchMessage(client, 0, "/region0/filter0/gain", "", nullptr); synth.dispatchMessage(client, 0, "/region0/filter1/veltrack", "", nullptr); + synth.dispatchMessage(client, 0, "/region0/filter3/veltrack_cc7", "", nullptr); + synth.dispatchMessage(client, 0, "/region0/filter4/veltrack_curvecc2", "", nullptr); std::vector expected { "/region0/filter2/cutoff,f : { 50 }", "/region0/filter1/resonance,f : { 3 }", @@ -3007,6 +3064,8 @@ TEST_CASE("[Values] Filter dispatching") "/region0/filter2/keytrack,i : { 100 }", "/region0/filter0/gain,f : { -5 }", "/region0/filter1/veltrack,i : { -100 }", + "/region0/filter3/veltrack_cc7,f : { -100 }", + "/region0/filter4/veltrack_curvecc2,i : { 2 }", }; REQUIRE(messageList == expected); } From da942c9244ef1ae8038e92f0a89a99609076d7f8 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 28 Jun 2021 10:27:24 +0200 Subject: [PATCH 116/193] Store files to load and their offset and defer loading Better behavior when using a file multiple times with different offsets. --- src/sfizz/Synth.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 1e200b58d..d02271c54 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -581,6 +581,8 @@ void Synth::Impl::finalizeSfzLoad() size_t currentRegionIndex = 0; size_t currentRegionCount = layers_.size(); + absl::flat_hash_map filesToLoad; + auto removeCurrentRegion = [this, ¤tRegionIndex, ¤tRegionCount]() { const Region& region = layers_[currentRegionIndex]->getRegion(); DBG("Removing the region with sample " << *region.sampleId); @@ -679,10 +681,8 @@ void Synth::Impl::finalizeSfzLoad() return Default::offsetMod.bounds.clamp(sumOffsetCC); }(); - if (!filePool.preloadFile(*region.sampleId, maxOffset)) { - removeCurrentRegion(); - continue; - } + auto& toLoad = filesToLoad[*region.sampleId]; + toLoad = max(toLoad, maxOffset); } else if (!region.isGenerator()) { if (!wavePool.createFileWave(filePool, std::string(region.sampleId->filename()))) { @@ -763,6 +763,11 @@ void Synth::Impl::finalizeSfzLoad() ++currentRegionIndex; } + + for (const auto& toLoad: filesToLoad) { + filePool.preloadFile(toLoad.first, toLoad.second); + } + if (currentRegionCount < layers_.size()) { DBG("Removing " << (layers_.size() - currentRegionCount) << " out of " << layers_.size() << " regions"); From 66ec8c4423492a9ec0cf217dece5709fab02d399 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 4 Jul 2021 19:32:17 +0200 Subject: [PATCH 117/193] Move the stateful region value computations in a separate file --- common.mk | 1 + scripts/run_clang_tidy.sh | 1 + src/CMakeLists.txt | 2 + src/sfizz/Region.cpp | 123 ----------- src/sfizz/Region.h | 56 ----- src/sfizz/RegionStateful.cpp | 134 ++++++++++++ src/sfizz/RegionStateful.h | 97 +++++++++ src/sfizz/Voice.cpp | 19 +- tests/RegionValueComputationsT.cpp | 330 ++++++++++++++++++++--------- tests/SynthT.cpp | 92 -------- 10 files changed, 476 insertions(+), 379 deletions(-) create mode 100644 src/sfizz/RegionStateful.cpp create mode 100644 src/sfizz/RegionStateful.h diff --git a/common.mk b/common.mk index 37e82c1ef..24a261a89 100644 --- a/common.mk +++ b/common.mk @@ -107,6 +107,7 @@ SFIZZ_SOURCES = \ src/sfizz/PowerFollower.cpp \ src/sfizz/Region.cpp \ src/sfizz/RegionSet.cpp \ + src/sfizz/RegionStateful.cpp \ src/sfizz/Resources.cpp \ src/sfizz/RTSemaphore.cpp \ src/sfizz/ScopedFTZ.cpp \ diff --git a/scripts/run_clang_tidy.sh b/scripts/run_clang_tidy.sh index e970a75f4..1072c5384 100755 --- a/scripts/run_clang_tidy.sh +++ b/scripts/run_clang_tidy.sh @@ -14,6 +14,7 @@ clang-tidy \ src/sfizz/Panning.cpp \ src/sfizz/sfizz.cpp \ src/sfizz/Region.cpp \ + src/sfizz/RegionStateful.cpp \ src/sfizz/SIMDHelpers.cpp \ src/sfizz/simd/HelpersSSE.cpp \ src/sfizz/simd/HelpersAVX.cpp \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a1a2533b7..f78bf315d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -96,6 +96,7 @@ set(SFIZZ_HEADERS sfizz/railsback/4-1.h sfizz/railsback/4-2.h sfizz/Region.h + sfizz/RegionStateful.h sfizz/RegionSet.h sfizz/Resources.h sfizz/RTSemaphore.h @@ -131,6 +132,7 @@ set(SFIZZ_SOURCES sfizz/AudioReader.cpp sfizz/FilterPool.cpp sfizz/EQPool.cpp + sfizz/RegionStateful.cpp sfizz/Region.cpp sfizz/Voice.cpp sfizz/ScopedFTZ.cpp diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 0dc7fc3d9..40357c711 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -6,13 +6,11 @@ #include "Region.h" #include "Opcode.h" -#include "MidiState.h" #include "MathHelpers.h" #include "utility/SwapAndPop.h" #include "utility/StringViewHelpers.h" #include "utility/Macros.h" #include "utility/Debug.h" -#include "ModifierHelpers.h" #include "modulations/ModId.h" #include "absl/strings/str_replace.h" #include "absl/strings/str_cat.h" @@ -1739,18 +1737,6 @@ float sfz::Region::getBasePitchVariation(float noteNumber, float velocity) const return centsFactor(pitchVariationInCents); } -float sfz::Region::getBaseVolumedB(const MidiState& midiState, int noteNumber) const noexcept -{ - fast_real_distribution volumeDistribution { 0.0f, ampRandom }; - auto baseVolumedB = volume + volumeDistribution(Random::randomGenerator); - baseVolumedB += globalVolume; - baseVolumedB += masterVolume; - baseVolumedB += groupVolume; - if (trigger == Trigger::release || trigger == Trigger::release_key) - baseVolumedB -= rtDecay * midiState.getNoteDuration(noteNumber); - return baseVolumedB; -} - float sfz::Region::getBaseGain() const noexcept { float baseGain = amplitude; @@ -1774,115 +1760,6 @@ float sfz::Region::getPhase() const noexcept return phase; } -uint64_t sfz::Region::getOffset(const MidiState& midiState) 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.data * midiState.getCCValue(mod.cc)); - return Default::offset.bounds.clamp(finalOffset); -} - -float sfz::Region::getDelay(const MidiState& midiState) const noexcept -{ - fast_real_distribution delayDistribution { 0, delayRandom }; - float finalDelay { delay }; - finalDelay += delayDistribution(Random::randomGenerator); - for (const auto& mod: delayCC) - finalDelay += mod.data * midiState.getCCValue(mod.cc); - - return Default::delay.bounds.clamp(finalDelay); -} - -uint32_t sfz::Region::getSampleEnd(MidiState& midiState) const noexcept -{ - int64_t end = sampleEnd; - for (const auto& mod: endCC) - end += static_cast(mod.data * midiState.getCCValue(mod.cc)); - - end = clamp(end, int64_t { 0 }, sampleEnd); - return static_cast(end); -} - -uint32_t sfz::Region::loopStart(MidiState& midiState) const noexcept -{ - auto start = loopRange.getStart(); - for (const auto& mod: loopStartCC) - start += static_cast(mod.data * midiState.getCCValue(mod.cc)); - - start = clamp(start, int64_t { 0 }, sampleEnd); - return static_cast(start); -} - -uint32_t sfz::Region::loopEnd(MidiState& midiState) const noexcept -{ - auto end = loopRange.getEnd(); - for (const auto& mod: loopEndCC) - end += static_cast(mod.data * midiState.getCCValue(mod.cc)); - - end = clamp(end, int64_t { 0 }, sampleEnd); - return static_cast(end); -} - -float sfz::Region::getNoteGain(int noteNumber, float velocity) const noexcept -{ - ASSERT(velocity >= 0.0f && velocity <= 1.0f); - - float baseGain { 1.0f }; - - // Amplitude key tracking - baseGain *= db2mag(ampKeytrack * static_cast(noteNumber - ampKeycenter)); - - // Crossfades related to the note number - baseGain *= crossfadeIn(crossfadeKeyInRange, noteNumber, crossfadeKeyCurve); - baseGain *= crossfadeOut(crossfadeKeyOutRange, noteNumber, crossfadeKeyCurve); - - // Amplitude velocity tracking - baseGain *= velocityCurve(velocity); - - // Crossfades related to velocity - baseGain *= crossfadeIn(crossfadeVelInRange, velocity, crossfadeVelCurve); - baseGain *= crossfadeOut(crossfadeVelOutRange, velocity, crossfadeVelCurve); - - return baseGain; -} - -float sfz::Region::getCrossfadeGain(const MidiState& midiState) const noexcept -{ - float gain { 1.0f }; - - // Crossfades due to CC states - 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& ccData : crossfadeCCOutRange) { - const auto ccValue = midiState.getCCValue(ccData.cc); - const auto crossfadeRange = ccData.data; - gain *= crossfadeOut(crossfadeRange, ccValue, crossfadeCCCurve); - } - - return gain; -} - -float sfz::Region::velocityCurve(float velocity) const noexcept -{ - ASSERT(velocity >= 0.0f && velocity <= 1.0f); - - float gain; - if (velCurve) - gain = velCurve->evalNormalized(velocity); - else - gain = velocity * velocity; - - gain = std::fabs(ampVeltrack) * (1.0f - gain); - gain = (ampVeltrack < 0) ? gain : (1.0f - gain); - - return gain; -} - void sfz::Region::offsetAllKeys(int offset) noexcept { // Offset key range diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 11a5757b7..ab1e76d8f 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -108,32 +108,6 @@ struct Region { * @return float */ float getBasePitchVariation(float noteNumber, float velocity) const noexcept; - /** - * @brief Get the note-related gain of the region depending on which note has been - * pressed and at which velocity. - * - * @param noteNumber - * @param velocity - * @return float - */ - float getNoteGain(int noteNumber, float velocity) const noexcept; - /** - * @brief Get the additional crossfade gain of the region depending on the - * CC values - * - * @param midiState - * @return float - */ - float getCrossfadeGain(const MidiState& midiState) const noexcept; - /** - * @brief Get the base volume of the region depending on which note has been - * pressed to trigger the region. - * - * @param midiState - * @param noteNumber - * @return float - */ - float getBaseVolumedB(const MidiState& midiState, int noteNumber) const noexcept; /** * @brief Get the base gain of the region. * @@ -146,12 +120,6 @@ struct Region { * @return float */ float getPhase() const noexcept; - /** - * @brief Computes the gain value related to the velocity of the note - * - * @return float - */ - float velocityCurve(float velocity) const noexcept; /** * @brief Get the detuning in cents for a given bend value between -1 and 1 * @@ -160,27 +128,6 @@ struct Region { */ float getBendInCents(float bend) const noexcept; - /** - * @brief Get the region offset in samples - * - * @param midiState - * @return uint32_t - */ - uint64_t getOffset(const MidiState& midiState) const noexcept; - /** - * @brief Get the region delay in seconds - * - * @param midiState - * @return float - */ - float getDelay(const MidiState& midiState) const noexcept; - /** - * @brief Get the index of the sample end, either natural end or forced - * loop. - * - * @return uint32_t - */ - uint32_t getSampleEnd(MidiState& midiState) const noexcept; /** * @brief Parse a new opcode into the region to fill in the proper parameters. * This must be called multiple times for each opcode applying to this region. @@ -260,9 +207,6 @@ struct Region { void offsetAllKeys(int offset) noexcept; - uint32_t loopStart(MidiState& midiState) const noexcept; - uint32_t loopEnd(MidiState& midiState) const noexcept; - /** * @brief Get the gain this region contributes into the input of the Nth * effect bus diff --git a/src/sfizz/RegionStateful.cpp b/src/sfizz/RegionStateful.cpp new file mode 100644 index 000000000..b04a52aeb --- /dev/null +++ b/src/sfizz/RegionStateful.cpp @@ -0,0 +1,134 @@ +// 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 "RegionStateful.h" +#include "ModifierHelpers.h" + +namespace sfz { + +float getBaseVolumedB(const Region& region, const MidiState& midiState, int noteNumber) noexcept +{ + fast_real_distribution volumeDistribution { 0.0f, region.ampRandom }; + auto baseVolumedB = region.volume + volumeDistribution(Random::randomGenerator); + baseVolumedB += region.globalVolume; + baseVolumedB += region.masterVolume; + baseVolumedB += region.groupVolume; + if (region.trigger == Trigger::release || region.trigger == Trigger::release_key) + baseVolumedB -= region.rtDecay * midiState.getNoteDuration(noteNumber); + return baseVolumedB; +} + + +uint64_t getOffset(const Region& region, const MidiState& midiState) noexcept +{ + std::uniform_int_distribution offsetDistribution { 0, region.offsetRandom }; + uint64_t finalOffset = region.offset + offsetDistribution(Random::randomGenerator); + for (const auto& mod: region.offsetCC) + finalOffset += static_cast(mod.data * midiState.getCCValue(mod.cc)); + return Default::offset.bounds.clamp(finalOffset); +} + +float getDelay(const Region& region, const MidiState& midiState) noexcept +{ + fast_real_distribution delayDistribution { 0, region.delayRandom }; + float finalDelay { region.delay }; + finalDelay += delayDistribution(Random::randomGenerator); + for (const auto& mod: region.delayCC) + finalDelay += mod.data * midiState.getCCValue(mod.cc); + + return Default::delay.bounds.clamp(finalDelay); +} + +uint32_t getSampleEnd(const Region& region, MidiState& midiState) noexcept +{ + int64_t end = region.sampleEnd; + for (const auto& mod: region.endCC) + end += static_cast(mod.data * midiState.getCCValue(mod.cc)); + + end = clamp(end, int64_t { 0 }, region.sampleEnd); + return static_cast(end); +} + +uint32_t loopStart(const Region& region, MidiState& midiState) noexcept +{ + auto start = region.loopRange.getStart(); + for (const auto& mod: region.loopStartCC) + start += static_cast(mod.data * midiState.getCCValue(mod.cc)); + + start = clamp(start, int64_t { 0 }, region.sampleEnd); + return static_cast(start); +} + +uint32_t loopEnd(const Region& region, MidiState& midiState) noexcept +{ + auto end = region.loopRange.getEnd(); + for (const auto& mod: region.loopEndCC) + end += static_cast(mod.data * midiState.getCCValue(mod.cc)); + + end = clamp(end, int64_t { 0 }, region.sampleEnd); + return static_cast(end); +} + +float getNoteGain(const Region& region, int noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept +{ + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + + float baseGain { 1.0f }; + + // Amplitude key tracking + baseGain *= db2mag(region.ampKeytrack * static_cast(noteNumber - region.ampKeycenter)); + + // Crossfades related to the note number + baseGain *= crossfadeIn(region.crossfadeKeyInRange, noteNumber, region.crossfadeKeyCurve); + baseGain *= crossfadeOut(region.crossfadeKeyOutRange, noteNumber, region.crossfadeKeyCurve); + + // Amplitude velocity tracking + baseGain *= velocityCurve(region, velocity, midiState, curveSet); + + // Crossfades related to velocity + baseGain *= crossfadeIn(region.crossfadeVelInRange, velocity, region.crossfadeVelCurve); + baseGain *= crossfadeOut(region.crossfadeVelOutRange, velocity, region.crossfadeVelCurve); + + return baseGain; +} + +float getCrossfadeGain(const Region& region, const MidiState& midiState) noexcept +{ + float gain { 1.0f }; + + // Crossfades due to CC states + for (const auto& ccData : region.crossfadeCCInRange) { + const auto ccValue = midiState.getCCValue(ccData.cc); + const auto crossfadeRange = ccData.data; + gain *= crossfadeIn(crossfadeRange, ccValue, region.crossfadeCCCurve); + } + + for (const auto& ccData : region.crossfadeCCOutRange) { + const auto ccValue = midiState.getCCValue(ccData.cc); + const auto crossfadeRange = ccData.data; + gain *= crossfadeOut(crossfadeRange, ccValue, region.crossfadeCCCurve); + } + + return gain; +} + +float velocityCurve(const Region& region, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept +{ + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + + float gain; + if (region.velCurve) + gain = region.velCurve->evalNormalized(velocity); + else + gain = velocity * velocity; + + gain = std::fabs(region.ampVeltrack) * (1.0f - gain); + gain = (region.ampVeltrack < 0) ? gain : (1.0f - gain); + + return gain; +} + +} diff --git a/src/sfizz/RegionStateful.h b/src/sfizz/RegionStateful.h new file mode 100644 index 000000000..9c2574b3d --- /dev/null +++ b/src/sfizz/RegionStateful.h @@ -0,0 +1,97 @@ +// 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 "Region.h" +#include "MidiState.h" +#include "Curve.h" + +namespace sfz { +/** + * @brief Get the note-related gain of the region depending on which note has been + * pressed and at which velocity. + * + * @param region + * @param noteNumber + * @param velocity + * @param midiState + * @param curveSet + * @return float + */ +float getNoteGain(const Region& region, int noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept; + +/** + * @brief Get the additional crossfade gain of the region depending on the + * CC values + * + * @param region + * @param midiState + * @return float + */ +float getCrossfadeGain(const Region& region, const MidiState& midiState) noexcept; + +/** + * @brief Get the base volume of the region depending on which note has been + * pressed to trigger the region. + * + * @param region + * @param midiState + * @param noteNumber + * @return float + */ +float getBaseVolumedB(const Region& region, const MidiState& midiState, int noteNumber) noexcept; + +/** + * @brief Get the region offset in samples + * + * @param region + * @param midiState + * @return uint32_t + */ +uint64_t getOffset(const Region& region, const MidiState& midiState) noexcept; +/** + * @brief Get the region delay in seconds + * + * @param region + * @param midiState + * @return float + */ +float getDelay(const Region& region, const MidiState& midiState) noexcept; +/** + * @brief Get the index of the sample end, either natural end or forced + * loop. + * + * @param region + * @param midiState + * @return uint32_t + */ +uint32_t getSampleEnd(const Region& region, MidiState& midiState) noexcept; + +/** + * @brief Computes the gain value related to the velocity of the note + * + * @return float + */ +float velocityCurve(const Region& region, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept; + +/** + * @brief Returns the start of the loop for a given region + * + * @param region + * @param midiState + * @return uint32_t + */ +uint32_t loopStart(const Region& region, MidiState& midiState) noexcept; + +/** + * @brief Returns the end of the loop for a given region + * + * @param region + * @param midiState + * @return uint32_t + */ +uint32_t loopEnd(const Region& region, MidiState& midiState) noexcept; + +} diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 14f148817..098b16a51 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -16,6 +16,7 @@ #include "LFO.h" #include "MathHelpers.h" #include "ModifierHelpers.h" +#include "RegionStateful.h" #include "TriggerEvent.h" #include "modulations/ModId.h" #include "modulations/ModKey.h" @@ -407,6 +408,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc Resources& resources = impl.resources_; MidiState& midiState = resources.getMidiState(); + CurveSet& curveSet = resources.getCurves(); const Region& region = layer->getRegion(); impl.region_ = ®ion; @@ -474,7 +476,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc } impl.updateLoopInformation(); impl.speedRatio_ = static_cast(impl.currentPromise_->information.sampleRate / impl.sampleRate_); - impl.sourcePosition_ = region.getOffset(midiState); + impl.sourcePosition_ = getOffset(region, midiState); } // do Scala retuning and reconvert the frequency into a 12TET key number @@ -488,10 +490,10 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc impl.pitchRatio_ *= stretch->getRatioForFractionalKey(numberRetuned); impl.pitchKeycenter_ = region.pitchKeycenter; - impl.baseVolumedB_ = region.getBaseVolumedB(midiState, impl.triggerEvent_.number); + impl.baseVolumedB_ = getBaseVolumedB(region, midiState, impl.triggerEvent_.number); impl.baseGain_ = region.getBaseGain(); if (impl.triggerEvent_.type != TriggerEventType::CC || region.velocityOverride == VelocityOverride::previous) - impl.baseGain_ *= region.getNoteGain(impl.triggerEvent_.number, impl.triggerEvent_.value); + impl.baseGain_ *= getNoteGain(region, impl.triggerEvent_.number, impl.triggerEvent_.value, midiState, curveSet); impl.gainSmoother_.reset(); impl.resetCrossfades(); @@ -505,9 +507,9 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc } impl.triggerDelay_ = delay; - impl.initialDelay_ = delay + static_cast(region.getDelay(midiState) * impl.sampleRate_); + impl.initialDelay_ = delay + static_cast(getDelay(region, midiState) * impl.sampleRate_); impl.baseFrequency_ = tuning.getFrequencyOfKey(impl.triggerEvent_.number); - impl.sampleEnd_ = int(region.getSampleEnd(midiState)); + impl.sampleEnd_ = int(getSampleEnd(region, midiState)); impl.sampleSize_ = impl.sampleEnd_- impl.sourcePosition_ - 1; impl.bendSmoother_.setSmoothing(region.bendSmooth, impl.sampleRate_); impl.bendSmoother_.reset(region.getBendInCents(midiState.getPitchBend())); @@ -1724,14 +1726,15 @@ void Voice::Impl::updateLoopInformation() noexcept if (!region_->shouldLoop()) return; + const Region& region = *region_; MidiState& midiState = resources_.getMidiState(); const FileInformation& info = currentPromise_->information; const double rate = info.sampleRate; - loop_.start = static_cast(region_->loopStart(midiState)); - loop_.end = max(static_cast(region_->loopEnd(midiState)), loop_.start); + loop_.start = static_cast(loopStart(region, midiState)); + loop_.end = max(static_cast(loopEnd(region, midiState)), loop_.start); loop_.size = loop_.end + 1 - loop_.start; - loop_.xfSize = static_cast(lroundPositive(region_->loopCrossfade * rate)); + loop_.xfSize = static_cast(lroundPositive(region.loopCrossfade * rate)); // Clamp the crossfade to the part available before the loop starts loop_.xfSize = min(loop_.start, loop_.xfSize); loop_.xfOutStart = loop_.end + 1 - loop_.xfSize; diff --git a/tests/RegionValueComputationsT.cpp b/tests/RegionValueComputationsT.cpp index c7516830e..8d833ccc4 100644 --- a/tests/RegionValueComputationsT.cpp +++ b/tests/RegionValueComputationsT.cpp @@ -6,6 +6,7 @@ #include "sfizz/Defaults.h" #include "sfizz/Region.h" +#include "sfizz/RegionStateful.h" #include "sfizz/MidiState.h" #include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" @@ -19,137 +20,155 @@ constexpr int numRandomTests { 64 }; TEST_CASE("[Region] Crossfade in on key") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "3" }); - REQUIRE(region.getNoteGain(2, 127_norm) == 0.70711_a); - REQUIRE(region.getNoteGain(1, 127_norm) == 0.0_a); - REQUIRE(region.getNoteGain(3, 127_norm) == 1.0_a); + REQUIRE(getNoteGain(region, 2, 127_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(getNoteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 3, 127_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade in on key - 2") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "5" }); - REQUIRE(region.getNoteGain(1, 127_norm) == 0.0_a); - REQUIRE(region.getNoteGain(2, 127_norm) == 0.5_a); - REQUIRE(region.getNoteGain(3, 127_norm) == 0.70711_a); - REQUIRE(region.getNoteGain(4, 127_norm) == 0.86603_a); - REQUIRE(region.getNoteGain(5, 127_norm) == 1.0_a); - REQUIRE(region.getNoteGain(6, 127_norm) == 1.0_a); + REQUIRE(getNoteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 2, 127_norm, midiState, curveSet) == 0.5_a); + REQUIRE(getNoteGain(region, 3, 127_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(getNoteGain(region, 4, 127_norm, midiState, curveSet) == 0.86603_a); + REQUIRE(getNoteGain(region, 5, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 6, 127_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade in on key - gain") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "5" }); region.parseOpcode({ "xf_keycurve", "gain" }); - REQUIRE(region.getNoteGain(1, 127_norm) == 0.0_a); - REQUIRE(region.getNoteGain(2, 127_norm) == 0.25_a); - REQUIRE(region.getNoteGain(3, 127_norm) == 0.5_a); - REQUIRE(region.getNoteGain(4, 127_norm) == 0.75_a); - REQUIRE(region.getNoteGain(5, 127_norm) == 1.0_a); + REQUIRE(getNoteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 2, 127_norm, midiState, curveSet) == 0.25_a); + REQUIRE(getNoteGain(region, 3, 127_norm, midiState, curveSet) == 0.5_a); + REQUIRE(getNoteGain(region, 4, 127_norm, midiState, curveSet) == 0.75_a); + REQUIRE(getNoteGain(region, 5, 127_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade out on key") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_hikey", "55" }); - REQUIRE(region.getNoteGain(50, 127_norm) == 1.0_a); - REQUIRE(region.getNoteGain(51, 127_norm) == 1.0_a); - REQUIRE(region.getNoteGain(52, 127_norm) == 0.86603_a); - REQUIRE(region.getNoteGain(53, 127_norm) == 0.70711_a); - REQUIRE(region.getNoteGain(54, 127_norm) == 0.5_a); - REQUIRE(region.getNoteGain(55, 127_norm) == 0.0_a); - REQUIRE(region.getNoteGain(56, 127_norm) == 0.0_a); + REQUIRE(getNoteGain(region, 50, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 51, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 52, 127_norm, midiState, curveSet) == 0.86603_a); + REQUIRE(getNoteGain(region, 53, 127_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(getNoteGain(region, 54, 127_norm, midiState, curveSet) == 0.5_a); + REQUIRE(getNoteGain(region, 55, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 56, 127_norm, midiState, curveSet) == 0.0_a); } TEST_CASE("[Region] Crossfade out on key - gain") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_hikey", "55" }); region.parseOpcode({ "xf_keycurve", "gain" }); - REQUIRE(region.getNoteGain(50, 127_norm) == 1.0_a); - REQUIRE(region.getNoteGain(51, 127_norm) == 1.0_a); - REQUIRE(region.getNoteGain(52, 127_norm) == 0.75_a); - REQUIRE(region.getNoteGain(53, 127_norm) == 0.5_a); - REQUIRE(region.getNoteGain(54, 127_norm) == 0.25_a); - REQUIRE(region.getNoteGain(55, 127_norm) == 0.0_a); - REQUIRE(region.getNoteGain(56, 127_norm) == 0.0_a); + REQUIRE(getNoteGain(region, 50, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 51, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 52, 127_norm, midiState, curveSet) == 0.75_a); + REQUIRE(getNoteGain(region, 53, 127_norm, midiState, curveSet) == 0.5_a); + REQUIRE(getNoteGain(region, 54, 127_norm, midiState, curveSet) == 0.25_a); + REQUIRE(getNoteGain(region, 55, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 56, 127_norm, midiState, curveSet) == 0.0_a); } TEST_CASE("[Region] Crossfade in on velocity") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lovel", "20" }); region.parseOpcode({ "xfin_hivel", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(region.getNoteGain(1, 19_norm) == 0.0_a); - REQUIRE(region.getNoteGain(1, 20_norm) == 0.0_a); - REQUIRE(region.getNoteGain(2, 21_norm) == 0.5_a); - REQUIRE(region.getNoteGain(3, 22_norm) == 0.70711_a); - REQUIRE(region.getNoteGain(4, 23_norm) == 0.86603_a); - REQUIRE(region.getNoteGain(5, 24_norm) == 1.0_a); - REQUIRE(region.getNoteGain(6, 25_norm) == 1.0_a); + REQUIRE(getNoteGain(region, 1, 19_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 1, 20_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 2, 21_norm, midiState, curveSet) == 0.5_a); + REQUIRE(getNoteGain(region, 3, 22_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(getNoteGain(region, 4, 23_norm, midiState, curveSet) == 0.86603_a); + REQUIRE(getNoteGain(region, 5, 24_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 6, 25_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade in on vel - gain") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lovel", "20" }); region.parseOpcode({ "xfin_hivel", "24" }); region.parseOpcode({ "xf_velcurve", "gain" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(region.getNoteGain(1, 19_norm) == 0.0_a); - REQUIRE(region.getNoteGain(1, 20_norm) == 0.0_a); - REQUIRE(region.getNoteGain(2, 21_norm) == 0.25_a); - REQUIRE(region.getNoteGain(3, 22_norm) == 0.5_a); - REQUIRE(region.getNoteGain(4, 23_norm) == 0.75_a); - REQUIRE(region.getNoteGain(5, 24_norm) == 1.0_a); - REQUIRE(region.getNoteGain(5, 25_norm) == 1.0_a); + REQUIRE(getNoteGain(region, 1, 19_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 1, 20_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 2, 21_norm, midiState, curveSet) == 0.25_a); + REQUIRE(getNoteGain(region, 3, 22_norm, midiState, curveSet) == 0.5_a); + REQUIRE(getNoteGain(region, 4, 23_norm, midiState, curveSet) == 0.75_a); + REQUIRE(getNoteGain(region, 5, 24_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 5, 25_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade out on vel") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lovel", "51" }); region.parseOpcode({ "xfout_hivel", "55" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(region.getNoteGain(5, 50_norm) == 1.0_a); - REQUIRE(region.getNoteGain(5, 51_norm) == 1.0_a); - REQUIRE(region.getNoteGain(5, 52_norm) == 0.86603_a); - REQUIRE(region.getNoteGain(5, 53_norm) == 0.70711_a); - REQUIRE(region.getNoteGain(5, 54_norm) == 0.5_a); - REQUIRE(region.getNoteGain(5, 55_norm) == 0.0_a); - REQUIRE(region.getNoteGain(5, 56_norm) == 0.0_a); + REQUIRE(getNoteGain(region, 5, 50_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 5, 51_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 5, 52_norm, midiState, curveSet) == 0.86603_a); + REQUIRE(getNoteGain(region, 5, 53_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(getNoteGain(region, 5, 54_norm, midiState, curveSet) == 0.5_a); + REQUIRE(getNoteGain(region, 5, 55_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 5, 56_norm, midiState, curveSet) == 0.0_a); } TEST_CASE("[Region] Crossfade out on vel - gain") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lovel", "51" }); region.parseOpcode({ "xfout_hivel", "55" }); region.parseOpcode({ "xf_velcurve", "gain" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(region.getNoteGain(56, 50_norm) == 1.0_a); - REQUIRE(region.getNoteGain(56, 51_norm) == 1.0_a); - REQUIRE(region.getNoteGain(56, 52_norm) == 0.75_a); - REQUIRE(region.getNoteGain(56, 53_norm) == 0.5_a); - REQUIRE(region.getNoteGain(56, 54_norm) == 0.25_a); - REQUIRE(region.getNoteGain(56, 55_norm) == 0.0_a); - REQUIRE(region.getNoteGain(56, 56_norm) == 0.0_a); + REQUIRE(getNoteGain(region, 56, 50_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 56, 51_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 56, 52_norm, midiState, curveSet) == 0.75_a); + REQUIRE(getNoteGain(region, 56, 53_norm, midiState, curveSet) == 0.5_a); + REQUIRE(getNoteGain(region, 56, 54_norm, midiState, curveSet) == 0.25_a); + REQUIRE(getNoteGain(region, 56, 55_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 56, 56_norm, midiState, curveSet) == 0.0_a); } TEST_CASE("[Region] Crossfade in on CC") @@ -161,19 +180,19 @@ TEST_CASE("[Region] Crossfade in on CC") region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); midiState.ccEvent(0, 24, 19_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 20_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 21_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.5_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.5_a); midiState.ccEvent(0, 24, 22_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.70711_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.70711_a); midiState.ccEvent(0, 24, 23_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.86603_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.86603_a); midiState.ccEvent(0, 24, 24_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 1.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 25_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 1.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); } TEST_CASE("[Region] Crossfade in on CC - gain") @@ -186,19 +205,19 @@ TEST_CASE("[Region] Crossfade in on CC - gain") region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); midiState.ccEvent(0, 24, 19_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 20_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 21_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.25_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.25_a); midiState.ccEvent(0, 24, 22_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.5_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.5_a); midiState.ccEvent(0, 24, 23_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.75_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.75_a); midiState.ccEvent(0, 24, 24_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 1.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 25_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 1.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); } TEST_CASE("[Region] Crossfade out on CC") { @@ -209,19 +228,19 @@ TEST_CASE("[Region] Crossfade out on CC") region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); midiState.ccEvent(0, 24, 19_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 1.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 20_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 1.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 21_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.86603_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.86603_a); midiState.ccEvent(0, 24, 22_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.70711_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.70711_a); midiState.ccEvent(0, 24, 23_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.5_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.5_a); midiState.ccEvent(0, 24, 24_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 25_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); } TEST_CASE("[Region] Crossfade out on CC - gain") @@ -234,47 +253,53 @@ TEST_CASE("[Region] Crossfade out on CC - gain") region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); midiState.ccEvent(0, 24, 19_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 1.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 20_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 1.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 21_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.75_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.75_a); midiState.ccEvent(0, 24, 22_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.5_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.5_a); midiState.ccEvent(0, 24, 23_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.25_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.25_a); midiState.ccEvent(0, 24, 24_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 25_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); } TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(region.getNoteGain(64, 127_norm) == 1.0_a); - REQUIRE(region.getNoteGain(64, 0_norm) == 1.0_a); + REQUIRE(getNoteGain(region, 64, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 64, 0_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Velocity bug for extreme values - positive veltrack") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "100" }); - REQUIRE(region.getNoteGain(64, 127_norm) == 1.0_a); - REQUIRE(region.getNoteGain(64, 0_norm) == Approx(0.0).margin(0.0001)); + REQUIRE(getNoteGain(region, 64, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 64, 0_norm, midiState, curveSet) == Approx(0.0).margin(0.0001)); } TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "-100" }); - REQUIRE(region.getNoteGain(64, 127_norm) == Approx(0.0).margin(0.0001)); - REQUIRE(region.getNoteGain(64, 0_norm) == 1.0_a); + REQUIRE(getNoteGain(region, 64, 127_norm, midiState, curveSet) == Approx(0.0).margin(0.0001)); + REQUIRE(getNoteGain(region, 64, 0_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] rt_decay") @@ -287,15 +312,15 @@ TEST_CASE("[Region] rt_decay") region.parseOpcode({ "rt_decay", "10" }); midiState.noteOnEvent(0, 64, 64_norm); midiState.advanceTime(100); - REQUIRE( region.getBaseVolumedB(midiState, 64) == Approx(Default::volume - 1.0f).margin(0.1) ); + REQUIRE( getBaseVolumedB(region, midiState, 64) == Approx(Default::volume - 1.0f).margin(0.1) ); region.parseOpcode({ "rt_decay", "20" }); midiState.noteOnEvent(0, 64, 64_norm); midiState.advanceTime(100); - REQUIRE( region.getBaseVolumedB(midiState, 64) == Approx(Default::volume - 2.0f).margin(0.1) ); + REQUIRE( getBaseVolumedB(region, midiState, 64) == Approx(Default::volume - 2.0f).margin(0.1) ); region.parseOpcode({ "trigger", "attack" }); midiState.noteOnEvent(0, 64, 64_norm); midiState.advanceTime(100); - REQUIRE( region.getBaseVolumedB(midiState, 64) == Approx(Default::volume).margin(0.1) ); + REQUIRE( getBaseVolumedB(region, midiState, 64) == Approx(Default::volume).margin(0.1) ); } TEST_CASE("[Region] Base delay") @@ -304,12 +329,12 @@ TEST_CASE("[Region] Base delay") Region region { 0 }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "delay", "10" }); - REQUIRE( region.getDelay(midiState) == 10.0f ); + REQUIRE( getDelay(region, midiState) == 10.0f ); region.parseOpcode({ "delay_random", "10" }); Random::randomGenerator.seed(42); for (int i = 0; i < numRandomTests; ++i) { - auto delay = region.getDelay(midiState); + auto delay = getDelay(region, midiState); REQUIRE( (delay >= 10.0 && delay <= 20.0) ); } } @@ -321,15 +346,15 @@ TEST_CASE("[Region] Offsets with CCs") region.parseOpcode({ "offset_cc4", "255" }); region.parseOpcode({ "offset", "10" }); - REQUIRE( region.getOffset(midiState) == 10 ); + REQUIRE( getOffset(region, midiState) == 10 ); midiState.ccEvent(0, 4, 127_norm); - REQUIRE( region.getOffset(midiState) == 265 ); + REQUIRE( getOffset(region, midiState) == 265 ); midiState.ccEvent(0, 4, 100_norm); - REQUIRE( region.getOffset(midiState) == 210 ); + REQUIRE( getOffset(region, midiState) == 210 ); midiState.ccEvent(0, 4, 10_norm); - REQUIRE( region.getOffset(midiState) == 30 ); + REQUIRE( getOffset(region, midiState) == 30 ); midiState.ccEvent(0, 4, 0); - REQUIRE( region.getOffset(midiState) == 10 ); + REQUIRE( getOffset(region, midiState) == 10 ); } TEST_CASE("[Region] Pitch variation with veltrack") @@ -344,3 +369,108 @@ TEST_CASE("[Region] Pitch variation with veltrack") REQUIRE(region.getBasePitchVariation(60.0, 64_norm) == Approx(centsFactor(600.0)).margin(0.01f)); REQUIRE(region.getBasePitchVariation(60.0, 127_norm) == Approx(centsFactor(1200.0)).margin(0.01f)); } + +TEST_CASE("[Synth] velcurve") +{ + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; + + struct VelocityData { float velocity, gain; bool exact; }; + static const VelocityData veldata[] = { + { 0_norm, 0.0, true }, + { 32_norm, 0.5f, false }, + { 64_norm, 1.0, true }, + { 96_norm, 1.0, true }, + { 127_norm, 1.0, true }, + }; + + SECTION("Default veltrack") + { + sfz::Region region { 0 }; + region.parseOpcode({ "sample", "*sine" }); + region.parseOpcode({ "amp_velcurve_064", "1" }); + region.velCurve = Curve::buildFromVelcurvePoints( + region.velocityPoints, Curve::Interpolator::Linear); + for (const VelocityData& vd : veldata) { + if (vd.exact) { + REQUIRE(velocityCurve(region, vd.velocity, midiState, curveSet) == vd.gain); + } else { + REQUIRE(velocityCurve(region, vd.velocity, midiState, curveSet) == Approx(vd.gain).margin(1e-2)); + } + } + } + + SECTION("Inverted veltrack") + { + sfz::Region region { 0 }; + region.parseOpcode({ "sample", "*sine" }); + region.parseOpcode({ "amp_velcurve_064", "1" }); + region.parseOpcode({ "amp_veltrack", "-100" }); + region.velCurve = Curve::buildFromVelcurvePoints( + region.velocityPoints, Curve::Interpolator::Linear); + for (const VelocityData& vd : veldata) { + if (vd.exact) { + REQUIRE(velocityCurve(region, vd.velocity, midiState, curveSet) == 1.0f - vd.gain); + } else { + REQUIRE(velocityCurve(region, vd.velocity, midiState, curveSet) == Approx( 1.0f - vd.gain).margin(1e-2)); + } + } + } +} + +TEST_CASE("[Synth] veltrack") +{ + struct VelocityData { float velocity, dBGain; }; + struct VeltrackData { float veltrack; absl::Span veldata; }; + + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; + + // measured on ARIA + const VelocityData veldata25[] = { + { 127_norm, 0.0 }, + { 96_norm, -1 }, + { 64_norm, -1.8 }, + { 32_norm, -2.3 }, + { 1_norm, -2.5 }, + }; + const VelocityData veldata50[] = { + { 127_norm, 0.0 }, + { 96_norm, -2.1 }, + { 64_norm, -4.1 }, + { 32_norm, -5.5 }, + { 1_norm, -6.0 }, + }; + const VelocityData veldata75[] = { + { 127_norm, 0.0 }, + { 96_norm, -3.4 }, + { 64_norm, -7.2 }, + { 32_norm, -10.5 }, + { 1_norm, -12.0 }, + }; + const VelocityData veldata100[] = { + { 127_norm, 0.0 }, + { 96_norm, -4.9 }, + { 64_norm, -12.0 }, + { 32_norm, -24.0 }, + { 1_norm, -84.1 }, + }; + + const VeltrackData veltrackdata[] = { + { 25, absl::MakeConstSpan(veldata25) }, + { 50, absl::MakeConstSpan(veldata50) }, + { 75, absl::MakeConstSpan(veldata75) }, + { 100, absl::MakeConstSpan(veldata100) }, + }; + + for (const VeltrackData& vt : veltrackdata) { + sfz::Region region { 0 }; + region.parseOpcode({ "sample", "*sine" }); + region.parseOpcode({ "amp_veltrack", std::to_string(vt.veltrack) }); + + for (const VelocityData& vd : vt.veldata) { + float dBGain = 20.0f * std::log10(velocityCurve(region, vd.velocity, midiState, curveSet)); + REQUIRE(dBGain == Approx(vd.dBGain).margin(0.1)); + } + } +} diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 639acefa8..8c037cb1a 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -431,98 +431,6 @@ TEST_CASE("[Synth] Velocity points") REQUIRE( synth.getRegionView(1)->velocityPoints[0].second == 1.0_a ); } -TEST_CASE("[Synth] velcurve") -{ - sfz::Synth synth; - synth.loadSfzString(fs::current_path() / "tests/TestFiles/velocity_endpoints.sfz", R"( - amp_velcurve_064=1 sample=*sine - amp_velcurve_064=1 amp_veltrack=-100 sample=*sine - )"); - - struct VelocityData { float velocity, gain; bool exact; }; - - static const VelocityData veldata[] = { - { 0_norm, 0.0, true }, - { 32_norm, 0.5f, false }, - { 64_norm, 1.0, true }, - { 96_norm, 1.0, true }, - { 127_norm, 1.0, true }, - }; - - REQUIRE(synth.getNumRegions() == 2); - const sfz::Region* r1 = synth.getRegionView(0); - const sfz::Region* r2 = synth.getRegionView(1); - - for (const VelocityData& vd : veldata) { - if (vd.exact) { - REQUIRE(r1->velocityCurve(vd.velocity) == vd.gain); - REQUIRE(r2->velocityCurve(vd.velocity) == 1.0f - vd.gain); - } - else { - REQUIRE(r1->velocityCurve(vd.velocity) == Approx(vd.gain).margin(1e-2)); - REQUIRE(r2->velocityCurve(vd.velocity) == Approx(1.0f - vd.gain).margin(1e-2)); - } - } -} - -TEST_CASE("[Synth] veltrack") -{ - struct VelocityData { float velocity, dBGain; }; - struct VeltrackData { float veltrack; absl::Span veldata; }; - - // measured on ARIA - const VelocityData veldata25[] = { - { 127_norm, 0.0 }, - { 96_norm, -1 }, - { 64_norm, -1.8 }, - { 32_norm, -2.3 }, - { 1_norm, -2.5 }, - }; - const VelocityData veldata50[] = { - { 127_norm, 0.0 }, - { 96_norm, -2.1 }, - { 64_norm, -4.1 }, - { 32_norm, -5.5 }, - { 1_norm, -6.0 }, - }; - const VelocityData veldata75[] = { - { 127_norm, 0.0 }, - { 96_norm, -3.4 }, - { 64_norm, -7.2 }, - { 32_norm, -10.5 }, - { 1_norm, -12.0 }, - }; - const VelocityData veldata100[] = { - { 127_norm, 0.0 }, - { 96_norm, -4.9 }, - { 64_norm, -12.0 }, - { 32_norm, -24.0 }, - { 1_norm, -84.1 }, - }; - - const VeltrackData veltrackdata[] = { - { 25, absl::MakeConstSpan(veldata25) }, - { 50, absl::MakeConstSpan(veldata50) }, - { 75, absl::MakeConstSpan(veldata75) }, - { 100, absl::MakeConstSpan(veldata100) }, - }; - - for (const VeltrackData& vt : veltrackdata) { - sfz::Synth synth; - const std::string sfzCode = "sample=*sine amp_veltrack=" + - std::to_string(vt.veltrack); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/veltrack.sfz", sfzCode); - - REQUIRE(synth.getNumRegions() == 1); - const sfz::Region* r = synth.getRegionView(0); - - for (const VelocityData& vd : vt.veldata) { - float dBGain = 20.0f * std::log10(r->velocityCurve(vd.velocity)); - REQUIRE(dBGain == Approx(vd.dBGain).margin(0.1)); - } - } -} - TEST_CASE("[Synth] Region by identifier") { sfz::Synth synth; From f7a56d67bf903e5abd369ba338d4cc0b09c151cc Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 4 Jul 2021 19:36:14 +0200 Subject: [PATCH 118/193] MOD build --- src/sfizz/SfzHelpers.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index 8fce77fad..079835f37 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -25,6 +25,8 @@ using NoteNamePair = std::pair; template struct ModifierCurvePair { + ModifierCurvePair(const T& modifier, uint8_t curve) + : modifier(modifier), curve(curve) {} T modifier {}; uint8_t curve {}; }; From f84cacebf6570ac8bf710e182f3abfe4bd7b5aa9 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 5 Jul 2021 09:12:35 +0200 Subject: [PATCH 119/193] Rename the stateful free functions --- src/sfizz/RegionStateful.cpp | 12 +- src/sfizz/RegionStateful.h | 12 +- src/sfizz/Voice.cpp | 10 +- tests/RegionValueComputationsT.cpp | 200 ++++++++++++++--------------- 4 files changed, 117 insertions(+), 117 deletions(-) diff --git a/src/sfizz/RegionStateful.cpp b/src/sfizz/RegionStateful.cpp index b04a52aeb..90963d7d2 100644 --- a/src/sfizz/RegionStateful.cpp +++ b/src/sfizz/RegionStateful.cpp @@ -9,7 +9,7 @@ namespace sfz { -float getBaseVolumedB(const Region& region, const MidiState& midiState, int noteNumber) noexcept +float baseVolumedB(const Region& region, const MidiState& midiState, int noteNumber) noexcept { fast_real_distribution volumeDistribution { 0.0f, region.ampRandom }; auto baseVolumedB = region.volume + volumeDistribution(Random::randomGenerator); @@ -22,7 +22,7 @@ float getBaseVolumedB(const Region& region, const MidiState& midiState, int note } -uint64_t getOffset(const Region& region, const MidiState& midiState) noexcept +uint64_t sampleOffset(const Region& region, const MidiState& midiState) noexcept { std::uniform_int_distribution offsetDistribution { 0, region.offsetRandom }; uint64_t finalOffset = region.offset + offsetDistribution(Random::randomGenerator); @@ -31,7 +31,7 @@ uint64_t getOffset(const Region& region, const MidiState& midiState) noexcept return Default::offset.bounds.clamp(finalOffset); } -float getDelay(const Region& region, const MidiState& midiState) noexcept +float regionDelay(const Region& region, const MidiState& midiState) noexcept { fast_real_distribution delayDistribution { 0, region.delayRandom }; float finalDelay { region.delay }; @@ -42,7 +42,7 @@ float getDelay(const Region& region, const MidiState& midiState) noexcept return Default::delay.bounds.clamp(finalDelay); } -uint32_t getSampleEnd(const Region& region, MidiState& midiState) noexcept +uint32_t sampleEnd(const Region& region, MidiState& midiState) noexcept { int64_t end = region.sampleEnd; for (const auto& mod: region.endCC) @@ -72,7 +72,7 @@ uint32_t loopEnd(const Region& region, MidiState& midiState) noexcept return static_cast(end); } -float getNoteGain(const Region& region, int noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept +float noteGain(const Region& region, int noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); @@ -95,7 +95,7 @@ float getNoteGain(const Region& region, int noteNumber, float velocity, const Mi return baseGain; } -float getCrossfadeGain(const Region& region, const MidiState& midiState) noexcept +float crossfadeGain(const Region& region, const MidiState& midiState) noexcept { float gain { 1.0f }; diff --git a/src/sfizz/RegionStateful.h b/src/sfizz/RegionStateful.h index 9c2574b3d..aa33b932b 100644 --- a/src/sfizz/RegionStateful.h +++ b/src/sfizz/RegionStateful.h @@ -20,7 +20,7 @@ namespace sfz { * @param curveSet * @return float */ -float getNoteGain(const Region& region, int noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept; +float noteGain(const Region& region, int noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept; /** * @brief Get the additional crossfade gain of the region depending on the @@ -30,7 +30,7 @@ float getNoteGain(const Region& region, int noteNumber, float velocity, const Mi * @param midiState * @return float */ -float getCrossfadeGain(const Region& region, const MidiState& midiState) noexcept; +float crossfadeGain(const Region& region, const MidiState& midiState) noexcept; /** * @brief Get the base volume of the region depending on which note has been @@ -41,7 +41,7 @@ float getCrossfadeGain(const Region& region, const MidiState& midiState) noexcep * @param noteNumber * @return float */ -float getBaseVolumedB(const Region& region, const MidiState& midiState, int noteNumber) noexcept; +float baseVolumedB(const Region& region, const MidiState& midiState, int noteNumber) noexcept; /** * @brief Get the region offset in samples @@ -50,7 +50,7 @@ float getBaseVolumedB(const Region& region, const MidiState& midiState, int note * @param midiState * @return uint32_t */ -uint64_t getOffset(const Region& region, const MidiState& midiState) noexcept; +uint64_t sampleOffset(const Region& region, const MidiState& midiState) noexcept; /** * @brief Get the region delay in seconds * @@ -58,7 +58,7 @@ uint64_t getOffset(const Region& region, const MidiState& midiState) noexcept; * @param midiState * @return float */ -float getDelay(const Region& region, const MidiState& midiState) noexcept; +float regionDelay(const Region& region, const MidiState& midiState) noexcept; /** * @brief Get the index of the sample end, either natural end or forced * loop. @@ -67,7 +67,7 @@ float getDelay(const Region& region, const MidiState& midiState) noexcept; * @param midiState * @return uint32_t */ -uint32_t getSampleEnd(const Region& region, MidiState& midiState) noexcept; +uint32_t sampleEnd(const Region& region, MidiState& midiState) noexcept; /** * @brief Computes the gain value related to the velocity of the note diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 098b16a51..fd7c80a29 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -476,7 +476,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc } impl.updateLoopInformation(); impl.speedRatio_ = static_cast(impl.currentPromise_->information.sampleRate / impl.sampleRate_); - impl.sourcePosition_ = getOffset(region, midiState); + impl.sourcePosition_ = sampleOffset(region, midiState); } // do Scala retuning and reconvert the frequency into a 12TET key number @@ -490,10 +490,10 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc impl.pitchRatio_ *= stretch->getRatioForFractionalKey(numberRetuned); impl.pitchKeycenter_ = region.pitchKeycenter; - impl.baseVolumedB_ = getBaseVolumedB(region, midiState, impl.triggerEvent_.number); + impl.baseVolumedB_ = baseVolumedB(region, midiState, impl.triggerEvent_.number); impl.baseGain_ = region.getBaseGain(); if (impl.triggerEvent_.type != TriggerEventType::CC || region.velocityOverride == VelocityOverride::previous) - impl.baseGain_ *= getNoteGain(region, impl.triggerEvent_.number, impl.triggerEvent_.value, midiState, curveSet); + impl.baseGain_ *= noteGain(region, impl.triggerEvent_.number, impl.triggerEvent_.value, midiState, curveSet); impl.gainSmoother_.reset(); impl.resetCrossfades(); @@ -507,9 +507,9 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc } impl.triggerDelay_ = delay; - impl.initialDelay_ = delay + static_cast(getDelay(region, midiState) * impl.sampleRate_); + impl.initialDelay_ = delay + static_cast(regionDelay(region, midiState) * impl.sampleRate_); impl.baseFrequency_ = tuning.getFrequencyOfKey(impl.triggerEvent_.number); - impl.sampleEnd_ = int(getSampleEnd(region, midiState)); + impl.sampleEnd_ = int(sampleEnd(region, midiState)); impl.sampleSize_ = impl.sampleEnd_- impl.sourcePosition_ - 1; impl.bendSmoother_.setSmoothing(region.bendSmooth, impl.sampleRate_); impl.bendSmoother_.reset(region.getBendInCents(midiState.getPitchBend())); diff --git a/tests/RegionValueComputationsT.cpp b/tests/RegionValueComputationsT.cpp index 8d833ccc4..d468b8c85 100644 --- a/tests/RegionValueComputationsT.cpp +++ b/tests/RegionValueComputationsT.cpp @@ -25,9 +25,9 @@ TEST_CASE("[Region] Crossfade in on key") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "3" }); - REQUIRE(getNoteGain(region, 2, 127_norm, midiState, curveSet) == 0.70711_a); - REQUIRE(getNoteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 3, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 2, 127_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(noteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 3, 127_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade in on key - 2") @@ -38,12 +38,12 @@ TEST_CASE("[Region] Crossfade in on key - 2") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "5" }); - REQUIRE(getNoteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 2, 127_norm, midiState, curveSet) == 0.5_a); - REQUIRE(getNoteGain(region, 3, 127_norm, midiState, curveSet) == 0.70711_a); - REQUIRE(getNoteGain(region, 4, 127_norm, midiState, curveSet) == 0.86603_a); - REQUIRE(getNoteGain(region, 5, 127_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 6, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 2, 127_norm, midiState, curveSet) == 0.5_a); + REQUIRE(noteGain(region, 3, 127_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(noteGain(region, 4, 127_norm, midiState, curveSet) == 0.86603_a); + REQUIRE(noteGain(region, 5, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 6, 127_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade in on key - gain") @@ -55,11 +55,11 @@ TEST_CASE("[Region] Crossfade in on key - gain") region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "5" }); region.parseOpcode({ "xf_keycurve", "gain" }); - REQUIRE(getNoteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 2, 127_norm, midiState, curveSet) == 0.25_a); - REQUIRE(getNoteGain(region, 3, 127_norm, midiState, curveSet) == 0.5_a); - REQUIRE(getNoteGain(region, 4, 127_norm, midiState, curveSet) == 0.75_a); - REQUIRE(getNoteGain(region, 5, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 2, 127_norm, midiState, curveSet) == 0.25_a); + REQUIRE(noteGain(region, 3, 127_norm, midiState, curveSet) == 0.5_a); + REQUIRE(noteGain(region, 4, 127_norm, midiState, curveSet) == 0.75_a); + REQUIRE(noteGain(region, 5, 127_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade out on key") @@ -70,13 +70,13 @@ TEST_CASE("[Region] Crossfade out on key") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_hikey", "55" }); - REQUIRE(getNoteGain(region, 50, 127_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 51, 127_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 52, 127_norm, midiState, curveSet) == 0.86603_a); - REQUIRE(getNoteGain(region, 53, 127_norm, midiState, curveSet) == 0.70711_a); - REQUIRE(getNoteGain(region, 54, 127_norm, midiState, curveSet) == 0.5_a); - REQUIRE(getNoteGain(region, 55, 127_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 56, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 50, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 51, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 52, 127_norm, midiState, curveSet) == 0.86603_a); + REQUIRE(noteGain(region, 53, 127_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(noteGain(region, 54, 127_norm, midiState, curveSet) == 0.5_a); + REQUIRE(noteGain(region, 55, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 56, 127_norm, midiState, curveSet) == 0.0_a); } TEST_CASE("[Region] Crossfade out on key - gain") @@ -88,13 +88,13 @@ TEST_CASE("[Region] Crossfade out on key - gain") region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_hikey", "55" }); region.parseOpcode({ "xf_keycurve", "gain" }); - REQUIRE(getNoteGain(region, 50, 127_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 51, 127_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 52, 127_norm, midiState, curveSet) == 0.75_a); - REQUIRE(getNoteGain(region, 53, 127_norm, midiState, curveSet) == 0.5_a); - REQUIRE(getNoteGain(region, 54, 127_norm, midiState, curveSet) == 0.25_a); - REQUIRE(getNoteGain(region, 55, 127_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 56, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 50, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 51, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 52, 127_norm, midiState, curveSet) == 0.75_a); + REQUIRE(noteGain(region, 53, 127_norm, midiState, curveSet) == 0.5_a); + REQUIRE(noteGain(region, 54, 127_norm, midiState, curveSet) == 0.25_a); + REQUIRE(noteGain(region, 55, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 56, 127_norm, midiState, curveSet) == 0.0_a); } TEST_CASE("[Region] Crossfade in on velocity") @@ -106,13 +106,13 @@ TEST_CASE("[Region] Crossfade in on velocity") region.parseOpcode({ "xfin_lovel", "20" }); region.parseOpcode({ "xfin_hivel", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(getNoteGain(region, 1, 19_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 1, 20_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 2, 21_norm, midiState, curveSet) == 0.5_a); - REQUIRE(getNoteGain(region, 3, 22_norm, midiState, curveSet) == 0.70711_a); - REQUIRE(getNoteGain(region, 4, 23_norm, midiState, curveSet) == 0.86603_a); - REQUIRE(getNoteGain(region, 5, 24_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 6, 25_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 1, 19_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 1, 20_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 2, 21_norm, midiState, curveSet) == 0.5_a); + REQUIRE(noteGain(region, 3, 22_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(noteGain(region, 4, 23_norm, midiState, curveSet) == 0.86603_a); + REQUIRE(noteGain(region, 5, 24_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 6, 25_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade in on vel - gain") @@ -125,13 +125,13 @@ TEST_CASE("[Region] Crossfade in on vel - gain") region.parseOpcode({ "xfin_hivel", "24" }); region.parseOpcode({ "xf_velcurve", "gain" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(getNoteGain(region, 1, 19_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 1, 20_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 2, 21_norm, midiState, curveSet) == 0.25_a); - REQUIRE(getNoteGain(region, 3, 22_norm, midiState, curveSet) == 0.5_a); - REQUIRE(getNoteGain(region, 4, 23_norm, midiState, curveSet) == 0.75_a); - REQUIRE(getNoteGain(region, 5, 24_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 5, 25_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 1, 19_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 1, 20_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 2, 21_norm, midiState, curveSet) == 0.25_a); + REQUIRE(noteGain(region, 3, 22_norm, midiState, curveSet) == 0.5_a); + REQUIRE(noteGain(region, 4, 23_norm, midiState, curveSet) == 0.75_a); + REQUIRE(noteGain(region, 5, 24_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 5, 25_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade out on vel") @@ -143,13 +143,13 @@ TEST_CASE("[Region] Crossfade out on vel") region.parseOpcode({ "xfout_lovel", "51" }); region.parseOpcode({ "xfout_hivel", "55" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(getNoteGain(region, 5, 50_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 5, 51_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 5, 52_norm, midiState, curveSet) == 0.86603_a); - REQUIRE(getNoteGain(region, 5, 53_norm, midiState, curveSet) == 0.70711_a); - REQUIRE(getNoteGain(region, 5, 54_norm, midiState, curveSet) == 0.5_a); - REQUIRE(getNoteGain(region, 5, 55_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 5, 56_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 5, 50_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 5, 51_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 5, 52_norm, midiState, curveSet) == 0.86603_a); + REQUIRE(noteGain(region, 5, 53_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(noteGain(region, 5, 54_norm, midiState, curveSet) == 0.5_a); + REQUIRE(noteGain(region, 5, 55_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 5, 56_norm, midiState, curveSet) == 0.0_a); } TEST_CASE("[Region] Crossfade out on vel - gain") @@ -162,13 +162,13 @@ TEST_CASE("[Region] Crossfade out on vel - gain") region.parseOpcode({ "xfout_hivel", "55" }); region.parseOpcode({ "xf_velcurve", "gain" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(getNoteGain(region, 56, 50_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 56, 51_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 56, 52_norm, midiState, curveSet) == 0.75_a); - REQUIRE(getNoteGain(region, 56, 53_norm, midiState, curveSet) == 0.5_a); - REQUIRE(getNoteGain(region, 56, 54_norm, midiState, curveSet) == 0.25_a); - REQUIRE(getNoteGain(region, 56, 55_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 56, 56_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 56, 50_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 56, 51_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 56, 52_norm, midiState, curveSet) == 0.75_a); + REQUIRE(noteGain(region, 56, 53_norm, midiState, curveSet) == 0.5_a); + REQUIRE(noteGain(region, 56, 54_norm, midiState, curveSet) == 0.25_a); + REQUIRE(noteGain(region, 56, 55_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 56, 56_norm, midiState, curveSet) == 0.0_a); } TEST_CASE("[Region] Crossfade in on CC") @@ -180,19 +180,19 @@ TEST_CASE("[Region] Crossfade in on CC") region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); midiState.ccEvent(0, 24, 19_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); + REQUIRE(crossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 20_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); + REQUIRE(crossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 21_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.5_a); + REQUIRE(crossfadeGain(region, midiState) == 0.5_a); midiState.ccEvent(0, 24, 22_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.70711_a); + REQUIRE(crossfadeGain(region, midiState) == 0.70711_a); midiState.ccEvent(0, 24, 23_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.86603_a); + REQUIRE(crossfadeGain(region, midiState) == 0.86603_a); midiState.ccEvent(0, 24, 24_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); + REQUIRE(crossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 25_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); + REQUIRE(crossfadeGain(region, midiState) == 1.0_a); } TEST_CASE("[Region] Crossfade in on CC - gain") @@ -205,19 +205,19 @@ TEST_CASE("[Region] Crossfade in on CC - gain") region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); midiState.ccEvent(0, 24, 19_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); + REQUIRE(crossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 20_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); + REQUIRE(crossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 21_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.25_a); + REQUIRE(crossfadeGain(region, midiState) == 0.25_a); midiState.ccEvent(0, 24, 22_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.5_a); + REQUIRE(crossfadeGain(region, midiState) == 0.5_a); midiState.ccEvent(0, 24, 23_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.75_a); + REQUIRE(crossfadeGain(region, midiState) == 0.75_a); midiState.ccEvent(0, 24, 24_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); + REQUIRE(crossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 25_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); + REQUIRE(crossfadeGain(region, midiState) == 1.0_a); } TEST_CASE("[Region] Crossfade out on CC") { @@ -228,19 +228,19 @@ TEST_CASE("[Region] Crossfade out on CC") region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); midiState.ccEvent(0, 24, 19_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); + REQUIRE(crossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 20_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); + REQUIRE(crossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 21_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.86603_a); + REQUIRE(crossfadeGain(region, midiState) == 0.86603_a); midiState.ccEvent(0, 24, 22_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.70711_a); + REQUIRE(crossfadeGain(region, midiState) == 0.70711_a); midiState.ccEvent(0, 24, 23_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.5_a); + REQUIRE(crossfadeGain(region, midiState) == 0.5_a); midiState.ccEvent(0, 24, 24_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); + REQUIRE(crossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 25_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); + REQUIRE(crossfadeGain(region, midiState) == 0.0_a); } TEST_CASE("[Region] Crossfade out on CC - gain") @@ -253,19 +253,19 @@ TEST_CASE("[Region] Crossfade out on CC - gain") region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); midiState.ccEvent(0, 24, 19_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); + REQUIRE(crossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 20_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); + REQUIRE(crossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 21_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.75_a); + REQUIRE(crossfadeGain(region, midiState) == 0.75_a); midiState.ccEvent(0, 24, 22_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.5_a); + REQUIRE(crossfadeGain(region, midiState) == 0.5_a); midiState.ccEvent(0, 24, 23_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.25_a); + REQUIRE(crossfadeGain(region, midiState) == 0.25_a); midiState.ccEvent(0, 24, 24_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); + REQUIRE(crossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 25_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); + REQUIRE(crossfadeGain(region, midiState) == 0.0_a); } TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0") @@ -275,8 +275,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0") CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(getNoteGain(region, 64, 127_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 64, 0_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 64, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 64, 0_norm, midiState, curveSet) == 1.0_a); } @@ -287,8 +287,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - positive veltrack") CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "100" }); - REQUIRE(getNoteGain(region, 64, 127_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 64, 0_norm, midiState, curveSet) == Approx(0.0).margin(0.0001)); + REQUIRE(noteGain(region, 64, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 64, 0_norm, midiState, curveSet) == Approx(0.0).margin(0.0001)); } TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack") @@ -298,8 +298,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack") CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "-100" }); - REQUIRE(getNoteGain(region, 64, 127_norm, midiState, curveSet) == Approx(0.0).margin(0.0001)); - REQUIRE(getNoteGain(region, 64, 0_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 64, 127_norm, midiState, curveSet) == Approx(0.0).margin(0.0001)); + REQUIRE(noteGain(region, 64, 0_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] rt_decay") @@ -312,15 +312,15 @@ TEST_CASE("[Region] rt_decay") region.parseOpcode({ "rt_decay", "10" }); midiState.noteOnEvent(0, 64, 64_norm); midiState.advanceTime(100); - REQUIRE( getBaseVolumedB(region, midiState, 64) == Approx(Default::volume - 1.0f).margin(0.1) ); + REQUIRE( baseVolumedB(region, midiState, 64) == Approx(Default::volume - 1.0f).margin(0.1) ); region.parseOpcode({ "rt_decay", "20" }); midiState.noteOnEvent(0, 64, 64_norm); midiState.advanceTime(100); - REQUIRE( getBaseVolumedB(region, midiState, 64) == Approx(Default::volume - 2.0f).margin(0.1) ); + REQUIRE( baseVolumedB(region, midiState, 64) == Approx(Default::volume - 2.0f).margin(0.1) ); region.parseOpcode({ "trigger", "attack" }); midiState.noteOnEvent(0, 64, 64_norm); midiState.advanceTime(100); - REQUIRE( getBaseVolumedB(region, midiState, 64) == Approx(Default::volume).margin(0.1) ); + REQUIRE( baseVolumedB(region, midiState, 64) == Approx(Default::volume).margin(0.1) ); } TEST_CASE("[Region] Base delay") @@ -329,12 +329,12 @@ TEST_CASE("[Region] Base delay") Region region { 0 }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "delay", "10" }); - REQUIRE( getDelay(region, midiState) == 10.0f ); + REQUIRE( regionDelay(region, midiState) == 10.0f ); region.parseOpcode({ "delay_random", "10" }); Random::randomGenerator.seed(42); for (int i = 0; i < numRandomTests; ++i) { - auto delay = getDelay(region, midiState); + auto delay = regionDelay(region, midiState); REQUIRE( (delay >= 10.0 && delay <= 20.0) ); } } @@ -346,15 +346,15 @@ TEST_CASE("[Region] Offsets with CCs") region.parseOpcode({ "offset_cc4", "255" }); region.parseOpcode({ "offset", "10" }); - REQUIRE( getOffset(region, midiState) == 10 ); + REQUIRE( sampleOffset(region, midiState) == 10 ); midiState.ccEvent(0, 4, 127_norm); - REQUIRE( getOffset(region, midiState) == 265 ); + REQUIRE( sampleOffset(region, midiState) == 265 ); midiState.ccEvent(0, 4, 100_norm); - REQUIRE( getOffset(region, midiState) == 210 ); + REQUIRE( sampleOffset(region, midiState) == 210 ); midiState.ccEvent(0, 4, 10_norm); - REQUIRE( getOffset(region, midiState) == 30 ); + REQUIRE( sampleOffset(region, midiState) == 30 ); midiState.ccEvent(0, 4, 0); - REQUIRE( getOffset(region, midiState) == 10 ); + REQUIRE( sampleOffset(region, midiState) == 10 ); } TEST_CASE("[Region] Pitch variation with veltrack") From 3658de4497f5f0f1717faf94c5d38fd65394c6d8 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 5 Jul 2021 09:22:07 +0200 Subject: [PATCH 120/193] Add amp and pitch veltrack oncc --- src/sfizz/Region.cpp | 13 ------------ src/sfizz/Region.h | 9 -------- src/sfizz/RegionStateful.cpp | 34 ++++++++++++++++++++++++++++-- src/sfizz/RegionStateful.h | 13 ++++++++++++ src/sfizz/Voice.cpp | 2 +- tests/RegionValueComputationsT.cpp | 14 ++++++------ 6 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 40357c711..07b327fa0 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -1724,19 +1724,6 @@ bool sfz::Region::processGenericCc(const Opcode& opcode, OpcodeSpec spec, return true; } -float sfz::Region::getBasePitchVariation(float noteNumber, float velocity) const noexcept -{ - ASSERT(velocity >= 0.0f && velocity <= 1.0f); - - fast_real_distribution pitchDistribution { 0.0f, pitchRandom }; - float pitchVariationInCents = pitchKeytrack * (noteNumber - float(pitchKeycenter)); // note difference with pitch center - pitchVariationInCents += pitch; // sample tuning - pitchVariationInCents += config::centPerSemitone * transpose; // sample transpose - pitchVariationInCents += velocity * pitchVeltrack; // track velocity - pitchVariationInCents += pitchDistribution(Random::randomGenerator); // random pitch changes - return centsFactor(pitchVariationInCents); -} - float sfz::Region::getBaseGain() const noexcept { float baseGain = amplitude; diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index ab1e76d8f..d0abf16d8 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -99,15 +99,6 @@ struct Region { */ bool shouldLoop() const noexcept { return (loopMode == LoopMode::loop_continuous || loopMode == LoopMode::loop_sustain); } - /** - * @brief Get the base pitch of the region depending on which note has been - * pressed and at which velocity. - * - * @param noteNumber - * @param velocity - * @return float - */ - float getBasePitchVariation(float noteNumber, float velocity) const noexcept; /** * @brief Get the base gain of the region. * diff --git a/src/sfizz/RegionStateful.cpp b/src/sfizz/RegionStateful.cpp index 90963d7d2..e0f7bf865 100644 --- a/src/sfizz/RegionStateful.cpp +++ b/src/sfizz/RegionStateful.cpp @@ -125,10 +125,40 @@ float velocityCurve(const Region& region, float velocity, const MidiState& midiS else gain = velocity * velocity; - gain = std::fabs(region.ampVeltrack) * (1.0f - gain); - gain = (region.ampVeltrack < 0) ? gain : (1.0f - gain); + float veltrack = region.ampVeltrack; + + for (const auto& mod : region.ampVeltrackCC) { + const auto& curve = curveSet.getCurve(mod.data.curve); + const float value = midiState.getCCValue(mod.cc); + veltrack += curve.evalNormalized(value) * mod.data.modifier; + } + + gain = std::fabs(veltrack) * (1.0f - gain); + gain = (veltrack < 0) ? gain : (1.0f - gain); return gain; } +float basePitchVariation(const Region& region, float noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept +{ + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + + fast_real_distribution pitchDistribution { 0.0f, region.pitchRandom }; + float pitchVariationInCents = region.pitchKeytrack * (noteNumber - float(region.pitchKeycenter)); // note difference with pitch center + pitchVariationInCents += region.pitch; // sample tuning + pitchVariationInCents += config::centPerSemitone * region.transpose; // sample transpose + + float veltrack = region.pitchVeltrack; + + for (const auto& mod : region.pitchVeltrackCC) { + const auto& curve = curveSet.getCurve(mod.data.curve); + const float value = midiState.getCCValue(mod.cc); + veltrack += curve.evalNormalized(value) * mod.data.modifier; + } + + pitchVariationInCents += velocity * veltrack; // track velocity + pitchVariationInCents += pitchDistribution(Random::randomGenerator); // random pitch changes + return centsFactor(pitchVariationInCents); +} + } diff --git a/src/sfizz/RegionStateful.h b/src/sfizz/RegionStateful.h index aa33b932b..830312653 100644 --- a/src/sfizz/RegionStateful.h +++ b/src/sfizz/RegionStateful.h @@ -94,4 +94,17 @@ uint32_t loopStart(const Region& region, MidiState& midiState) noexcept; */ uint32_t loopEnd(const Region& region, MidiState& midiState) noexcept; +/** + * @brief Get the base pitch of the region depending on which note has been + * pressed and at which velocity. + * + * @param region + * @param noteNumber + * @param velocity + * @param midiState + * @param curveSet + * @return float + */ +float basePitchVariation(const Region& region, float noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept; + } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index fd7c80a29..7841cacdc 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -483,7 +483,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc Tuning& tuning = resources.getTuning(); const float numberRetuned = tuning.getKeyFractional12TET(impl.triggerEvent_.number); - impl.pitchRatio_ = region.getBasePitchVariation(numberRetuned, impl.triggerEvent_.value); + impl.pitchRatio_ = basePitchVariation(region, numberRetuned, impl.triggerEvent_.value, midiState, curveSet); // apply stretch tuning if set if (absl::optional& stretch = resources.getStretch()) diff --git a/tests/RegionValueComputationsT.cpp b/tests/RegionValueComputationsT.cpp index d468b8c85..30eef25b6 100644 --- a/tests/RegionValueComputationsT.cpp +++ b/tests/RegionValueComputationsT.cpp @@ -360,14 +360,16 @@ TEST_CASE("[Region] Offsets with CCs") TEST_CASE("[Region] Pitch variation with veltrack") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; - REQUIRE(region.getBasePitchVariation(60.0, 0_norm) == 1.0); - REQUIRE(region.getBasePitchVariation(60.0, 64_norm) == 1.0); - REQUIRE(region.getBasePitchVariation(60.0, 127_norm) == 1.0); + REQUIRE(basePitchVariation(region, 60.0, 0_norm, midiState, curveSet) == 1.0); + REQUIRE(basePitchVariation(region, 60.0, 64_norm, midiState, curveSet) == 1.0); + REQUIRE(basePitchVariation(region, 60.0, 127_norm, midiState, curveSet) == 1.0); region.parseOpcode({ "pitch_veltrack", "1200" }); - REQUIRE(region.getBasePitchVariation(60.0, 0_norm) == 1.0); - REQUIRE(region.getBasePitchVariation(60.0, 64_norm) == Approx(centsFactor(600.0)).margin(0.01f)); - REQUIRE(region.getBasePitchVariation(60.0, 127_norm) == Approx(centsFactor(1200.0)).margin(0.01f)); + REQUIRE(basePitchVariation(region, 60.0, 0_norm, midiState, curveSet) == 1.0); + REQUIRE(basePitchVariation(region, 60.0, 64_norm, midiState, curveSet) == Approx(centsFactor(600.0)).margin(0.01f)); + REQUIRE(basePitchVariation(region, 60.0, 127_norm, midiState, curveSet) == Approx(centsFactor(1200.0)).margin(0.01f)); } TEST_CASE("[Synth] velcurve") From 5dbeb58eb084ad1b88dcf67591a745b7c90824b5 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 5 Jul 2021 09:24:39 +0200 Subject: [PATCH 121/193] Add filter veltrack oncc --- src/sfizz/FilterPool.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/sfizz/FilterPool.cpp b/src/sfizz/FilterPool.cpp index 88b1802f5..8fbd356d5 100644 --- a/src/sfizz/FilterPool.cpp +++ b/src/sfizz/FilterPool.cpp @@ -38,8 +38,15 @@ void sfz::FilterHolder::setup(const Region& region, unsigned filterId, int noteN } const auto keytrack = description->keytrack * float(noteNumber - description->keycenter); baseCutoff *= centsFactor(keytrack); - const auto veltrack = description->veltrack * velocity; - baseCutoff *= centsFactor(veltrack); + auto veltrack = description->veltrack; + + for (const auto& mod : description->veltrackCC) { + const auto& curve = resources.getCurves().getCurve(mod.data.curve); + const float value = resources.getMidiState().getCCValue(mod.cc); + veltrack += curve.evalNormalized(value) * mod.data.modifier; + } + + baseCutoff *= centsFactor(veltrack * velocity); baseCutoff = Default::filterCutoff.bounds.clamp(baseCutoff); baseGain = description->gain; From 559464684a6a8125ad84717dc64163c824046cec Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 5 Jul 2021 11:31:24 +0200 Subject: [PATCH 122/193] CC Triggers also considers sequences --- src/sfizz/Layer.cpp | 14 ++++++++------ src/sfizz/Layer.h | 2 +- src/sfizz/Synth.cpp | 4 ++-- tests/SynthT.cpp | 26 ++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/sfizz/Layer.cpp b/src/sfizz/Layer.cpp index 146d21816..a90261da3 100644 --- a/src/sfizz/Layer.cpp +++ b/src/sfizz/Layer.cpp @@ -129,7 +129,7 @@ bool Layer::registerNoteOff(int noteNumber, float velocity, float randValue) noe return false; } -bool Layer::registerCC(int ccNumber, float ccValue) noexcept +bool Layer::registerCC(int ccNumber, float ccValue, bool dontTrigger) noexcept { const Region& region = region_; @@ -152,14 +152,16 @@ bool Layer::registerCC(int ccNumber, float ccValue) noexcept else ccSwitched_.set(ccNumber, false); - if (!isSwitchedOn()) - return false; - - if (!region.triggerOnCC) + if (dontTrigger || !region.triggerOnCC) return false; if (auto triggerRange = region.ccTriggers.get(ccNumber)) { - if (triggerRange->containsWithEnd(ccValue)) + if (!triggerRange->containsWithEnd(ccValue)) + return false; + + sequenceSwitched_ = + ((sequenceCounter_++ % region.sequenceLength) == region.sequencePosition - 1); + if (isSwitchedOn()) return true; } diff --git a/src/sfizz/Layer.h b/src/sfizz/Layer.h index ede1490a1..ec7a78063 100644 --- a/src/sfizz/Layer.h +++ b/src/sfizz/Layer.h @@ -88,7 +88,7 @@ struct Layer { * @return true if the region should trigger on this event * @return false */ - bool registerCC(int ccNumber, float ccValue) noexcept; + bool registerCC(int ccNumber, float ccValue, bool dontTrigger = false) noexcept; /** * @brief Register a new pitch wheel event. * diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index d02271c54..7a9930903 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -726,7 +726,7 @@ void Synth::Impl::finalizeSfzLoad() // Defaults MidiState& midiState = resources_.getMidiState(); for (int cc = 0; cc < config::numCCs; cc++) { - layer.registerCC(cc, midiState.getCCValue(cc)); + layer.registerCC(cc, midiState.getCCValue(cc), true); } @@ -1951,7 +1951,7 @@ void Synth::Impl::resetAllControllers(int delay) noexcept for (const LayerPtr& layerPtr : layers_) { Layer& layer = *layerPtr; for (int cc = 0; cc < config::numCCs; ++cc) - layer.registerCC(cc, defaultCCValues_[cc]); + layer.registerCC(cc, defaultCCValues_[cc], true); } } diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 639acefa8..0905ae0be 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -1960,3 +1960,29 @@ TEST_CASE("[Synth] Resets all controllers to default values") REQUIRE( synth.getHdcc(56) == 64_norm ); REQUIRE( synth.getHdcc(78) == 0.0f ); } + +TEST_CASE("[Synth] Sequences also work on cc triggers") +{ + sfz::Synth synth; + std::vector messageList; + sfz::Client client(&messageList); + client.setReceiveCallback(&simpleMessageReceiver); + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/sequence_cc_triggers.sfz", R"( + seq_length=3 + sample=*sine hikey=-1 start_locc61=0 start_hicc61=64 seq_position=1 + sample=*saw hikey=-1 start_locc61=0 start_hicc61=64 seq_position=2 + )"); + synth.cc(0, 61, 10); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine" } ); + synth.cc(0, 61, 20); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine", "*saw" } ); + synth.cc(0, 61, 20); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine", "*saw" } ); + synth.cc(0, 61, 20); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine", "*saw", "*sine" } ); +} From 5fc3db6ad4aa6132627925d43a688f0bba996b12 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 5 Jul 2021 12:06:15 +0200 Subject: [PATCH 123/193] Passing lokey=-1 does not reactivate the note trigger --- src/sfizz/Region.cpp | 8 ++++++-- tests/RegionValuesT.cpp | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 344d7c6a4..d3f635d64 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -208,8 +208,12 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode, bool cleanOpcode) break; // Region logic: key mapping case hash("lokey"): - triggerOnNote = true; - keyRange.setStart(opcode.read(Default::loKey)); + { + absl::optional optValue = opcode.readOptional(Default::loKey); + triggerOnNote = optValue != absl::nullopt; + uint8_t value = optValue.value_or(Default::loKey); + keyRange.setStart(value); + } break; case hash("hikey"): { diff --git a/tests/RegionValuesT.cpp b/tests/RegionValuesT.cpp index a6fc4dd7e..40bdabd09 100644 --- a/tests/RegionValuesT.cpp +++ b/tests/RegionValuesT.cpp @@ -566,17 +566,20 @@ TEST_CASE("[Values] Triggers on note") sample=kick.wav hikey=-1 sample=kick.wav key=-1 sample=kick.wav hikey=-1 lokey=12 + sample=kick.wav hikey=-1 lokey=-1 )"); synth.dispatchMessage(client, 0, "/region0/trigger_on_note", "", nullptr); synth.dispatchMessage(client, 0, "/region1/trigger_on_note", "", nullptr); synth.dispatchMessage(client, 0, "/region2/trigger_on_note", "", nullptr); // TODO: Double check with Sforzando/rgc synth.dispatchMessage(client, 0, "/region3/trigger_on_note", "", nullptr); + synth.dispatchMessage(client, 0, "/region4/trigger_on_note", "", nullptr); std::vector expected { "/region0/trigger_on_note,T : { }", "/region1/trigger_on_note,F : { }", "/region2/trigger_on_note,F : { }", "/region3/trigger_on_note,T : { }", + "/region4/trigger_on_note,F : { }", }; REQUIRE(messageList == expected); } From 148fe445c4d8287c03d16596629a188224913c18 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 5 Jul 2021 23:57:09 +0200 Subject: [PATCH 124/193] Don't include the VST for PPC --- plugins/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index d88dcd717..d3f1070e4 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -51,7 +51,7 @@ if(SFIZZ_LV2) add_subdirectory(lv2) endif() -if(SFIZZ_VST OR SFIZZ_AU OR SFIZZ_VST2) +if((SFIZZ_VST OR SFIZZ_AU OR SFIZZ_VST2) AND NOT (SFIZZ_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc).*")) add_subdirectory(vst) endif() From 81f88c60cf8abe08069de7557ca49c7fec42deb1 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 6 Jul 2021 00:14:48 +0200 Subject: [PATCH 125/193] Rewrite the LGPL warning regarding libsndfile --- plugins/lv2/CMakeLists.txt | 3 - plugins/lv2/LICENSE.md.in | 21 ++--- plugins/lv2/lgpl-3.0.txt | 165 ------------------------------------- 3 files changed, 7 insertions(+), 182 deletions(-) delete mode 100644 plugins/lv2/lgpl-3.0.txt diff --git a/plugins/lv2/CMakeLists.txt b/plugins/lv2/CMakeLists.txt index 18305a325..a8d97bb52 100644 --- a/plugins/lv2/CMakeLists.txt +++ b/plugins/lv2/CMakeLists.txt @@ -89,9 +89,6 @@ if(SFIZZ_LV2_UI) configure_file(${PROJECT_NAME}_ui.ttl.in ${PROJECT_BINARY_DIR}/${PROJECT_NAME}_ui.ttl) endif() configure_file(LICENSE.md.in ${PROJECT_BINARY_DIR}/LICENSE.md) -if(SFIZZ_USE_VCPKG OR SFIZZ_STATIC_DEPENDENCIES OR CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - file(COPY "lgpl-3.0.txt" DESTINATION ${PROJECT_BINARY_DIR}) -endif() # Generate controllers.ttl sfizz_lv2_generate_controllers_ttl("${PROJECT_BINARY_DIR}/controllers.ttl") diff --git a/plugins/lv2/LICENSE.md.in b/plugins/lv2/LICENSE.md.in index bae47b17b..1d0b134e9 100644 --- a/plugins/lv2/LICENSE.md.in +++ b/plugins/lv2/LICENSE.md.in @@ -1,19 +1,12 @@ -The `sfizz` LV2 plugin is able to be built statically against all its components, -which is a desirable property for audio plugin hosts. These components include -`libsndfile` from Erik de Castro Lopo (http://www.mega-nerd.com/libsndfile). -`libsndfile` is distributed under the terms of the LGPL v2 or v3. As such, -statically-linked LV2 version of `sfizz` are to be distributed under the terms -of the LGPL-v3.0 license. You should be able to rebuild from source the sfizz LV2 -plugin using any `libsndfile` version you wish to use. - -See the file `lgpl-3.0.txt` or (https://www.gnu.org/licenses/lgpl-3.0.txt) for more -information about the LGPL v3 license. - Most of the code in the plug comes from various examples in the LV2 distribution and example plugins. As such, the source code of the LV2 plugin version of `sfizz` is distributed under the same terms as the LV2 specification, which is the ISC license. -Binaries of this LV2 plugin dynamically linked against e.g. a system installation -of `libsndfile` thus have to respect the terms of the ISC license and BSD 2-clause -license. Check the LICENSE.md at (@LV2PLUGIN_REPOSITORY@) file for more +Check the LICENSE.md at (@LV2PLUGIN_REPOSITORY@) file for more information about the license of the source code of the `sfizz` library and its contributors. + +The `sfizz` LV2 plugin can be optionally statically built against `libsndfile` +from Erik de Castro Lopo (http://www.mega-nerd.com/libsndfile). `libsndfile` +is distributed under the terms of the LGPL v2 or v3. If you distribute a +statically-linked LV2 version of `sfizz` built against `libsndfile`, you need +to respect the terms of the LGPL v2 or v3 license. diff --git a/plugins/lv2/lgpl-3.0.txt b/plugins/lv2/lgpl-3.0.txt deleted file mode 100644 index 0a041280b..000000000 --- a/plugins/lv2/lgpl-3.0.txt +++ /dev/null @@ -1,165 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. From fe744b2823739ea5c3a0ae24be25fea2e8cd5e45 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 6 Jul 2021 00:26:43 +0200 Subject: [PATCH 126/193] Remove the LGPL file from the installer --- scripts/innosetup.iss.in | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/innosetup.iss.in b/scripts/innosetup.iss.in index 268bfa632..ae23ab8c7 100644 --- a/scripts/innosetup.iss.in +++ b/scripts/innosetup.iss.in @@ -55,7 +55,6 @@ Source: "sfizz.lv2\Contents\Resources\*"; Components: lv2; DestDir: "{commoncf}\ Source: "sfizz.lv2\manifest.ttl"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2" Source: "sfizz.lv2\sfizz.ttl"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2" Source: "sfizz.lv2\sfizz_ui.ttl"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2" -Source: "sfizz.lv2\lgpl-3.0.txt"; Components: main; DestDir: "{app}" Source: "sfizz.lv2\LICENSE.md"; Components: main; DestDir: "{app}" Source: "sfizz.vst3\desktop.ini"; Components: vst3; DestDir: "{commoncf}\VST3\sfizz.vst3" Source: "sfizz.vst3\Contents\@VST3_PACKAGE_ARCHITECTURE@-win\sfizz.vst3"; Components: vst3; DestDir: "{commoncf}\VST3\sfizz.vst3\Contents\@VST3_PACKAGE_ARCHITECTURE@-win"; Flags: ignoreversion From 07260b134f1181d9ac83df05675249c665b92d3c Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 6 Jul 2021 17:24:21 +0200 Subject: [PATCH 127/193] Add missing include in devtools/Preprocessor --- devtools/Preprocessor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/devtools/Preprocessor.cpp b/devtools/Preprocessor.cpp index 43ca80471..a053d4341 100644 --- a/devtools/Preprocessor.cpp +++ b/devtools/Preprocessor.cpp @@ -13,6 +13,7 @@ */ #include "parser/Parser.h" +#include "parser/ParserListener.h" #include #include #include From d1d2f9a36b3494bbb3f5c11bec5731a6a72398c9 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 10 Jul 2021 01:46:46 +0200 Subject: [PATCH 128/193] Don't draw a white keyboard if all keys are used --- plugins/editor/src/editor/GUIPiano.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/plugins/editor/src/editor/GUIPiano.cpp b/plugins/editor/src/editor/GUIPiano.cpp index 4091ef602..9ecc86b7e 100644 --- a/plugins/editor/src/editor/GUIPiano.cpp +++ b/plugins/editor/src/editor/GUIPiano.cpp @@ -164,7 +164,6 @@ void SPiano::draw(CDrawContext* dc) const Dimensions dim = getDimensions(false); const unsigned octs = impl.octs_; const unsigned keyCount = octs * 12; - const bool allKeysUsed = impl.keyUsed_.all(); dc->setDrawMode(kAntiAliasing); @@ -184,8 +183,6 @@ void SPiano::draw(CDrawContext* dc) switch (getKeyRole(key)) { case KeyRole::Note: - if (allKeysUsed) - goto whiteKeyDefault; hcy.h = impl.keyUsedHue_; break; case KeyRole::Switch: @@ -224,8 +221,6 @@ void SPiano::draw(CDrawContext* dc) switch (getKeyRole(key)) { case KeyRole::Note: - if (allKeysUsed) - goto blackKeyDefault; hcy.h = impl.keyUsedHue_; break; case KeyRole::Switch: From 5e1b06d810140e47eba652cbe02b778a0f340e36 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 10 Jul 2021 20:58:04 +0200 Subject: [PATCH 129/193] Don't normalize pitch and filter --- src/sfizz/Defaults.cpp | 4 ++-- src/sfizz/Region.cpp | 2 +- src/sfizz/SynthMessaging.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index a70f3602b..be65df600 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -104,7 +104,7 @@ FloatSpec filterGainMod { 0.0f, {-96.0f, 96.0f}, kPermissiveBounds }; FloatSpec filterRandom { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds }; FloatSpec filterKeytrack { 0, {0, 1200}, kPermissiveBounds }; FloatSpec filterVeltrack { 0, {-12000, 12000}, kPermissiveBounds }; -FloatSpec filterVeltrackMod { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; +FloatSpec filterVeltrackMod { 0.0f, {-12000, 12000}, kPermissiveBounds }; FloatSpec eqBandwidth { 1.0f, {0.001f, 4.0f}, kPermissiveBounds }; FloatSpec eqBandwidthMod { 0.0f, {-4.0f, 4.0f}, kPermissiveBounds }; FloatSpec eqFrequency { 0.0f, {0.0f, 20000.0f}, kPermissiveBounds }; @@ -116,7 +116,7 @@ FloatSpec eqVel2Gain { 0.0f, {-96.0f, 96.0f}, kPermissiveBounds }; FloatSpec pitchKeytrack { 100, {-1200, 1200}, kPermissiveBounds }; FloatSpec pitchRandom { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds }; FloatSpec pitchVeltrack { 0, {-12000, 12000}, kPermissiveBounds }; -FloatSpec pitchVeltrackMod { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; +FloatSpec pitchVeltrackMod { 0.0f, {-12000, 12000}, kPermissiveBounds }; FloatSpec transpose { 0, {-127, 127}, kPermissiveBounds }; FloatSpec pitch { 0.0f, {-2400.0f, 2400.0f}, kPermissiveBounds }; FloatSpec pitchMod { 0.0f, {-2400.0f, 2400.0f}, kPermissiveBounds }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 07b327fa0..5832e07f2 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -645,7 +645,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode, bool cleanOpcode) if (cc >= config::numCCs) return false; - filters[filterIndex].veltrackCC[cc].modifier = opcode.read(Default::ampVeltrackMod); + filters[filterIndex].veltrackCC[cc].modifier = opcode.read(Default::filterVeltrackMod); } break; case hash("fil&_veltrack_curvecc&"): diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index ce0c62b38..36f4bc032 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -945,7 +945,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co GET_REGION_OR_BREAK(indices[0]) if (region.pitchVeltrackCC.contains(indices[1])) { const auto& cc = region.pitchVeltrackCC.getWithDefault(indices[1]); - client.receive<'f'>(delay, path, cc.modifier * 100.0f); + client.receive<'f'>(delay, path, cc.modifier); } else { client.receive<'N'>(delay, path, {}); } @@ -1322,7 +1322,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co GET_FILTER_OR_BREAK(indices[1]) if (filter.veltrackCC.contains(indices[2])) { const auto& cc = filter.veltrackCC.getWithDefault(indices[2]); - client.receive<'f'>(delay, path, cc.modifier * 100.0f); + client.receive<'f'>(delay, path, cc.modifier); } else { client.receive<'N'>(delay, path, {}); } From f34de3e3c9e5022ae46f724bf2403e67008a4f69 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 7 Jul 2021 01:27:33 +0200 Subject: [PATCH 130/193] CC input - Add an EditValue in the knob - Shade the knobs when editing - Right-click on the knob allows to change from low resolution to high resolution CC --- plugins/editor/src/editor/Editor.cpp | 22 ++- plugins/editor/src/editor/GUIComponents.cpp | 203 +++++++++++++++++--- plugins/editor/src/editor/GUIComponents.h | 76 +++++++- plugins/editor/src/editor/GUIPiano.cpp | 4 +- 4 files changed, 259 insertions(+), 46 deletions(-) diff --git a/plugins/editor/src/editor/Editor.cpp b/plugins/editor/src/editor/Editor.cpp index 676948563..bc7b4d71b 100644 --- a/plugins/editor/src/editor/Editor.cpp +++ b/plugins/editor/src/editor/Editor.cpp @@ -162,11 +162,11 @@ struct Editor::Impl : EditorController::Receiver, SharedPointer backgroundBitmap_; SharedPointer defaultBackgroundBitmap_; - CControl* getSecondaryCCControl(unsigned cc) + SKnobCCBox* getSecondaryCCKnob(unsigned cc) { switch (cc) { - case 7: return volumeCCKnob_ ? volumeCCKnob_->getControl() : nullptr; - case 10: return panCCKnob_ ? panCCKnob_->getControl() : nullptr; + case 7: return volumeCCKnob_ ? volumeCCKnob_ : nullptr; + case 10: return panCCKnob_ ? panCCKnob_ : nullptr; default: return nullptr; } } @@ -906,6 +906,10 @@ void Editor::Impl::createFrameContents() box->setCCLabelFont(font); OnThemeChanged.push_back([box, palette]() { box->setNameLabelFontColor(palette->knobText); + box->setValueEditFontColor(palette->knobText); + auto shadingColor = palette->knobText; + shadingColor.alpha = 70; + box->setShadingRectangleColor(shadingColor); box->setCCLabelFontColor(palette->knobLabelText); box->setCCLabelBackColor(palette->knobLabelBackground); box->setKnobFontColor(palette->knobText); @@ -913,10 +917,6 @@ void Editor::Impl::createFrameContents() box->setKnobActiveTrackColor(palette->knobActiveTrack); box->setKnobInactiveTrackColor(palette->knobInactiveTrack); }); - box->setValueToStringFunction([](float value, std::string& text) -> bool { - text = std::to_string(std::lround(value * 127)); - return true; - }); return box; }; auto createBackground = [&background](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { @@ -932,6 +932,10 @@ void Editor::Impl::createFrameContents() panel->setCCLabelFont(font); OnThemeChanged.push_back([panel, palette]() { panel->setNameLabelFontColor(palette->knobText); + panel->setValueEditFontColor(palette->knobText); + auto shadingColor = palette->knobText; + shadingColor.alpha = 70; + panel->setShadingRectangleColor(shadingColor); panel->setCCLabelFontColor(palette->knobLabelText); panel->setCCLabelBackColor(palette->knobLabelBackground); panel->setKnobFontColor(palette->knobText); @@ -1665,7 +1669,7 @@ void Editor::Impl::updateCCValue(unsigned cc, float value) if (SControlsPanel* panel = controlsPanel_) panel->setControlValue(cc, value); - if (CControl* other = getSecondaryCCControl(cc)) { + if (SKnobCCBox* other = getSecondaryCCKnob(cc)) { other->setValue(value); other->invalid(); } @@ -1676,7 +1680,7 @@ void Editor::Impl::updateCCDefaultValue(unsigned cc, float value) if (SControlsPanel* panel = controlsPanel_) panel->setControlDefaultValue(cc, value); - if (CControl* other = getSecondaryCCControl(cc)) + if (SKnobCCBox* other = getSecondaryCCKnob(cc)) other->setDefaultValue(value); } diff --git a/plugins/editor/src/editor/GUIComponents.cpp b/plugins/editor/src/editor/GUIComponents.cpp index 769503fc9..968b13c45 100644 --- a/plugins/editor/src/editor/GUIComponents.cpp +++ b/plugins/editor/src/editor/GUIComponents.cpp @@ -15,6 +15,7 @@ #include "vstgui/lib/cvstguitimer.h" #include "vstgui/lib/cframe.h" #include "utility/vstgui_after.h" +#include "absl/strings/numbers.h" /// SBoxContainer::SBoxContainer(const CRect& size) @@ -517,12 +518,6 @@ void SStyledKnob::setFontColor(CColor fontColor) invalid(); } -void SStyledKnob::setValueToStringFunction(ValueToStringFunction func) -{ - valueToStringFunction_ = std::move(func); - invalid(); -} - void SStyledKnob::draw(CDrawContext* dc) { const CCoord lineWidth = 4.0; @@ -581,14 +576,18 @@ void SStyledKnob::draw(CDrawContext* dc) dc->setLineStyle(kLineSolid); dc->drawLine(p1, p2); } +} - if (valueToStringFunction_ && fontColor_.alpha > 0) { - std::string text; - if (valueToStringFunction_(getValue(), text)) { - dc->setFont(font_); - dc->setFontColor(fontColor_); - dc->drawString(text.c_str(), bounds); - } +void CFilledRect::draw(CDrawContext* dc) +{ + CRect bounds = getViewSize(); + dc->setFillColor(color_); + bool isRounded = radius_ > 0.0; + if (isRounded) { + auto roundRect = owned(dc->createRoundRectGraphicsPath(bounds, radius_)); + dc->drawGraphicsPath(roundRect, CDrawContext::kPathFilled); + } else { + dc->drawRect(bounds, kDrawFilled); } } @@ -596,8 +595,12 @@ void SStyledKnob::draw(CDrawContext* dc) SKnobCCBox::SKnobCCBox(const CRect& size, IControlListener* listener, int32_t tag) : CViewContainer(size), label_(makeOwned(CRect())), + valueEdit_(makeOwned(CRect(), listener, tag)), knob_(makeOwned(CRect(), listener, tag)), - ccLabel_(makeOwned(CRect())) + ccLabel_(makeOwned(CRect())), + shadingRectangle_(makeOwned(CRect())), + menuEntry_(makeOwned("Use HDCC", tag)), + menuListener_(owned(new MenuListener(*this))) { setBackgroundColor(CColor(0x00, 0x00, 0x00, 0x00)); @@ -614,17 +617,118 @@ SKnobCCBox::SKnobCCBox(const CRect& size, IControlListener* listener, int32_t ta ccLabel_->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00)); ccLabel_->setFontColor(CColor(0xff, 0xff, 0xff)); + valueEdit_->setBackColor(CColor(0x00, 0x00, 0x00, 0x00)); + valueEdit_->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00)); + valueEdit_->setFontColor(CColor(0x00, 0x00, 0x00, 0xff)); + valueEdit_->registerViewListener(this); + setHDMode(false); + valueEdit_->setVisible(true); + + shadingRectangle_->setVisible(false); + addView(label_); label_->remember(); addView(knob_); knob_->remember(); + addView(shadingRectangle_); + shadingRectangle_->remember(); + addView(valueEdit_); + valueEdit_->remember(); addView(ccLabel_); ccLabel_->remember(); - updateViewColors(); updateViewSizes(); } +SKnobCCBox::~SKnobCCBox() +{ + valueEdit_->unregisterViewListener(this); +} + +void SKnobCCBox::setHDMode(bool mode) +{ + if (mode) { + valueEdit_->setValueToStringFunction2([](float value, std::string& text, VSTGUI::CParamDisplay*) -> bool { + std::string s = std::to_string(value + 0.005f); + text = s.substr(0, 4); + return true; + }); + valueEdit_->setStringToValueFunction([](UTF8StringPtr txt, float& result, CTextEdit*) -> bool { + float value; + if (absl::SimpleAtof(txt, &value)) { + result = value; + return true; + } + + return false; + }); + menuEntry_->setTitle("Use low-res. CC"); + } else { + valueEdit_->setValueToStringFunction2([](float value, std::string& text, VSTGUI::CParamDisplay*) -> bool { + text = std::to_string(std::lround(value * 127)); + return true; + }); + valueEdit_->setStringToValueFunction([](UTF8StringPtr txt, float& result, CTextEdit*) -> bool { + float value; + if (absl::SimpleAtof(txt, &value)) { + result = value / 127.0f; + return true; + } + + return false; + }); + menuEntry_->setTitle("Use high-res. CC"); + } + + hdMode_ = mode; + valueEdit_->setValue(valueEdit_->getValue()); + invalid(); +} + +CMouseEventResult SKnobCCBox::onMouseDown(CPoint& where, const CButtonState& buttons) +{ + if (buttons.isRightButton()) { + CFrame* frame = getFrame(); + CPoint frameWhere = where; + frameWhere.offset(-getViewSize().left, -getViewSize().top); + this->localToFrame(frameWhere); + + auto self = shared(this); + frame->doAfterEventProcessing([self, frameWhere]() { + if (CFrame* frame = self->getFrame()) { + SharedPointer menu = + owned(new COptionMenu(CRect(), self->menuListener_, -1, nullptr, nullptr, COptionMenu::kPopupStyle)); + menu->addEntry(self->menuEntry_); + self->menuEntry_->remember(); // above call does not increment refcount + + menu->setFont(self->getValueEditFont()); + menu->setFontColor(self->getValueEditFontColor()); + menu->setBackColor(self->getValueEditBackColor()); + menu->popup(frame, frameWhere); + } + }); + return kMouseEventHandled; + } + + return CViewContainer::onMouseDown(where, buttons); +} + +void SKnobCCBox::viewLostFocus (CView* view) +{ + if (view == valueEdit_.get()) { + shadingRectangle_->setVisible(false); + invalid(); + } +} + +void SKnobCCBox::viewTookFocus (CView* view) +{ + if (view == valueEdit_.get()) { + shadingRectangle_->setVisible(true); + invalid(); + } +} + void SKnobCCBox::setHue(float hue) { hue_ = hue; @@ -637,12 +741,33 @@ void SKnobCCBox::setNameLabelFont(CFontRef font) updateViewSizes(); } +void SKnobCCBox::setValueEditFont(CFontRef font) +{ + label_->setFont(font); + updateViewSizes(); +} + void SKnobCCBox::setCCLabelFont(CFontRef font) { ccLabel_->setFont(font); updateViewSizes(); } +void SKnobCCBox::setValue(float value) +{ + float oldValue = knob_->getValue(); + knob_->setValue(value); + valueEdit_->setValue(value); + if (value != oldValue) + invalid(); +} + +void SKnobCCBox::setDefaultValue(float value) +{ + knob_->setDefaultValue(value); + valueEdit_->setDefaultValue(value); +} + void SKnobCCBox::updateViewSizes() { const CRect size = getViewSize(); @@ -650,10 +775,17 @@ void SKnobCCBox::updateViewSizes() const CFontRef nameFont = label_->getFont(); const CFontRef ccFont = ccLabel_->getFont(); + const CFontRef valueFont = valueEdit_->getFont(); nameLabelSize_ = CRect(0.0, 0.0, size.getWidth(), nameFont->getSize() + 2 * ypad); ccLabelSize_ = CRect(0.0, size.getHeight() - ccFont->getSize() - 2 * ypad, size.getWidth(), size.getHeight()); knobSize_ = CRect(0.0, nameLabelSize_.bottom, size.getWidth(), ccLabelSize_.top); + valueEditSize_ = CRect( + size.getWidth() / 2 - valueFont->getSize(), + size.getHeight() / 2 - valueFont->getSize() / 2, + size.getWidth() / 2 + valueFont->getSize(), + size.getHeight() / 2 + valueFont->getSize() / 2 + ); // remove knob side areas CCoord side = std::max(0.0, knobSize_.getWidth() - knobSize_.getHeight()); @@ -663,6 +795,8 @@ void SKnobCCBox::updateViewSizes() label_->setViewSize(nameLabelSize_); knob_->setViewSize(knobSize_); ccLabel_->setViewSize(ccLabelSize_); + valueEdit_->setViewSize(valueEditSize_); + shadingRectangle_->setViewSize(knobSize_); invalid(); } @@ -746,11 +880,6 @@ SControlsPanel::ControlSlot* SControlsPanel::getOrCreateSlot(uint32_t index) slot->box = box; slot->box->setCCLabelText(("CC " + std::to_string(index)).c_str()); - slot->box->setValueToStringFunction([](float value, std::string& text) -> bool { - text = std::to_string(std::lround(value * 127)); - return true; - }); - syncSlotStyle(index); return slot; @@ -760,10 +889,9 @@ void SControlsPanel::setControlValue(uint32_t index, float value) { ControlSlot* slot = getOrCreateSlot(index); SKnobCCBox* box = slot->box; - auto* control = box->getControl(); - float oldValue = control->getValue(); - control->setValue(value); - if (control->getValue() != oldValue) + float oldValue = box->getValue(); + box->setValue(value); + if (box->getValue() != oldValue) box->invalid(); } @@ -771,7 +899,7 @@ void SControlsPanel::setControlDefaultValue(uint32_t index, float value) { ControlSlot* slot = getOrCreateSlot(index); SKnobCCBox* box = slot->box; - box->getControl()->setDefaultValue(value); + box->setDefaultValue(value); } void SControlsPanel::setControlLabelText(uint32_t index, UTF8StringPtr text) @@ -788,12 +916,14 @@ void SControlsPanel::setControlLabelText(uint32_t index, UTF8StringPtr text) void SControlsPanel::setNameLabelFont(CFontRef font) { slots_[0]->box->setNameLabelFont(font); + slots_[0]->box->setValueEditFont(font); syncAllSlotStyles(); } void SControlsPanel::setNameLabelFontColor(CColor color) { slots_[0]->box->setNameLabelFontColor(color); + slots_[0]->box->setValueEditFontColor(color); syncAllSlotStyles(); } @@ -815,6 +945,24 @@ void SControlsPanel::setCCLabelFontColor(CColor color) syncAllSlotStyles(); } +void SControlsPanel::setValueEditBackColor(CColor color) +{ + slots_[0]->box->setValueEditBackColor(color); + syncAllSlotStyles(); +} + +void SControlsPanel::setShadingRectangleColor(CColor color) +{ + slots_[0]->box->setShadingRectangleColor(color); + syncAllSlotStyles(); +} + +void SControlsPanel::setValueEditFontColor(CColor color) +{ + slots_[0]->box->setValueEditFontColor(color); + syncAllSlotStyles(); +} + void SControlsPanel::setKnobActiveTrackColor(CColor color) { slots_[0]->box->setKnobActiveTrackColor(color); @@ -947,6 +1095,11 @@ void SControlsPanel::syncSlotStyle(uint32_t index) cur->setNameLabelFont(ref->getNameLabelFont()); cur->setNameLabelFontColor(ref->getNameLabelFontColor()); + cur->setValueEditFont(ref->getValueEditFont()); + cur->setValueEditFontColor(ref->getValueEditFontColor()); + + cur->setShadingRectangleColor(ref->getShadingRectangleColor()); + cur->setCCLabelFont(ref->getCCLabelFont()); cur->setCCLabelFontColor(ref->getCCLabelFontColor()); cur->setCCLabelBackColor(ref->getCCLabelBackColor()); diff --git a/plugins/editor/src/editor/GUIComponents.h b/plugins/editor/src/editor/GUIComponents.h index 8161154f2..9ff96eeb2 100644 --- a/plugins/editor/src/editor/GUIComponents.h +++ b/plugins/editor/src/editor/GUIComponents.h @@ -14,6 +14,7 @@ #include "vstgui/lib/controls/cslider.h" #include "vstgui/lib/controls/cknob.h" #include "vstgui/lib/controls/ctextlabel.h" +#include "vstgui/lib/controls/ctextedit.h" #include "vstgui/lib/controls/cbuttons.h" #include "vstgui/lib/controls/coptionmenu.h" #include "vstgui/lib/controls/cscrollbar.h" @@ -230,9 +231,6 @@ class SStyledKnob : public CKnobBase { void setFontColor(CColor fontColor); CColor getFontColor() const { return fontColor_; } - using ValueToStringFunction = std::function; - void setValueToStringFunction(ValueToStringFunction func); - CLASS_METHODS(SStyledKnob, CKnobBase) protected: void draw(CDrawContext* dc) override; @@ -244,16 +242,37 @@ class SStyledKnob : public CKnobBase { SharedPointer font_ = kNormalFont; CColor fontColor_ { 0x00, 0x00, 0x00 }; +}; + +class CFilledRect : public CView +{ +public: + explicit CFilledRect(const CRect& size) + : CView(size) {} - ValueToStringFunction valueToStringFunction_; + void setRadius(CCoord radius) { radius_ = radius; invalid(); } + CCoord getRadius() const { return radius_; } + + void setColor(CColor color){ color_ = color; invalid(); } + CColor getColor() { return color_; } +protected: + void draw(CDrawContext* dc) override; +private: + CCoord radius_ { 5.0 }; + CColor color_ { 0, 0, 0, 70 }; }; /// -class SKnobCCBox : public CViewContainer { +class SKnobCCBox : public CViewContainer, ViewListenerAdapter { public: SKnobCCBox(const CRect& size, IControlListener* listener, int32_t tag); + ~SKnobCCBox(); void setHue(float hue); - SStyledKnob* getControl() const { return knob_; } + + float getValue() const { return knob_->getValue(); } + float getDefaultValue() const { return knob_->getDefaultValue(); } + void setValue(float value); + void setDefaultValue(float value); void setNameLabelText(const UTF8String& name) { label_->setText(name); label_->invalid(); } void setCCLabelText(const UTF8String& name) { ccLabel_->setText(name); ccLabel_->invalid(); } @@ -264,6 +283,18 @@ class SKnobCCBox : public CViewContainer { void setNameLabelFontColor(CColor color) { label_->setFontColor(color); label_->invalid(); } CColor getNameLabelFontColor() const { return label_->getFontColor(); } + void setValueEditFont(CFontRef font); + CFontRef getValueEditFont() const { return label_->getFont(); } + + void setValueEditFontColor(CColor color) { valueEdit_->setFontColor(color); valueEdit_->invalid(); } + CColor getValueEditFontColor() const { return valueEdit_->getFontColor(); } + + void setValueEditBackColor(CColor color) { valueEdit_->setBackColor(color); valueEdit_->invalid(); } + CColor getValueEditBackColor() const { return valueEdit_->getBackColor(); } + + void setShadingRectangleColor(CColor color) { shadingRectangle_->setColor(color); shadingRectangle_->invalid(); } + CColor getShadingRectangleColor() const { return shadingRectangle_->getColor(); } + void setCCLabelFont(CFontRef font); CFontRef getCCLabelFont() const { return ccLabel_->getFont(); } @@ -288,21 +319,43 @@ class SKnobCCBox : public CViewContainer { void setKnobFontColor(CColor color) { knob_->setFontColor(color); knob_->invalid(); } CColor getKnobFontColor() const { return knob_->getFontColor(); } - using ValueToStringFunction = SStyledKnob::ValueToStringFunction; - void setValueToStringFunction(ValueToStringFunction f) { knob_->setValueToStringFunction(std::move(f)); knob_->invalid(); } + void viewLostFocus (CView* view) override; + void viewTookFocus (CView* view) override; + + bool isHD() const noexcept { return hdMode_; } + void setHDMode(bool mode); + +protected: + CMouseEventResult onMouseDown(CPoint& where, const CButtonState& buttons) override; private: void updateViewSizes(); void updateViewColors(); - -private: SharedPointer label_; + SharedPointer valueEdit_; SharedPointer knob_; SharedPointer ccLabel_; + SharedPointer shadingRectangle_; + SharedPointer menuEntry_; CRect nameLabelSize_; CRect knobSize_; CRect ccLabelSize_; + CRect valueEditSize_; + CRect rectangleSize_; float hue_ = 0.35; + + class MenuListener : public IControlListener, public NonAtomicReferenceCounted { + public: + explicit MenuListener(SKnobCCBox& box) : box_(box) {} + void valueChanged(CControl*) override + { + box_.setHDMode(!box_.isHD()); + } + private: + SKnobCCBox& box_; + }; + SharedPointer menuListener_; + bool hdMode_ { false }; }; /// @@ -320,6 +373,9 @@ class SControlsPanel : public CScrollView { void setCCLabelFont(CFontRef font); void setCCLabelBackColor(CColor color); void setCCLabelFontColor(CColor color); + void setValueEditBackColor(CColor color); + void setValueEditFontColor(CColor color); + void setShadingRectangleColor(CColor color); void setKnobActiveTrackColor(CColor color); void setKnobInactiveTrackColor(CColor color); void setKnobLineIndicatorColor(CColor color); diff --git a/plugins/editor/src/editor/GUIPiano.cpp b/plugins/editor/src/editor/GUIPiano.cpp index 9ecc86b7e..132044751 100644 --- a/plugins/editor/src/editor/GUIPiano.cpp +++ b/plugins/editor/src/editor/GUIPiano.cpp @@ -188,7 +188,7 @@ void SPiano::draw(CDrawContext* dc) case KeyRole::Switch: hcy.h = impl.keySwitchHue_; break; - default: whiteKeyDefault: + default: hcy.y = 1.0; if (impl.keyval_[key]) hcy.c = 0.0; @@ -226,7 +226,7 @@ void SPiano::draw(CDrawContext* dc) case KeyRole::Switch: hcy.h = impl.keySwitchHue_; break; - default: blackKeyDefault: + default: hcy.c = 0.0; break; } From 3431642cd2c1ec01fba16f807e672a521f09435d Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Fri, 16 Jul 2021 00:03:03 +0200 Subject: [PATCH 131/193] Use linear knobs in VST and LV2 --- plugins/lv2/sfizz_ui.cpp | 2 +- plugins/vst/SfizzVstController.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/lv2/sfizz_ui.cpp b/plugins/lv2/sfizz_ui.cpp index 3756c7f83..1bab16e2b 100644 --- a/plugins/lv2/sfizz_ui.cpp +++ b/plugins/lv2/sfizz_ui.cpp @@ -101,7 +101,7 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface { /// VSTGUIEditorInterface CFrame* getFrame() const override { return uiFrame.get(); } - + int32_t getKnobMode () const override { return kLinearMode; } LV2_Atom_Forge atom_forge; LV2_URID atom_event_transfer_uri; LV2_URID atom_object_uri; diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index a3d38f07d..957d2b56d 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -145,6 +145,9 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) addProgramList(list); list->addRef(); + // Use linear knobs + setKnobMode(kLinearMode); + return kResultTrue; } From 5b278bafdf41cf2455e5067f6ec0b5d8f631bd05 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Fri, 16 Jul 2021 00:16:35 +0200 Subject: [PATCH 132/193] Edit on double click --- plugins/editor/src/editor/GUIComponents.cpp | 47 ++++++++++++++++----- plugins/editor/src/editor/GUIComponents.h | 11 ++++- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/plugins/editor/src/editor/GUIComponents.cpp b/plugins/editor/src/editor/GUIComponents.cpp index 968b13c45..b1ef08f05 100644 --- a/plugins/editor/src/editor/GUIComponents.cpp +++ b/plugins/editor/src/editor/GUIComponents.cpp @@ -518,6 +518,12 @@ void SStyledKnob::setFontColor(CColor fontColor) invalid(); } +void SStyledKnob::setValueToStringFunction(ValueToStringFunction func) +{ + valueToStringFunction_ = std::move(func); + invalid(); +} + void SStyledKnob::draw(CDrawContext* dc) { const CCoord lineWidth = 4.0; @@ -576,6 +582,15 @@ void SStyledKnob::draw(CDrawContext* dc) dc->setLineStyle(kLineSolid); dc->drawLine(p1, p2); } + + if (valueToStringFunction_ && fontColor_.alpha > 0 && !hideValue_) { + std::string text; + if (valueToStringFunction_(getValue(), text)) { + dc->setFont(font_); + dc->setFontColor(fontColor_); + dc->drawString(text.c_str(), bounds); + } + } } void CFilledRect::draw(CDrawContext* dc) @@ -622,7 +637,7 @@ SKnobCCBox::SKnobCCBox(const CRect& size, IControlListener* listener, int32_t ta valueEdit_->setFontColor(CColor(0x00, 0x00, 0x00, 0xff)); valueEdit_->registerViewListener(this); setHDMode(false); - valueEdit_->setVisible(true); + valueEdit_->setVisible(false); shadingRectangle_->setVisible(false); @@ -648,11 +663,16 @@ SKnobCCBox::~SKnobCCBox() void SKnobCCBox::setHDMode(bool mode) { if (mode) { - valueEdit_->setValueToStringFunction2([](float value, std::string& text, VSTGUI::CParamDisplay*) -> bool { + auto valueToString = [](float value, std::string& text, VSTGUI::CParamDisplay*) -> bool { std::string s = std::to_string(value + 0.005f); text = s.substr(0, 4); return true; + }; + knob_->setValueToStringFunction([valueToString](float value, std::string& text) { + return valueToString(value, text, nullptr); }); + valueEdit_->setValueToStringFunction2(valueToString); + valueEdit_->setStringToValueFunction([](UTF8StringPtr txt, float& result, CTextEdit*) -> bool { float value; if (absl::SimpleAtof(txt, &value)) { @@ -664,10 +684,15 @@ void SKnobCCBox::setHDMode(bool mode) }); menuEntry_->setTitle("Use low-res. CC"); } else { - valueEdit_->setValueToStringFunction2([](float value, std::string& text, VSTGUI::CParamDisplay*) -> bool { + auto valueToString = [](float value, std::string& text, VSTGUI::CParamDisplay*) -> bool { text = std::to_string(std::lround(value * 127)); return true; + }; + knob_->setValueToStringFunction([valueToString](float value, std::string& text) { + return valueToString(value, text, nullptr); }); + valueEdit_->setValueToStringFunction2(valueToString); + valueEdit_->setStringToValueFunction([](UTF8StringPtr txt, float& result, CTextEdit*) -> bool { float value; if (absl::SimpleAtof(txt, &value)) { @@ -708,6 +733,12 @@ CMouseEventResult SKnobCCBox::onMouseDown(CPoint& where, const CButtonState& but } }); return kMouseEventHandled; + } else if (buttons.isDoubleClick() && !valueEdit_->isVisible()) { + valueEdit_->setVisible(true); + shadingRectangle_->setVisible(true); + knob_->setHideValue(true); + valueEdit_->takeFocus(); + invalid(); } return CViewContainer::onMouseDown(where, buttons); @@ -717,14 +748,8 @@ void SKnobCCBox::viewLostFocus (CView* view) { if (view == valueEdit_.get()) { shadingRectangle_->setVisible(false); - invalid(); - } -} - -void SKnobCCBox::viewTookFocus (CView* view) -{ - if (view == valueEdit_.get()) { - shadingRectangle_->setVisible(true); + valueEdit_->setVisible(false); + knob_->setHideValue(false); invalid(); } } diff --git a/plugins/editor/src/editor/GUIComponents.h b/plugins/editor/src/editor/GUIComponents.h index 9ff96eeb2..f1769db7a 100644 --- a/plugins/editor/src/editor/GUIComponents.h +++ b/plugins/editor/src/editor/GUIComponents.h @@ -231,6 +231,12 @@ class SStyledKnob : public CKnobBase { void setFontColor(CColor fontColor); CColor getFontColor() const { return fontColor_; } + using ValueToStringFunction = std::function; + void setValueToStringFunction(ValueToStringFunction func); + + void setHideValue(bool hide) { hideValue_ = hide; invalid(); } + bool getHideValue() const { return hideValue_; } + CLASS_METHODS(SStyledKnob, CKnobBase) protected: void draw(CDrawContext* dc) override; @@ -239,9 +245,12 @@ class SStyledKnob : public CKnobBase { CColor activeTrackColor_; CColor inactiveTrackColor_; CColor lineIndicatorColor_; + bool hideValue_ { false }; SharedPointer font_ = kNormalFont; CColor fontColor_ { 0x00, 0x00, 0x00 }; + + ValueToStringFunction valueToStringFunction_; }; class CFilledRect : public CView @@ -319,8 +328,8 @@ class SKnobCCBox : public CViewContainer, ViewListenerAdapter { void setKnobFontColor(CColor color) { knob_->setFontColor(color); knob_->invalid(); } CColor getKnobFontColor() const { return knob_->getFontColor(); } + // Edit box listener void viewLostFocus (CView* view) override; - void viewTookFocus (CView* view) override; bool isHD() const noexcept { return hdMode_; } void setHDMode(bool mode); From aaa49f3f8d51434fe1af16484293cb9fc15d9e1a Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Fri, 16 Jul 2021 00:53:22 +0200 Subject: [PATCH 133/193] Correct the int type in handleGroupOpcodes --- src/sfizz/Synth.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 7a9930903..fcc11f516 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -337,7 +337,7 @@ void Synth::Impl::handleGlobalOpcodes(const std::vector& members) void Synth::Impl::handleGroupOpcodes(const std::vector& members, const std::vector& masterMembers) { - absl::optional groupIdx; + absl::optional groupIdx; absl::optional maxPolyphony; const auto parseOpcode = [&](const Opcode& rawMember) { From 63c1af91ef8afe0e7fd6bbbb57c29421e6ea35bf Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Fri, 16 Jul 2021 11:09:17 +0200 Subject: [PATCH 134/193] Input didn't work on Linux --- plugins/editor/src/editor/GUIComponents.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/editor/src/editor/GUIComponents.cpp b/plugins/editor/src/editor/GUIComponents.cpp index b1ef08f05..5b3f743dc 100644 --- a/plugins/editor/src/editor/GUIComponents.cpp +++ b/plugins/editor/src/editor/GUIComponents.cpp @@ -739,6 +739,7 @@ CMouseEventResult SKnobCCBox::onMouseDown(CPoint& where, const CButtonState& but knob_->setHideValue(true); valueEdit_->takeFocus(); invalid(); + return kMouseEventHandled; } return CViewContainer::onMouseDown(where, buttons); From e7792b927f721e5141211af57dbe37f0080ffaa6 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Tue, 20 Jul 2021 10:26:42 +0200 Subject: [PATCH 135/193] Create LICENSE --- LICENSE | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..7334143fc --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2021, sfizz contributors (detailed in AUTHORS.md) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 53277dae3ce3c33440e6764419934532e88fe466 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Tue, 20 Jul 2021 10:27:40 +0200 Subject: [PATCH 136/193] Delete old license file [ci skip] --- LICENSE.md | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index d3f769aef..000000000 --- a/LICENSE.md +++ /dev/null @@ -1,29 +0,0 @@ -BSD 2-Clause License - -The code is copyrighted by their respective authors, as indicated by the -source control mechanism. - -Please refer to AUTHORS.md for the list of contributors. - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 825d558a4cae575f2d85df5d11d45f9aac609c6a Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 20 Jul 2021 11:18:04 +0200 Subject: [PATCH 137/193] Added aliases for Cakewalk and RGC loop_oncc opcodes --- src/sfizz/OpcodeCleanup.cpp | 1293 +++++++++++++++++++++-------------- src/sfizz/OpcodeCleanup.re | 15 + src/sfizz/Region.cpp | 4 +- tests/RegionValuesT.cpp | 11 + 4 files changed, 800 insertions(+), 523 deletions(-) diff --git a/src/sfizz/OpcodeCleanup.cpp b/src/sfizz/OpcodeCleanup.cpp index 1d7531613..6282c2d29 100644 --- a/src/sfizz/OpcodeCleanup.cpp +++ b/src/sfizz/OpcodeCleanup.cpp @@ -1,4 +1,4 @@ -/* Generated by re2c 2.0.3 on Sat Dec 12 13:32:03 2020 */ +/* Generated by re2c 1.3 on Tue Jul 20 11:23:08 2021 */ #line 1 "src/sfizz/OpcodeCleanup.re" /* -*- mode: c++; -*- */ // SPDX-License-Identifier: BSD-2-Clause @@ -219,7 +219,7 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc yy19: ++YYCURSOR; yy20: -#line 195 "src/sfizz/OpcodeCleanup.re" +#line 210 "src/sfizz/OpcodeCleanup.re" { goto end_region; } @@ -685,39 +685,40 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc yy82: yych = *++YYCURSOR; switch (yych) { + case '_': goto yy114; case 'e': yyt1 = YYCURSOR; - goto yy114; + goto yy115; case 'm': yyt1 = YYCURSOR; - goto yy115; + goto yy116; case 's': yyt1 = YYCURSOR; - goto yy116; + goto yy117; default: goto yy34; } yy83: yych = *++YYCURSOR; switch (yych) { - case 'y': goto yy117; + case 'y': goto yy118; default: goto yy34; } yy84: yych = *++YYCURSOR; switch (yych) { - case 'o': goto yy118; + case 'o': goto yy119; default: goto yy34; } yy85: yych = *++YYCURSOR; switch (yych) { - case 'i': goto yy119; + case 'i': goto yy120; default: goto yy34; } yy86: yych = *++YYCURSOR; switch (yych) { - case 'o': goto yy119; + case 'o': goto yy120; default: goto yy34; } yy87: @@ -729,13 +730,13 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc yy88: yych = *++YYCURSOR; switch (yych) { - case 'p': goto yy120; + case 'p': goto yy121; default: goto yy34; } yy89: yych = *++YYCURSOR; switch (yych) { - case 'n': goto yy121; + case 'n': goto yy122; default: goto yy34; } yy90: @@ -743,88 +744,88 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc switch (yych) { case 0x00: yyt2 = yyt3 = NULL; - goto yy122; + goto yy123; case '_': yyt3 = YYCURSOR; - goto yy124; + goto yy125; default: goto yy34; } yy91: yych = *++YYCURSOR; switch (yych) { - case '_': goto yy126; + case '_': goto yy127; default: goto yy34; } yy92: yych = *++YYCURSOR; switch (yych) { - case 'o': goto yy127; + case 'o': goto yy128; default: goto yy34; } yy93: yych = *++YYCURSOR; switch (yych) { - case 'o': goto yy128; + case 'o': goto yy129; default: goto yy34; } yy94: yych = *++YYCURSOR; switch (yych) { - case 't': goto yy129; + case 't': goto yy130; default: goto yy34; } yy95: yych = *++YYCURSOR; switch (yych) { - case 'p': goto yy130; + case 'p': goto yy131; default: goto yy34; } yy96: yych = *++YYCURSOR; switch (yych) { - case 'f': goto yy131; + case 'f': goto yy132; default: goto yy34; } yy97: yych = *++YYCURSOR; switch (yych) { - case 'u': goto yy132; + case 'u': goto yy133; default: goto yy34; } yy98: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy133; + case 'e': goto yy134; default: goto yy34; } yy99: yych = *++YYCURSOR; switch (yych) { - case 'w': goto yy134; + case 'w': goto yy135; default: goto yy34; } yy100: yych = *++YYCURSOR; switch (yych) { - case 'r': goto yy135; + case 'r': goto yy136; default: goto yy34; } yy101: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy136; + case 'a': goto yy137; default: goto yy34; } yy102: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy137; + case 'e': goto yy138; default: goto yy34; } yy103: yych = *++YYCURSOR; switch (yych) { - case 'y': goto yy138; + case 'y': goto yy139; default: goto yy34; } yy104: @@ -839,7 +840,7 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat("fil1_", group(1)); goto end_region; } -#line 843 "src/sfizz/OpcodeCleanup.cpp" +#line 844 "src/sfizz/OpcodeCleanup.cpp" yy106: yych = *++YYCURSOR; yy107: @@ -848,7 +849,7 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc yy108: yych = *++YYCURSOR; switch (yych) { - case 'p': goto yy139; + case 'p': goto yy140; default: goto yy34; } yy109: @@ -863,17 +864,17 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat("volume", group(1)); goto end_region; } -#line 867 "src/sfizz/OpcodeCleanup.cpp" +#line 868 "src/sfizz/OpcodeCleanup.cpp" yy111: yych = *++YYCURSOR; switch (yych) { - case 'r': goto yy142; - default: goto yy141; + case 'r': goto yy143; + default: goto yy142; } yy112: yych = *++YYCURSOR; switch (yych) { - case 'l': goto yy143; + case 'l': goto yy144; default: goto yy34; } yy113: @@ -881,69 +882,76 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc switch (yych) { case 'c': yyt2 = YYCURSOR; - goto yy144; + goto yy145; case 'o': yyt2 = YYCURSOR; - goto yy145; + goto yy146; case 'r': yyt2 = YYCURSOR; - goto yy146; + goto yy147; case 's': yyt2 = YYCURSOR; - goto yy147; + goto yy148; case 'w': yyt2 = YYCURSOR; - goto yy148; + goto yy149; default: goto yy34; } yy114: yych = *++YYCURSOR; switch (yych) { - case 'n': goto yy149; + case 'l': goto yy150; + case 's': goto yy151; default: goto yy34; } yy115: yych = *++YYCURSOR; switch (yych) { - case 'o': goto yy150; + case 'n': goto yy152; default: goto yy34; } yy116: yych = *++YYCURSOR; switch (yych) { - case 't': goto yy151; + case 'o': goto yy153; default: goto yy34; } yy117: - yych = *++YYCURSOR; - if (yych <= 0x00) goto yy152; - goto yy34; -yy118: yych = *++YYCURSOR; switch (yych) { - case 'd': goto yy154; + case 't': goto yy154; default: goto yy34; } +yy118: + yych = *++YYCURSOR; + if (yych <= 0x00) goto yy155; + goto yy34; yy119: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy155; - case 'h': goto yy156; + case 'd': goto yy157; default: goto yy34; } yy120: yych = *++YYCURSOR; switch (yych) { - case 'h': goto yy157; + case 'c': goto yy158; + case 'h': goto yy159; default: goto yy34; } yy121: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy158; + case 'h': goto yy160; default: goto yy34; } yy122: + yych = *++YYCURSOR; + switch (yych) { + case 'a': goto yy161; + default: goto yy34; + } +yy123: ++YYCURSOR; yynmatch = 2; yypmatch[0] = yyt1; @@ -955,63 +963,63 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat("pitch", group(1)); goto end_region; } -#line 959 "src/sfizz/OpcodeCleanup.cpp" -yy124: +#line 967 "src/sfizz/OpcodeCleanup.cpp" +yy125: yych = *++YYCURSOR; if (yych <= 0x00) { yyt2 = YYCURSOR; - goto yy122; + goto yy123; } - goto yy124; -yy126: + goto yy125; +yy127: yych = *++YYCURSOR; switch (yych) { case 'a': yyt2 = YYCURSOR; - goto yy159; + goto yy162; case 'd': yyt2 = YYCURSOR; - goto yy160; + goto yy163; case 'h': yyt2 = YYCURSOR; - goto yy161; + goto yy164; case 'r': yyt2 = YYCURSOR; - goto yy162; + goto yy165; case 's': yyt2 = YYCURSOR; - goto yy163; - case 'v': goto yy164; + goto yy166; + case 'v': goto yy167; default: goto yy34; } -yy127: +yy128: yych = *++YYCURSOR; switch (yych) { - case '_': goto yy165; + case '_': goto yy168; default: goto yy34; } -yy128: +yy129: yych = *++YYCURSOR; switch (yych) { - case 'w': goto yy166; + case 'w': goto yy169; default: goto yy34; } -yy129: +yy130: yych = *++YYCURSOR; switch (yych) { case 'e': goto yy95; default: goto yy34; } -yy130: +yy131: yych = *++YYCURSOR; - if (yych <= 0x00) goto yy167; + if (yych <= 0x00) goto yy170; goto yy34; -yy131: +yy132: yych = *++YYCURSOR; switch (yych) { case 0x00: yyt2 = yyt3 = NULL; - goto yy169; + goto yy172; case '0': case '1': case '2': @@ -1023,131 +1031,143 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc case '8': case '9': yyt2 = YYCURSOR; - goto yy171; + goto yy174; case '_': yyt2 = yyt4 = NULL; yyt3 = YYCURSOR; - goto yy173; - default: goto yy34; - } -yy132: - yych = *++YYCURSOR; - switch (yych) { - case 't': goto yy174; + goto yy176; default: goto yy34; } yy133: yych = *++YYCURSOR; switch (yych) { - case 's': goto yy175; + case 't': goto yy177; default: goto yy34; } yy134: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy176; + case 's': goto yy178; default: goto yy34; } yy135: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy177; + case 'c': goto yy179; default: goto yy34; } yy136: yych = *++YYCURSOR; switch (yych) { - case 'i': goto yy178; + case 'e': goto yy180; default: goto yy34; } yy137: yych = *++YYCURSOR; switch (yych) { - case 'l': goto yy179; + case 'i': goto yy181; default: goto yy34; } yy138: yych = *++YYCURSOR; switch (yych) { - case 'p': goto yy180; + case 'l': goto yy182; default: goto yy34; } yy139: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy181; + case 'p': goto yy183; default: goto yy34; } yy140: yych = *++YYCURSOR; + switch (yych) { + case 'e': goto yy184; + default: goto yy34; + } yy141: + yych = *++YYCURSOR; +yy142: if (yych <= 0x00) { yyt2 = YYCURSOR; goto yy109; } - goto yy140; -yy142: - yych = *++YYCURSOR; - switch (yych) { - case 'a': goto yy182; - default: goto yy141; - } + goto yy141; yy143: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy183; - default: goto yy34; + case 'a': goto yy185; + default: goto yy142; } yy144: yych = *++YYCURSOR; switch (yych) { - case 'u': goto yy184; + case 'c': goto yy186; default: goto yy34; } yy145: yych = *++YYCURSOR; switch (yych) { - case 'f': goto yy185; + case 'u': goto yy187; default: goto yy34; } yy146: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy186; - case 'e': goto yy187; + case 'f': goto yy188; default: goto yy34; } yy147: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy188; + case 'a': goto yy189; + case 'e': goto yy190; default: goto yy34; } yy148: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy189; + case 'c': goto yy191; default: goto yy34; } yy149: yych = *++YYCURSOR; switch (yych) { - case 'd': goto yy190; + case 'a': goto yy192; default: goto yy34; } yy150: yych = *++YYCURSOR; switch (yych) { - case 'd': goto yy191; + case 'e': goto yy193; default: goto yy34; } yy151: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy192; + case 't': goto yy194; default: goto yy34; } yy152: + yych = *++YYCURSOR; + switch (yych) { + case 'd': goto yy195; + default: goto yy34; + } +yy153: + yych = *++YYCURSOR; + switch (yych) { + case 'd': goto yy196; + default: goto yy34; + } +yy154: + yych = *++YYCURSOR; + switch (yych) { + case 'a': goto yy197; + default: goto yy34; + } +yy155: ++YYCURSOR; yynmatch = 2; yypmatch[2] = yyt1; @@ -1159,92 +1179,92 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat("off_", group(1)); goto end_region; } -#line 1163 "src/sfizz/OpcodeCleanup.cpp" -yy154: +#line 1183 "src/sfizz/OpcodeCleanup.cpp" +yy157: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy117; + case 'e': goto yy118; default: goto yy34; } -yy155: +yy158: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy193; + case 'c': goto yy198; default: goto yy34; } -yy156: +yy159: yych = *++YYCURSOR; switch (yych) { - case 'd': goto yy194; + case 'd': goto yy199; default: goto yy34; } -yy157: +yy160: yych = *++YYCURSOR; switch (yych) { - case 'o': goto yy195; + case 'o': goto yy200; default: goto yy34; } -yy158: +yy161: yych = *++YYCURSOR; switch (yych) { - case 'n': goto yy196; + case 'n': goto yy201; default: goto yy34; } -yy159: +yy162: yych = *++YYCURSOR; switch (yych) { - case 't': goto yy197; + case 't': goto yy202; default: goto yy34; } -yy160: +yy163: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy198; + case 'e': goto yy203; default: goto yy34; } -yy161: +yy164: yych = *++YYCURSOR; switch (yych) { - case 'o': goto yy199; + case 'o': goto yy204; default: goto yy34; } -yy162: +yy165: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy200; + case 'e': goto yy205; default: goto yy34; } -yy163: +yy166: yych = *++YYCURSOR; switch (yych) { - case 't': goto yy201; - case 'u': goto yy202; + case 't': goto yy206; + case 'u': goto yy207; default: goto yy34; } -yy164: +yy167: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy203; + case 'e': goto yy208; default: goto yy34; } -yy165: +yy168: yych = *++YYCURSOR; switch (yych) { case 'd': yyt2 = YYCURSOR; - goto yy204; + goto yy209; case 'f': yyt2 = YYCURSOR; - goto yy205; + goto yy210; default: goto yy34; } -yy166: +yy169: yych = *++YYCURSOR; switch (yych) { - case 'n': goto yy130; + case 'n': goto yy131; default: goto yy34; } -yy167: +yy170: ++YYCURSOR; yynmatch = 2; yypmatch[2] = yyt1; @@ -1256,8 +1276,8 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat("bend_", group(1)); goto end_region; } -#line 1260 "src/sfizz/OpcodeCleanup.cpp" -yy169: +#line 1280 "src/sfizz/OpcodeCleanup.cpp" +yy172: ++YYCURSOR; yynmatch = 2; yypmatch[0] = yyt1; @@ -1269,8 +1289,8 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat("cutoff1", group(1)); goto end_region; } -#line 1273 "src/sfizz/OpcodeCleanup.cpp" -yy171: +#line 1293 "src/sfizz/OpcodeCleanup.cpp" +yy174: yych = *++YYCURSOR; switch (yych) { case '0': @@ -1282,253 +1302,265 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc case '6': case '7': case '8': - case '9': goto yy171; + case '9': goto yy174; case '_': yyt4 = YYCURSOR; - goto yy206; - default: goto yy34; - } -yy173: - yych = *++YYCURSOR; - switch (yych) { - case 'r': goto yy209; - default: goto yy208; - } -yy174: - yych = *++YYCURSOR; - switch (yych) { - case 'o': goto yy210; - default: goto yy34; - } -yy175: - yych = *++YYCURSOR; - switch (yych) { - case 'o': goto yy211; + goto yy211; default: goto yy34; } yy176: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy212; - default: goto yy34; + case 'r': goto yy214; + default: goto yy213; } yy177: yych = *++YYCURSOR; switch (yych) { - case 'q': goto yy134; + case 'o': goto yy215; default: goto yy34; } yy178: yych = *++YYCURSOR; switch (yych) { - case 'n': goto yy134; + case 'o': goto yy216; default: goto yy34; } yy179: yych = *++YYCURSOR; switch (yych) { - case '2': goto yy213; + case 'c': goto yy217; default: goto yy34; } yy180: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy214; + case 'q': goto yy135; default: goto yy34; } yy181: yych = *++YYCURSOR; - if (yych <= 0x00) goto yy215; - goto yy34; + switch (yych) { + case 'n': goto yy135; + default: goto yy34; + } yy182: yych = *++YYCURSOR; switch (yych) { - case 'n': goto yy217; - default: goto yy141; + case '2': goto yy218; + default: goto yy34; } yy183: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy218; + case 'e': goto yy219; default: goto yy34; } yy184: yych = *++YYCURSOR; - switch (yych) { - case 't': goto yy219; - default: goto yy34; - } + if (yych <= 0x00) goto yy220; + goto yy34; yy185: yych = *++YYCURSOR; switch (yych) { - case 'f': goto yy220; - default: goto yy34; + case 'n': goto yy222; + default: goto yy142; } yy186: yych = *++YYCURSOR; switch (yych) { - case 't': goto yy221; + case 'c': goto yy223; default: goto yy34; } yy187: yych = *++YYCURSOR; switch (yych) { - case 's': goto yy222; + case 't': goto yy224; default: goto yy34; } yy188: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy223; + case 'f': goto yy225; default: goto yy34; } yy189: yych = *++YYCURSOR; switch (yych) { - case 'v': goto yy224; + case 't': goto yy226; default: goto yy34; } yy190: yych = *++YYCURSOR; - if (yych <= 0x00) goto yy225; - goto yy34; + switch (yych) { + case 's': goto yy227; + default: goto yy34; + } yy191: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy190; + case 'a': goto yy228; default: goto yy34; } yy192: yych = *++YYCURSOR; switch (yych) { - case 'r': goto yy227; + case 'v': goto yy229; default: goto yy34; } yy193: yych = *++YYCURSOR; switch (yych) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - yyt1 = YYCURSOR; - goto yy228; + case 'n': goto yy230; default: goto yy34; } yy194: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy230; + case 'a': goto yy231; default: goto yy34; } yy195: yych = *++YYCURSOR; - switch (yych) { - case 'n': goto yy231; - default: goto yy34; - } + if (yych <= 0x00) goto yy232; + goto yy34; yy196: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy232; + case 'e': goto yy195; default: goto yy34; } yy197: yych = *++YYCURSOR; switch (yych) { - case 't': goto yy233; + case 'r': goto yy234; default: goto yy34; } yy198: yych = *++YYCURSOR; switch (yych) { - case 'c': - case 'l': goto yy234; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + yyt1 = YYCURSOR; + goto yy235; default: goto yy34; } yy199: yych = *++YYCURSOR; switch (yych) { - case 'l': goto yy235; + case 'c': goto yy237; default: goto yy34; } yy200: yych = *++YYCURSOR; switch (yych) { - case 'l': goto yy236; + case 'n': goto yy238; default: goto yy34; } yy201: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy237; + case 'c': goto yy239; default: goto yy34; } yy202: yych = *++YYCURSOR; switch (yych) { - case 's': goto yy238; + case 't': goto yy240; default: goto yy34; } yy203: yych = *++YYCURSOR; switch (yych) { - case 'l': goto yy239; + case 'c': + case 'l': goto yy241; default: goto yy34; } yy204: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy240; + case 'l': goto yy242; default: goto yy34; } yy205: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy241; - case 'r': goto yy242; + case 'l': goto yy243; default: goto yy34; } yy206: yych = *++YYCURSOR; switch (yych) { - case 'r': goto yy243; + case 'a': goto yy244; default: goto yy34; } yy207: yych = *++YYCURSOR; + switch (yych) { + case 's': goto yy245; + default: goto yy34; + } yy208: - if (yych <= 0x00) { - yyt2 = YYCURSOR; - goto yy169; + yych = *++YYCURSOR; + switch (yych) { + case 'l': goto yy246; + default: goto yy34; } - goto yy207; yy209: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy244; - default: goto yy208; + case 'e': goto yy247; + default: goto yy34; } yy210: yych = *++YYCURSOR; switch (yych) { - case 'f': goto yy245; + case 'a': goto yy248; + case 'r': goto yy249; default: goto yy34; } yy211: yych = *++YYCURSOR; switch (yych) { - case 'n': goto yy246; + case 'r': goto yy250; default: goto yy34; } yy212: yych = *++YYCURSOR; +yy213: + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy172; + } + goto yy212; +yy214: + yych = *++YYCURSOR; + switch (yych) { + case 'a': goto yy251; + default: goto yy213; + } +yy215: + yych = *++YYCURSOR; + switch (yych) { + case 'f': goto yy252; + default: goto yy34; + } +yy216: + yych = *++YYCURSOR; + switch (yych) { + case 'n': goto yy253; + default: goto yy34; + } +yy217: + yych = *++YYCURSOR; switch (yych) { case '0': case '1': @@ -1541,18 +1573,18 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc case '8': case '9': yyt3 = YYCURSOR; - goto yy247; + goto yy254; default: goto yy34; } -yy213: +yy218: yych = *++YYCURSOR; yyt2 = YYCURSOR; - goto yy252; -yy214: + goto yy259; +yy219: yych = *++YYCURSOR; - if (yych <= 0x00) goto yy253; + if (yych <= 0x00) goto yy260; goto yy34; -yy215: +yy220: ++YYCURSOR; yynmatch = 1; yypmatch[0] = YYCURSOR - 8; @@ -1562,14 +1594,14 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat("fil1_type"); goto end_region; } -#line 1566 "src/sfizz/OpcodeCleanup.cpp" -yy217: +#line 1598 "src/sfizz/OpcodeCleanup.cpp" +yy222: yych = *++YYCURSOR; switch (yych) { - case 'd': goto yy255; - default: goto yy141; + case 'd': goto yy262; + default: goto yy142; } -yy218: +yy223: yych = *++YYCURSOR; switch (yych) { case '0': @@ -1583,46 +1615,58 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc case '8': case '9': yyt1 = YYCURSOR; - goto yy256; + goto yy263; default: goto yy34; } -yy219: +yy224: yych = *++YYCURSOR; switch (yych) { - case 'o': goto yy258; + case 'o': goto yy265; default: goto yy34; } -yy220: +yy225: yych = *++YYCURSOR; switch (yych) { - case 's': goto yy259; + case 's': goto yy266; default: goto yy34; } -yy221: +yy226: yych = *++YYCURSOR; switch (yych) { - case 'i': goto yy260; + case 'i': goto yy267; default: goto yy34; } -yy222: +yy227: yych = *++YYCURSOR; switch (yych) { - case 'o': goto yy261; + case 'o': goto yy268; default: goto yy34; } -yy223: +yy228: yych = *++YYCURSOR; switch (yych) { - case 'l': goto yy224; + case 'l': goto yy229; default: goto yy34; } -yy224: +yy229: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy262; + case 'e': goto yy269; default: goto yy34; } -yy225: +yy230: + yych = *++YYCURSOR; + switch (yych) { + case 'g': goto yy270; + default: goto yy34; + } +yy231: + yych = *++YYCURSOR; + switch (yych) { + case 'r': goto yy271; + default: goto yy34; + } +yy232: ++YYCURSOR; yynmatch = 2; yypmatch[2] = yyt1; @@ -1634,17 +1678,17 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat("loop_", group(1)); goto end_region; } -#line 1638 "src/sfizz/OpcodeCleanup.cpp" -yy227: +#line 1682 "src/sfizz/OpcodeCleanup.cpp" +yy234: yych = *++YYCURSOR; switch (yych) { - case 't': goto yy190; + case 't': goto yy195; default: goto yy34; } -yy228: +yy235: yych = *++YYCURSOR; switch (yych) { - case 0x00: goto yy263; + case 0x00: goto yy272; case '0': case '1': case '2': @@ -1654,115 +1698,115 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc case '6': case '7': case '8': - case '9': goto yy228; + case '9': goto yy235; default: goto yy34; } -yy230: +yy237: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy265; + case 'c': goto yy274; default: goto yy34; } -yy231: +yy238: yych = *++YYCURSOR; switch (yych) { - case 'y': goto yy266; + case 'y': goto yy275; default: goto yy34; } -yy232: +yy239: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy267; + case 'e': goto yy276; default: goto yy34; } -yy233: +yy240: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy268; + case 'a': goto yy277; default: goto yy34; } -yy234: +yy241: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy269; + case 'a': goto yy278; default: goto yy34; } -yy235: +yy242: yych = *++YYCURSOR; switch (yych) { - case 'd': goto yy270; + case 'd': goto yy279; default: goto yy34; } -yy236: +yy243: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy271; + case 'e': goto yy280; default: goto yy34; } -yy237: +yy244: yych = *++YYCURSOR; switch (yych) { - case 'r': goto yy272; + case 'r': goto yy281; default: goto yy34; } -yy238: +yy245: yych = *++YYCURSOR; switch (yych) { - case 't': goto yy273; + case 't': goto yy282; default: goto yy34; } -yy239: +yy246: yych = *++YYCURSOR; switch (yych) { - case '2': goto yy274; + case '2': goto yy283; default: goto yy34; } -yy240: +yy247: yych = *++YYCURSOR; switch (yych) { - case 'p': goto yy275; + case 'p': goto yy284; default: goto yy34; } -yy241: +yy248: yych = *++YYCURSOR; switch (yych) { - case 'd': goto yy276; + case 'd': goto yy285; default: goto yy34; } -yy242: +yy249: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy277; + case 'e': goto yy286; default: goto yy34; } -yy243: +yy250: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy278; + case 'a': goto yy287; default: goto yy34; } -yy244: +yy251: yych = *++YYCURSOR; switch (yych) { - case 'n': goto yy279; - default: goto yy208; + case 'n': goto yy288; + default: goto yy213; } -yy245: +yy252: yych = *++YYCURSOR; switch (yych) { - case 'f': goto yy280; + case 'f': goto yy289; default: goto yy34; } -yy246: +yy253: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy281; + case 'a': goto yy290; default: goto yy34; } -yy247: +yy254: yych = *++YYCURSOR; switch (yych) { - case 0x00: goto yy282; + case 0x00: goto yy291; case '0': case '1': case '2': @@ -1772,10 +1816,10 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc case '6': case '7': case '8': - case '9': goto yy247; + case '9': goto yy254; default: goto yy34; } -yy249: +yy256: ++YYCURSOR; yynmatch = 3; yypmatch[2] = yyt1; @@ -1789,13 +1833,13 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat(group(1), "_velto", group(2)); goto end_region; } -#line 1793 "src/sfizz/OpcodeCleanup.cpp" -yy251: +#line 1837 "src/sfizz/OpcodeCleanup.cpp" +yy258: yych = *++YYCURSOR; -yy252: - if (yych <= 0x00) goto yy249; - goto yy251; -yy253: +yy259: + if (yych <= 0x00) goto yy256; + goto yy258; +yy260: ++YYCURSOR; yynmatch = 2; yypmatch[2] = yyt1; @@ -1807,17 +1851,17 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat("fil", group(1), "_type"); goto end_region; } -#line 1811 "src/sfizz/OpcodeCleanup.cpp" -yy255: +#line 1855 "src/sfizz/OpcodeCleanup.cpp" +yy262: yych = *++YYCURSOR; switch (yych) { - case 'o': goto yy284; - default: goto yy141; + case 'o': goto yy293; + default: goto yy142; } -yy256: +yy263: yych = *++YYCURSOR; switch (yych) { - case 0x00: goto yy285; + case 0x00: goto yy294; case '0': case '1': case '2': @@ -1827,38 +1871,50 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc case '6': case '7': case '8': - case '9': goto yy256; + case '9': goto yy263; default: goto yy34; } -yy258: +yy265: yych = *++YYCURSOR; switch (yych) { - case 'f': goto yy287; + case 'f': goto yy296; default: goto yy34; } -yy259: +yy266: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy288; + case 'e': goto yy297; default: goto yy34; } -yy260: +yy267: yych = *++YYCURSOR; switch (yych) { - case 'o': goto yy262; + case 'o': goto yy269; default: goto yy34; } -yy261: +yy268: yych = *++YYCURSOR; switch (yych) { - case 'n': goto yy289; + case 'n': goto yy298; default: goto yy34; } -yy262: +yy269: yych = *++YYCURSOR; - if (yych <= 0x00) goto yy290; + if (yych <= 0x00) goto yy299; goto yy34; -yy263: +yy270: + yych = *++YYCURSOR; + switch (yych) { + case 't': goto yy301; + default: goto yy34; + } +yy271: + yych = *++YYCURSOR; + switch (yych) { + case 't': goto yy302; + default: goto yy34; + } +yy272: ++YYCURSOR; yynmatch = 3; yypmatch[4] = yyt1; @@ -1872,8 +1928,8 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat("start_", group(1), "cc", group(2)); goto end_region; } -#line 1876 "src/sfizz/OpcodeCleanup.cpp" -yy265: +#line 1932 "src/sfizz/OpcodeCleanup.cpp" +yy274: yych = *++YYCURSOR; switch (yych) { case '0': @@ -1887,115 +1943,115 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc case '8': case '9': yyt1 = YYCURSOR; - goto yy292; + goto yy303; default: goto yy34; } -yy266: +yy275: yych = *++YYCURSOR; switch (yych) { - case '_': goto yy294; + case '_': goto yy305; default: goto yy34; } -yy267: +yy276: yych = *++YYCURSOR; switch (yych) { case 0x00: yyt2 = yyt3 = NULL; - goto yy295; + goto yy306; case '_': yyt3 = YYCURSOR; - goto yy297; + goto yy308; default: goto yy34; } -yy268: +yy277: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy299; + case 'c': goto yy310; default: goto yy34; } -yy269: +yy278: yych = *++YYCURSOR; switch (yych) { - case 'y': goto yy270; + case 'y': goto yy279; default: goto yy34; } -yy270: +yy279: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy300; + case 'c': goto yy311; default: goto yy34; } -yy271: +yy280: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy301; + case 'a': goto yy312; default: goto yy34; } -yy272: +yy281: yych = *++YYCURSOR; switch (yych) { - case 't': goto yy270; + case 't': goto yy279; default: goto yy34; } -yy273: +yy282: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy302; + case 'a': goto yy313; default: goto yy34; } -yy274: +yy283: yych = *++YYCURSOR; yyt2 = YYCURSOR; - goto yy306; -yy275: + goto yy317; +yy284: yych = *++YYCURSOR; switch (yych) { - case 't': goto yy307; + case 't': goto yy318; default: goto yy34; } -yy276: +yy285: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy308; + case 'e': goto yy319; default: goto yy34; } -yy277: +yy286: yych = *++YYCURSOR; switch (yych) { - case 'q': goto yy308; + case 'q': goto yy319; default: goto yy34; } -yy278: +yy287: yych = *++YYCURSOR; switch (yych) { - case 'n': goto yy309; + case 'n': goto yy320; default: goto yy34; } -yy279: +yy288: yych = *++YYCURSOR; switch (yych) { - case 'd': goto yy310; - default: goto yy208; + case 'd': goto yy321; + default: goto yy213; } -yy280: +yy289: yych = *++YYCURSOR; switch (yych) { case 0x00: yyt4 = yyt5 = NULL; yyt3 = YYCURSOR; - goto yy311; + goto yy322; case '_': yyt3 = yyt5 = YYCURSOR; - goto yy313; + goto yy324; default: goto yy34; } -yy281: +yy290: yych = *++YYCURSOR; switch (yych) { - case 'n': goto yy315; + case 'n': goto yy326; default: goto yy34; } -yy282: +yy291: ++YYCURSOR; yynmatch = 4; yypmatch[2] = yyt1; @@ -2011,14 +2067,14 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat(group(1), "_", group(2), "_oncc", group(3)); goto end_region; } -#line 2015 "src/sfizz/OpcodeCleanup.cpp" -yy284: +#line 2071 "src/sfizz/OpcodeCleanup.cpp" +yy293: yych = *++YYCURSOR; switch (yych) { - case 'm': goto yy316; - default: goto yy141; + case 'm': goto yy327; + default: goto yy142; } -yy285: +yy294: ++YYCURSOR; yynmatch = 3; yypmatch[4] = yyt1; @@ -2032,26 +2088,26 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat(group(1), "hdcc", group(2)); goto end_region; } -#line 2036 "src/sfizz/OpcodeCleanup.cpp" -yy287: +#line 2092 "src/sfizz/OpcodeCleanup.cpp" +yy296: yych = *++YYCURSOR; switch (yych) { - case 'f': goto yy317; + case 'f': goto yy328; default: goto yy34; } -yy288: +yy297: yych = *++YYCURSOR; switch (yych) { - case 't': goto yy262; + case 't': goto yy269; default: goto yy34; } -yy289: +yy298: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy318; + case 'a': goto yy329; default: goto yy34; } -yy290: +yy299: ++YYCURSOR; yynmatch = 3; yypmatch[2] = yyt1; @@ -2065,11 +2121,23 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat(group(1), "_", group(2), "1"); goto end_region; } -#line 2069 "src/sfizz/OpcodeCleanup.cpp" -yy292: +#line 2125 "src/sfizz/OpcodeCleanup.cpp" +yy301: + yych = *++YYCURSOR; + switch (yych) { + case 'h': goto yy330; + default: goto yy34; + } +yy302: + yych = *++YYCURSOR; + switch (yych) { + case 'c': goto yy331; + default: goto yy34; + } +yy303: yych = *++YYCURSOR; switch (yych) { - case 0x00: goto yy319; + case 0x00: goto yy332; case '0': case '1': case '2': @@ -2079,16 +2147,16 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc case '6': case '7': case '8': - case '9': goto yy292; + case '9': goto yy303; default: goto yy34; } -yy294: +yy305: yych = *++YYCURSOR; switch (yych) { - case 'g': goto yy321; + case 'g': goto yy334; default: goto yy34; } -yy295: +yy306: ++YYCURSOR; yynmatch = 2; yypmatch[0] = yyt1; @@ -2100,39 +2168,39 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat("resonance1", group(1)); goto end_region; } -#line 2104 "src/sfizz/OpcodeCleanup.cpp" -yy297: +#line 2172 "src/sfizz/OpcodeCleanup.cpp" +yy308: yych = *++YYCURSOR; if (yych <= 0x00) { yyt2 = YYCURSOR; - goto yy295; + goto yy306; } - goto yy297; -yy299: + goto yy308; +yy310: yych = *++YYCURSOR; switch (yych) { - case 'k': goto yy270; + case 'k': goto yy279; default: goto yy34; } -yy300: +yy311: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy322; + case 'c': goto yy335; default: goto yy34; } -yy301: +yy312: yych = *++YYCURSOR; switch (yych) { - case 's': goto yy323; + case 's': goto yy336; default: goto yy34; } -yy302: +yy313: yych = *++YYCURSOR; switch (yych) { - case 'i': goto yy324; + case 'i': goto yy337; default: goto yy34; } -yy303: +yy314: ++YYCURSOR; yynmatch = 3; yypmatch[2] = yyt1; @@ -2146,37 +2214,37 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat(group(1), "_velto", group(2)); goto end_region; } -#line 2150 "src/sfizz/OpcodeCleanup.cpp" -yy305: +#line 2218 "src/sfizz/OpcodeCleanup.cpp" +yy316: yych = *++YYCURSOR; -yy306: - if (yych <= 0x00) goto yy303; - goto yy305; -yy307: +yy317: + if (yych <= 0x00) goto yy314; + goto yy316; +yy318: yych = *++YYCURSOR; switch (yych) { - case 'h': goto yy308; + case 'h': goto yy319; default: goto yy34; } -yy308: +yy319: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy325; + case 'c': goto yy338; default: goto yy34; } -yy309: +yy320: yych = *++YYCURSOR; switch (yych) { - case 'd': goto yy326; + case 'd': goto yy339; default: goto yy34; } -yy310: +yy321: yych = *++YYCURSOR; switch (yych) { - case 'o': goto yy327; - default: goto yy208; + case 'o': goto yy340; + default: goto yy213; } -yy311: +yy322: ++YYCURSOR; yynmatch = 4; yypmatch[2] = yyt1; @@ -2192,43 +2260,56 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat(group(1), "_", group(2), "1", group(3)); goto end_region; } -#line 2196 "src/sfizz/OpcodeCleanup.cpp" -yy313: +#line 2264 "src/sfizz/OpcodeCleanup.cpp" +yy324: yych = *++YYCURSOR; if (yych <= 0x00) { yyt4 = YYCURSOR; - goto yy311; + goto yy322; } - goto yy313; -yy315: + goto yy324; +yy326: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy328; + case 'c': goto yy341; default: goto yy34; } -yy316: +yy327: yych = *++YYCURSOR; - if (yych <= 0x00) goto yy329; - goto yy140; -yy317: + if (yych <= 0x00) goto yy342; + goto yy141; +yy328: yych = *++YYCURSOR; switch (yych) { case 0x00: yyt4 = yyt5 = NULL; yyt3 = YYCURSOR; - goto yy331; + goto yy344; case '_': yyt3 = yyt5 = YYCURSOR; - goto yy333; + goto yy346; default: goto yy34; } -yy318: +yy329: yych = *++YYCURSOR; switch (yych) { - case 'n': goto yy335; + case 'n': goto yy348; default: goto yy34; } -yy319: +yy330: + yych = *++YYCURSOR; + switch (yych) { + case '_': goto yy349; + case 'c': goto yy350; + default: goto yy34; + } +yy331: + yych = *++YYCURSOR; + switch (yych) { + case 'c': goto yy351; + default: goto yy34; + } +yy332: ++YYCURSOR; yynmatch = 3; yypmatch[4] = yyt1; @@ -2242,14 +2323,14 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat("start_", group(1), "hdcc", group(2)); goto end_region; } -#line 2246 "src/sfizz/OpcodeCleanup.cpp" -yy321: +#line 2327 "src/sfizz/OpcodeCleanup.cpp" +yy334: yych = *++YYCURSOR; switch (yych) { - case 'r': goto yy336; + case 'r': goto yy352; default: goto yy34; } -yy322: +yy335: yych = *++YYCURSOR; switch (yych) { case '0': @@ -2263,46 +2344,46 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc case '8': case '9': yyt3 = YYCURSOR; - goto yy337; + goto yy353; default: goto yy34; } -yy323: +yy336: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy270; + case 'e': goto yy279; default: goto yy34; } -yy324: +yy337: yych = *++YYCURSOR; switch (yych) { - case 'n': goto yy270; + case 'n': goto yy279; default: goto yy34; } -yy325: +yy338: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy339; + case 'c': goto yy355; default: goto yy34; } -yy326: +yy339: yych = *++YYCURSOR; switch (yych) { - case 'o': goto yy340; + case 'o': goto yy356; default: goto yy34; } -yy327: +yy340: yych = *++YYCURSOR; switch (yych) { - case 'm': goto yy341; - default: goto yy208; + case 'm': goto yy357; + default: goto yy213; } -yy328: +yy341: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy280; + case 'e': goto yy289; default: goto yy34; } -yy329: +yy342: ++YYCURSOR; yynmatch = 1; yypmatch[0] = YYCURSOR - 12; @@ -2312,8 +2393,8 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = "amp_random"; goto end_region; } -#line 2316 "src/sfizz/OpcodeCleanup.cpp" -yy331: +#line 2397 "src/sfizz/OpcodeCleanup.cpp" +yy344: ++YYCURSOR; yynmatch = 4; yypmatch[2] = yyt1; @@ -2329,30 +2410,35 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat(group(1), "_", group(2), "1", group(3)); goto end_region; } -#line 2333 "src/sfizz/OpcodeCleanup.cpp" -yy333: +#line 2414 "src/sfizz/OpcodeCleanup.cpp" +yy346: yych = *++YYCURSOR; if (yych <= 0x00) { yyt4 = YYCURSOR; - goto yy331; + goto yy344; } - goto yy333; -yy335: + goto yy346; +yy348: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy342; + case 'c': goto yy358; default: goto yy34; } -yy336: +yy349: yych = *++YYCURSOR; switch (yych) { - case 'o': goto yy343; + case 'o': goto yy359; default: goto yy34; } -yy337: +yy350: + yych = *++YYCURSOR; + switch (yych) { + case 'c': goto yy360; + default: goto yy34; + } +yy351: yych = *++YYCURSOR; switch (yych) { - case 0x00: goto yy344; case '0': case '1': case '2': @@ -2362,10 +2448,34 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc case '6': case '7': case '8': - case '9': goto yy337; + case '9': + yyt1 = YYCURSOR; + goto yy361; default: goto yy34; } -yy339: +yy352: + yych = *++YYCURSOR; + switch (yych) { + case 'o': goto yy363; + default: goto yy34; + } +yy353: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy364; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': goto yy353; + default: goto yy34; + } +yy355: yych = *++YYCURSOR; switch (yych) { case '0': @@ -2379,32 +2489,71 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc case '8': case '9': yyt3 = YYCURSOR; - goto yy346; + goto yy366; default: goto yy34; } -yy340: +yy356: yych = *++YYCURSOR; switch (yych) { - case 'm': goto yy348; + case 'm': goto yy368; default: goto yy34; } -yy341: +yy357: yych = *++YYCURSOR; - if (yych <= 0x00) goto yy349; - goto yy207; -yy342: + if (yych <= 0x00) goto yy369; + goto yy212; +yy358: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy317; + case 'e': goto yy328; default: goto yy34; } -yy343: +yy359: yych = *++YYCURSOR; switch (yych) { - case 'u': goto yy351; + case 'n': goto yy371; default: goto yy34; } -yy344: +yy360: + yych = *++YYCURSOR; + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + yyt1 = YYCURSOR; + goto yy372; + default: goto yy34; + } +yy361: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy374; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': goto yy361; + default: goto yy34; + } +yy363: + yych = *++YYCURSOR; + switch (yych) { + case 'u': goto yy376; + default: goto yy34; + } +yy364: ++YYCURSOR; yynmatch = 4; yypmatch[2] = yyt1; @@ -2420,11 +2569,11 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat(group(1), "_", group(2), "_oncc", group(3)); goto end_region; } -#line 2424 "src/sfizz/OpcodeCleanup.cpp" -yy346: +#line 2573 "src/sfizz/OpcodeCleanup.cpp" +yy366: yych = *++YYCURSOR; switch (yych) { - case 0x00: goto yy352; + case 0x00: goto yy377; case '0': case '1': case '2': @@ -2434,13 +2583,13 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc case '6': case '7': case '8': - case '9': goto yy346; + case '9': goto yy366; default: goto yy34; } -yy348: +yy368: yych = *++YYCURSOR; if (yych >= 0x01) goto yy34; -yy349: +yy369: ++YYCURSOR; yynmatch = 2; yypmatch[0] = yyt1; @@ -2452,14 +2601,49 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat("fil", group(1), "_random"); goto again_region; } -#line 2456 "src/sfizz/OpcodeCleanup.cpp" -yy351: +#line 2605 "src/sfizz/OpcodeCleanup.cpp" +yy371: yych = *++YYCURSOR; switch (yych) { - case 'p': goto yy354; + case 'c': goto yy379; default: goto yy34; } -yy352: +yy372: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy380; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': goto yy372; + default: goto yy34; + } +yy374: + ++YYCURSOR; + yynmatch = 2; + yypmatch[2] = yyt1; + yypmatch[0] = yyt1 - 12; + yypmatch[1] = YYCURSOR; + yypmatch[3] = YYCURSOR - 1; +#line 195 "src/sfizz/OpcodeCleanup.re" + { + opcode = absl::StrCat("loop_start_oncc", group(1)); + goto end_region; + } +#line 2640 "src/sfizz/OpcodeCleanup.cpp" +yy376: + yych = *++YYCURSOR; + switch (yych) { + case 'p': goto yy382; + default: goto yy34; + } +yy377: ++YYCURSOR; yynmatch = 4; yypmatch[2] = yyt1; @@ -2475,10 +2659,48 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = absl::StrCat(group(1), "_", group(2), "_oncc", group(3)); goto end_region; } -#line 2479 "src/sfizz/OpcodeCleanup.cpp" -yy354: +#line 2663 "src/sfizz/OpcodeCleanup.cpp" +yy379: yych = *++YYCURSOR; - if (yych >= 0x01) goto yy34; + switch (yych) { + case 'c': goto yy383; + default: goto yy34; + } +yy380: + ++YYCURSOR; + yynmatch = 2; + yypmatch[2] = yyt1; + yypmatch[0] = yyt1 - 13; + yypmatch[1] = YYCURSOR; + yypmatch[3] = YYCURSOR - 1; +#line 200 "src/sfizz/OpcodeCleanup.re" + { + opcode = absl::StrCat("loop_end_oncc", group(1)); + goto end_region; + } +#line 2682 "src/sfizz/OpcodeCleanup.cpp" +yy382: + yych = *++YYCURSOR; + if (yych <= 0x00) goto yy384; + goto yy34; +yy383: + yych = *++YYCURSOR; + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + yyt1 = YYCURSOR; + goto yy386; + default: goto yy34; + } +yy384: ++YYCURSOR; yynmatch = 1; yypmatch[0] = YYCURSOR - 16; @@ -2488,9 +2710,38 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc opcode = "group"; goto end_region; } -#line 2492 "src/sfizz/OpcodeCleanup.cpp" +#line 2714 "src/sfizz/OpcodeCleanup.cpp" +yy386: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy388; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': goto yy386; + default: goto yy34; + } +yy388: + ++YYCURSOR; + yynmatch = 2; + yypmatch[2] = yyt1; + yypmatch[0] = yyt1 - 16; + yypmatch[1] = YYCURSOR; + yypmatch[3] = YYCURSOR - 1; +#line 205 "src/sfizz/OpcodeCleanup.re" + { + opcode = absl::StrCat("loop_end_oncc", group(1)); + goto end_region; + } +#line 2743 "src/sfizz/OpcodeCleanup.cpp" } -#line 199 "src/sfizz/OpcodeCleanup.re" +#line 214 "src/sfizz/OpcodeCleanup.re" end_region: @@ -2505,80 +2756,80 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc YYCURSOR = opcode.c_str(); -#line 2509 "src/sfizz/OpcodeCleanup.cpp" +#line 2760 "src/sfizz/OpcodeCleanup.cpp" { char yych; yych = *YYCURSOR; switch (yych) { - case 's': goto yy361; - default: goto yy359; + case 's': goto yy394; + default: goto yy392; } -yy359: +yy392: ++YYCURSOR; -yy360: -#line 219 "src/sfizz/OpcodeCleanup.re" +yy393: +#line 234 "src/sfizz/OpcodeCleanup.re" { goto end_control; } -#line 2524 "src/sfizz/OpcodeCleanup.cpp" -yy361: +#line 2775 "src/sfizz/OpcodeCleanup.cpp" +yy394: yych = *(YYMARKER = ++YYCURSOR); switch (yych) { - case 'e': goto yy362; - default: goto yy360; + case 'e': goto yy395; + default: goto yy393; } -yy362: +yy395: yych = *++YYCURSOR; switch (yych) { - case 't': goto yy364; - default: goto yy363; + case 't': goto yy397; + default: goto yy396; } -yy363: +yy396: YYCURSOR = YYMARKER; - goto yy360; -yy364: + goto yy393; +yy397: yych = *++YYCURSOR; switch (yych) { - case '_': goto yy365; - default: goto yy363; + case '_': goto yy398; + default: goto yy396; } -yy365: +yy398: yych = *++YYCURSOR; switch (yych) { - case 'r': goto yy366; - default: goto yy363; + case 'r': goto yy399; + default: goto yy396; } -yy366: +yy399: yych = *++YYCURSOR; switch (yych) { - case 'e': goto yy367; - default: goto yy363; + case 'e': goto yy400; + default: goto yy396; } -yy367: +yy400: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy368; - default: goto yy363; + case 'a': goto yy401; + default: goto yy396; } -yy368: +yy401: yych = *++YYCURSOR; switch (yych) { - case 'l': goto yy369; - default: goto yy363; + case 'l': goto yy402; + default: goto yy396; } -yy369: +yy402: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy370; - default: goto yy363; + case 'c': goto yy403; + default: goto yy396; } -yy370: +yy403: yych = *++YYCURSOR; switch (yych) { - case 'c': goto yy371; - default: goto yy363; + case 'c': goto yy404; + default: goto yy396; } -yy371: +yy404: yych = *++YYCURSOR; switch (yych) { case '0': @@ -2592,13 +2843,13 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc case '8': case '9': yyt1 = YYCURSOR; - goto yy372; - default: goto yy363; + goto yy405; + default: goto yy396; } -yy372: +yy405: yych = *++YYCURSOR; switch (yych) { - case 0x00: goto yy374; + case 0x00: goto yy407; case '0': case '1': case '2': @@ -2608,24 +2859,24 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc case '6': case '7': case '8': - case '9': goto yy372; - default: goto yy363; + case '9': goto yy405; + default: goto yy396; } -yy374: +yy407: ++YYCURSOR; yynmatch = 2; yypmatch[2] = yyt1; yypmatch[0] = yyt1 - 10; yypmatch[1] = YYCURSOR; yypmatch[3] = YYCURSOR - 1; -#line 214 "src/sfizz/OpcodeCleanup.re" +#line 229 "src/sfizz/OpcodeCleanup.re" { opcode = absl::StrCat("set_hdcc", group(1)); goto end_control; } -#line 2627 "src/sfizz/OpcodeCleanup.cpp" +#line 2878 "src/sfizz/OpcodeCleanup.cpp" } -#line 223 "src/sfizz/OpcodeCleanup.re" +#line 238 "src/sfizz/OpcodeCleanup.re" end_control: diff --git a/src/sfizz/OpcodeCleanup.re b/src/sfizz/OpcodeCleanup.re index d3bdb12e3..27ea2e22b 100644 --- a/src/sfizz/OpcodeCleanup.re +++ b/src/sfizz/OpcodeCleanup.re @@ -192,6 +192,21 @@ end_region_oncc: goto end_region; } + "loop_startcc" (number) END { + opcode = absl::StrCat("loop_start_oncc", group(1)); + goto end_region; + } + + "loop_lengthcc" (number) END { + opcode = absl::StrCat("loop_end_oncc", group(1)); + goto end_region; + } + + "loop_length_oncc" (number) END { + opcode = absl::StrCat("loop_end_oncc", group(1)); + goto end_region; + } + * { goto end_region; } diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index c4aba7012..685e38115 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -128,13 +128,13 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode, bool cleanOpcode) case hash("loop_start"): // also loopstart loopRange.setStart(opcode.read(Default::loopStart)); break; - case hash("loop_start_oncc&"): // also loop_start_cc& + case hash("loop_start_oncc&"): // also loop_start_cc&, loop_startcc& if (opcode.parameters.back() > config::numCCs) return false; loopStartCC[opcode.parameters.back()] = opcode.read(Default::loopMod); break; - case hash("loop_end_oncc&"): // also loop_end_cc& + case hash("loop_end_oncc&"): // also loop_end_cc&, loop_lengthcc&, loop_length_oncc&, loop_length_cc& if (opcode.parameters.back() > config::numCCs) return false; diff --git a/tests/RegionValuesT.cpp b/tests/RegionValuesT.cpp index ab982531c..15c7f860b 100644 --- a/tests/RegionValuesT.cpp +++ b/tests/RegionValuesT.cpp @@ -356,6 +356,9 @@ TEST_CASE("[Values] Loop range") sample=kick.wav sample=kick.wav loop_start_cc12=10 loop_end_cc14=-100 sample=kick.wav loop_start_oncc12=-10 loop_end_oncc14=100 + sample=kick.wav loop_startcc12=-10 loop_lengthcc14=100 + sample=kick.wav loop_length_oncc14=100 + sample=kick.wav loop_length_cc14=100 )"); synth.dispatchMessage(client, 0, "/region0/loop_start_cc12", "", nullptr); synth.dispatchMessage(client, 0, "/region0/loop_end_cc14", "", nullptr); @@ -363,6 +366,10 @@ TEST_CASE("[Values] Loop range") synth.dispatchMessage(client, 0, "/region1/loop_end_cc14", "", nullptr); synth.dispatchMessage(client, 0, "/region2/loop_start_cc12", "", nullptr); synth.dispatchMessage(client, 0, "/region2/loop_end_cc14", "", nullptr); + synth.dispatchMessage(client, 0, "/region3/loop_start_cc12", "", nullptr); + synth.dispatchMessage(client, 0, "/region3/loop_end_cc14", "", nullptr); + synth.dispatchMessage(client, 0, "/region4/loop_end_cc14", "", nullptr); + synth.dispatchMessage(client, 0, "/region5/loop_end_cc14", "", nullptr); std::vector expected { "/region0/loop_start_cc12,h : { 0 }", "/region0/loop_end_cc14,h : { 0 }", @@ -370,6 +377,10 @@ TEST_CASE("[Values] Loop range") "/region1/loop_end_cc14,h : { -100 }", "/region2/loop_start_cc12,h : { -10 }", "/region2/loop_end_cc14,h : { 100 }", + "/region3/loop_start_cc12,h : { -10 }", + "/region3/loop_end_cc14,h : { 100 }", + "/region4/loop_end_cc14,h : { 100 }", + "/region5/loop_end_cc14,h : { 100 }", }; REQUIRE(messageList == expected); } From aeb17c1fcac3a35ccb857a3f3ac91b67db9c972f Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 20 Jul 2021 12:25:03 +0200 Subject: [PATCH 138/193] Parse oscillator=auto and only have implicit wavetables if the file is short AND wavetable tags are in --- src/sfizz/Opcode.cpp | 3 +++ src/sfizz/Synth.cpp | 2 +- tests/FilesT.cpp | 21 +++++++++++++++++---- tests/TestFiles/channels_multi.sfz | 4 +++- tests/TestFiles/short_non_wavetable.wav | Bin 0 -> 5860 bytes 5 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 tests/TestFiles/short_non_wavetable.wav diff --git a/src/sfizz/Opcode.cpp b/src/sfizz/Opcode.cpp index e860c5368..f27a6685b 100644 --- a/src/sfizz/Opcode.cpp +++ b/src/sfizz/Opcode.cpp @@ -327,6 +327,9 @@ absl::optional readBoolean(absl::string_view value) template <> absl::optional Opcode::readOptional(OpcodeSpec, absl::string_view value) { + if (value == "auto") + return OscillatorEnabled::Auto; + auto v = readBoolean(value); if (!v) return absl::nullopt; diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index fcc11f516..468623b21 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -638,7 +638,7 @@ void Synth::Impl::finalizeSfzLoad() region.sampleId.reset(new FileId("*silence")); region.hasWavetableSample = false; } else { - region.hasWavetableSample |= true; + region.hasWavetableSample &= true; } } } diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 196f5bcf9..b65b9d8bc 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -274,7 +274,7 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)") { Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels_multi.sfz"); - REQUIRE(synth.getNumRegions() == 10); + REQUIRE(synth.getNumRegions() == 12); int regionNumber = 0; const Region* region = nullptr; @@ -327,14 +327,27 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)") REQUIRE(!region->isOscillator()); REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Off); - // implicit wavetable (sound file < 3000 frames) + // implicit wavetable (sound file < 3000 frames and wavetable tags) region = synth.getRegionView(regionNumber++); - REQUIRE(region->sampleId->filename() == "ramp_wave.wav"); - REQUIRE(!region->isStereo()); + REQUIRE(region->sampleId->filename() == "wavetables/surge.wav"); + REQUIRE(!region->isGenerator()); + REQUIRE(region->isOscillator()); + REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto); + + // Parse oscillator=auto and same as above + region = synth.getRegionView(regionNumber++); + REQUIRE(region->sampleId->filename() == "wavetables/surge.wav"); REQUIRE(!region->isGenerator()); REQUIRE(region->isOscillator()); REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto); + // implicit non wavetable (sound file < 3000 frames but no wavetable tags) + region = synth.getRegionView(regionNumber++); + REQUIRE(region->sampleId->filename() == "short_non_wavetable.wav"); + REQUIRE(!region->isGenerator()); + REQUIRE(!region->isOscillator()); + REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto); + // implicit non-wavetable (sound file >= 3000 frames) region = synth.getRegionView(regionNumber++); REQUIRE(region->sampleId->filename() == "snare.wav"); diff --git a/tests/TestFiles/channels_multi.sfz b/tests/TestFiles/channels_multi.sfz index ee6d6acf6..9921b152c 100644 --- a/tests/TestFiles/channels_multi.sfz +++ b/tests/TestFiles/channels_multi.sfz @@ -4,7 +4,9 @@ sample=ramp_wave.wav oscillator=on oscillator_multi=3 sample=ramp_wave.wav oscillator=off sample=ramp_wave.wav oscillator=off oscillator_multi=3 - sample=ramp_wave.wav + sample=wavetables/surge.wav + oscillator=auto sample=wavetables/surge.wav + sample=short_non_wavetable.wav sample=snare.wav sample=*sine oscillator_multi=1 sample=*sine oscillator_multi=2 diff --git a/tests/TestFiles/short_non_wavetable.wav b/tests/TestFiles/short_non_wavetable.wav new file mode 100644 index 0000000000000000000000000000000000000000..e71973d35e1a76f2776711c29abe809e6e2bb4fd GIT binary patch literal 5860 zcmX|F2V7Iv_kSzNdr4l#V}~Rn4ir&Q9Ed=y+ginK9aVAGRjspDrM1Pabx@R+D!;bw zRqLo&S8*UJ0Ys2JlMo1m@!reJ&ih~6&*%R+pS#aJ@7{Os`QCHRj7j6iR|i7S+>hpr zTe*Hqm;!`a`Gf(=f#kl7l0m~$008-1oJR3 ziUKYnMfW&O904{QD)>%;0k>rMH&6}K0Chmk zkOLCHQaAus!M))@@F-XZ&xL2gYv5Jzc6cxRBYX?~2X27tVK<;aBoOI|L?Q9WWF#5c ziDV+rk+(=2Qj6FT7!5*JxLhGu7G4ndeyGaNCdAkQtYi)Dd%&kXT16%J}##?MHds_OnWHnE2 zerjHAZf!c*6w{lp2Mc;}k6&orpRFqc8DibT$S6-|%RQ9O)tm@~g#wtzq+UgtCY;|JI!J4|7 zxZ1SZw%RduSL)#UFY9yahc+B;=xSKnSk^ev@YIlKd}vHC{b34g`mHI_{FgbgxuAJb zi>D>cGPw11>#{a`+evHx_Wbtc9f6&^r~S5LvvZnD z?QV5n@g#c{z9Qc-ej+4+E8vUBN>qwH#r_Z+5Ka>PBN~UdK5D@{7cAZDoJQz=n?f8jY3Oy^K?5O*1P-V?)$^1 z_i**t*wfbYcCW*|=S5I`O8TVroe)_Z`DH&e>Qq!re{=tg0V4-K8Mrq(c2Myk<=|z5 z^9Qev>5BPch-B#bp~Hs#GE6x=SM6W z(H6Tnwr+URaLcf(!^REUJM_R1L(H+51u^M^Zx8ZDXGiak4vdZ-ICj9i{*R-^MYZ;u z*Y9HFlfFTHANSeX#~D%6duuPghpIWPvh&GE)*Ko=oWR#rP@FT497>1okI75#50- zga<+M_M-sc{vXPSGj>w9OWgS9WQ-{nlnviY#&$(uGZhj>4CPJqp(qaQb|GmA+fSlmcnNGX3@ZKk_%|@q$+UCcRiM*T?AN zL79{PBEK%*oS&7SnctcJnSQLkD!(FshCWAMtY`HueVP6jAf;!)s)Ce)jDq(CWZ{y+ zyM>-YMUkcOL1Ag((4zDrrfBZloVS|dq~fE+rNvC~H}5u=>?~bh`m*Fq$-vS(rIn?> zl%6ZiE1mGZe_3nU8lbg#$sX0?Sw63sj ze!Z!FM#HlPzF~S}Y2!YF(a>(_VZ3D=X4+vYH0=P|dD~1hA8H;3wDVib8;jjSwXSQG zwOwpOtqZKp)C1F4CX=x;;p`7=AKO)%mOIJe_Wkw{ z#}h|)=NM;ol5Q{e<1%Nd&@Fpxw3LunXH>UORiPKE5a266_XWn6rU?zC?+Y- zDf5+#QbR>kL#cS`HwyQg?l;+Qx!=!zg#SeUZT{c+zxMAQ@KwNufXM;=0W$-h1Pl!9 z8z>1J5ttTO6xbN32wD(yD(G@hQ4kvJAKU@TvLGxtA~-2{Wbo+Vd%@R2QdPgJhN_xF z!c^;3nW`jJ4;89PP+e15RIgPjs#~gsq59CPp>smVg`Nt16Iu}ZBJ_EvIn)~J3^j(* zp(6Fi>NV=q>Q`z+Bi9(!KdM)#kE)fL>l#SQXfiZAG{-eJH3v1lG*OzXnjo#8me%-d zleEjVYOPuGUV~@{Yv+J^hvu1v(G1akqMf3htKFwf*Y40x)AE`^O__$*RBBdfaLp&0 zUp0Se7HfKIdTRVMHR}E9edv3RT|oDM zTK^3Hjs9By$9^mQLi`%2Z>Uh}hH|m8n=)6iPSGXbBcCWAC@17ave`0JcAA_;R!O%> zwbFXYkCJf7MRC0NA)z6z0$b!yd?Wrw)I)ShI9nJbtQGt$fCVvFDmn}mpw-AJWD4>Q z*j6qm8(IO?@H6;!-%;OKpVj-d7xpgmc-@)qBzK!D#iex>IJY=a=V`|XN1k10zsN;! z?`%hHb8OY@LbipHyc>FopCE!Hd66l*lQHt<7VbMa^aAo92V&HReQf zq*-MCyJ>yX#HQFL#&py)-L%0p(-dy1G2*5XrU=tJ<7wj|;{iZNjHirW8)q5Aji|A~ z@P}coVWuJ3kZE{eXfjw0jfTgDONJ|;^&Yg%hGN4*LxsU(=x*cV($IoiA}tFo+bt(7S(bN}{;e5c-yN;ewxw-3;AIzD z!>y~V_pR+#NqbcL*7h6i_V$F1td91MA)P6me{??T% z*w&dy9U?$`pkvYH=$~i{8ivip(lIL*CfFdjE$|BZ3#SOb7G4q3z)GGi+9bLza*HP5 z-{N24KjDvYC9#_Lk;ow$2#t8YI76H#wuli)uw<#^A4!)4lJXLbbcXbM={aex)F73R z@#H2ljl4o;lNK^kwo#TUyC8cb<7Ls{?k|&nE&oQoR-P)qFZWZ71v;@;@uT9H;s?b= zfLpWzQuYFrpiERwSI$wcRi-O*m8?=h1ylX0QPe7GC-npMEtNuT2DF>nNBu(GqRJ@~ zWufw@zp01RW2%a>QF1@RuY+o&3=~7LR0q{ey#DLd#JV4 z6lxx|m0Cazpe)Ll%BRXgQQKlLqM>KtD@A zm-WrIp6FNL!$gV?8mU2T%%l3 zE}5&!`IqyM^K)mUQ{}WdS{(NrnT`XF-Hus~K@JRf9VPY)_8s=Q_D}4i>;vqm-NMyy z_1p{YEVqeU#ZBP`a2Qu-dt^Ir+ilxqn`le2CE7*-|3qx#**3O;&1ZjQcd^Ore0Dnf zF&oW>vL435R59D}~NdNI9*{+ixEC($~3 z49F({-HxX}qCcjm(&ItjaC$!d1)U0LKYfGFrQgx7fP6h&OF1r`UF+TDP4i}Z8E=?xJh&@qzDK@BpMoF3C-W!xXS|tr@)~Fn zbQZb^X&-tO)uSwmU_G(X*kq{pC&(1?IGF`d z?k;(mJVR!JS_5e!o5&*4Otz4AvW9F1URoo_t3huQ*#(Futz;`mRb&~NOFkp-kvGWy zk-v}`8LMWE#>3!H{D3H+UgD7(^f{p&y`mAns)4-|`Rmz5E=$8!zXJ zeLwkT`htCK?_2K;aC_!?`+B{e7XV9^dHQ=Ko(lIp_fhu-x6U2p?gY2+i0e~uC!Nmc z&YR9-&fU(*&TdYFA)8_#bg0QNdx5t&~t-12RLpq|1kMX9YZr+Oe@m{ zG?!p0R?Bv0W7uizY<3yDi2aH^#QwzIU~jWe*#fqX|5*^V6U(3#dg{r>X_izUHeB*3%V!+~??po(ccm3(AaY^07fgZ1O?{%MbKX+HUMIM!BkY}oAt>=R0 zmB;77yiq`#w|jr{>b+Jk3=m)K8|s@3;zn0|dS9mx;|KGT_|^O#{&)U4-@to$1&AGv z0_VFIx&rB;dWeHO5Cw;USYI;G@YC=U_yJr8+u>kj6tGdhK(-=>k(0t@xVk5C}*ko)hC`V!OfQDeh zKy4DX6kCVw#5Q7UuvJ(xsL#aaVB@g~*mQ75=VP+~$2c&u0^1JSGeK(__9-?S`vgeR zf%yZmK_Gq>g+*Y!v1lwDe2YVC!CP5^K1KgRucMFA+h`8Jq#Ni3@P6(>lhL{8Ds%(- z1v(qmftOT_b|EdmqN)YEDFk#6$wqD=_mM2*M`RDO3R!|oL}noKkwwS?fOOH|%?(E) zKvE+C2!b$h4XlS>f;ebC{1l|y@J0A{_#Av0pyOUR72XW*g1-W>ydCgHI0fDV?*pyP J@KSgg{D0Wnvl0LR literal 0 HcmV?d00001 From a9f7f458f6c529e5fd570fde20be3be3521f85d8 Mon Sep 17 00:00:00 2001 From: Pete Savage Date: Thu, 22 Jul 2021 07:29:37 +0100 Subject: [PATCH 139/193] Fixed import for CPP 17 --- plugins/common/plugin/NativeHelpers.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/common/plugin/NativeHelpers.cpp b/plugins/common/plugin/NativeHelpers.cpp index 7df142922..8f7f0aee9 100644 --- a/plugins/common/plugin/NativeHelpers.cpp +++ b/plugins/common/plugin/NativeHelpers.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #if defined(_WIN32) #include From 7ddebc5c3dd59e03b3e344f02b1e8a65ddb6db7d Mon Sep 17 00:00:00 2001 From: Pete Savage Date: Thu, 22 Jul 2021 15:51:00 +0100 Subject: [PATCH 140/193] Fix simde finding on non pkg_config enabled installations --- cmake/SfizzDeps.cmake | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/cmake/SfizzDeps.cmake b/cmake/SfizzDeps.cmake index fbf5f8e00..2b8fb8b03 100644 --- a/cmake/SfizzDeps.cmake +++ b/cmake/SfizzDeps.cmake @@ -1,3 +1,5 @@ +include(CheckCXXSourceCompiles) + # Find system threads find_package(Threads REQUIRED) @@ -105,10 +107,25 @@ add_subdirectory("external/st_audiofile" EXCLUDE_FROM_ALL) add_library(sfizz_simde INTERFACE) add_library(sfizz::simde ALIAS sfizz_simde) if(SFIZZ_USE_SYSTEM_SIMDE) - find_package(PkgConfig REQUIRED) - pkg_check_modules(SIMDE "simde" REQUIRED) - target_include_directories(sfizz_simde INTERFACE "${SIMDE_INCLUDE_DIRS}") - if(NOT SIMDE_VERSION OR SIMDE_VERSION VERSION_LESS_EQUAL "0.7.2") + find_path(SIMDE_INCLUDE_DIR "simde/simde-features.h") + if(NOT SIMDE_INCLUDE_DIR) + message(FATAL_ERROR "Cannot find simde") + endif() + target_include_directories(sfizz_simde INTERFACE "${SIMDE_INCLUDE_DIR}") + + function(sfizz_ensure_simde_version result major minor micro) + set(CMAKE_REQUIRED_INCLUDES "${SIMDE_INCLUDE_DIR}") + check_cxx_source_compiles( + "#include +#if SIMDE_VERSION < HEDLEY_VERSION_ENCODE(${major}, ${minor}, ${micro}) +# error Version check failed +#endif +int main() { return 0; }" + "${result}") + endfunction() + + sfizz_ensure_simde_version(SFIZZ_SIMDE_AT_LEAST_0_7_3 0 7 3) + if(NOT SFIZZ_SIMDE_AT_LEAST_0_7_3) message(WARNING "The version of SIMDe on this system has known issues. \ It is recommended to either update if a newer version is available, or use the \ version bundled with this package. Refer to following issues: \ From 47f45c6f77351ad11ecdc3944a432a8fd8190c0d Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 24 Jul 2021 10:44:19 +0200 Subject: [PATCH 141/193] Port values don't need to be saved/restored But lv2:Parameters do ! --- plugins/lv2/sfizz.cpp | 58 ------------------------------------------- 1 file changed, 58 deletions(-) diff --git a/plugins/lv2/sfizz.cpp b/plugins/lv2/sfizz.cpp index 6f22f8fcb..d51577394 100644 --- a/plugins/lv2/sfizz.cpp +++ b/plugins/lv2/sfizz.cpp @@ -1285,9 +1285,6 @@ restore(LV2_Handle instance, // Set default values sfizz_lv2_get_default_sfz_path(self, self->sfz_file_path, MAX_PATH_SIZE); sfizz_lv2_get_default_scala_path(self, self->scala_file_path, MAX_PATH_SIZE); - self->num_voices = DEFAULT_VOICES; - self->preload_size = DEFAULT_PRELOAD; - self->oversampling = DEFAULT_OVERSAMPLING; // Fetch back the saved file path, if any size_t size; @@ -1336,28 +1333,6 @@ restore(LV2_Handle instance, } } - value = retrieve(handle, self->sfizz_num_voices_uri, &size, &type, &val_flags); - if (value) - { - int num_voices = *(const int *)value; - if (num_voices > 0 && num_voices <= MAX_VOICES) - self->num_voices = num_voices; - } - - value = retrieve(handle, self->sfizz_preload_size_uri, &size, &type, &val_flags); - if (value) - { - unsigned int preload_size = *(const unsigned int *)value; - self->preload_size = preload_size; - } - - value = retrieve(handle, self->sfizz_oversampling_uri, &size, &type, &val_flags); - if (value) - { - sfizz_oversampling_factor_t oversampling = *(const sfizz_oversampling_factor_t *)value; - self->oversampling = oversampling; - } - // Collect all CC values present in the state std::unique_ptr[]> cc_values( new absl::optional[sfz::config::numCCs]); @@ -1398,15 +1373,6 @@ restore(LV2_Handle instance, "[sfizz] Error while restoring the scale %s\n", self->scala_file_path); } - lv2_log_note(&self->logger, "[sfizz] Restoring the number of voices to %d\n", self->num_voices); - sfizz_set_num_voices(self->synth, self->num_voices); - - lv2_log_note(&self->logger, "[sfizz] Restoring the preload size to %d\n", self->preload_size); - sfizz_set_preload_size(self->synth, self->preload_size); - - lv2_log_note(&self->logger, "[sfizz] Restoring the oversampling to %d\n", self->oversampling); - sfizz_set_oversampling_factor(self->synth, self->oversampling); - // Override default automation values with these from the state file for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { absl::optional value = cc_values[cc]; @@ -1484,30 +1450,6 @@ save(LV2_Handle instance, if (map_path) free_path->free_path(free_path->handle, (char *)path); - // Save the number of voices - store(handle, - self->sfizz_num_voices_uri, - &self->num_voices, - sizeof(int), - self->atom_int_uri, - LV2_STATE_IS_POD); - - // Save the preload size - store(handle, - self->sfizz_preload_size_uri, - &self->preload_size, - sizeof(unsigned int), - self->atom_int_uri, - LV2_STATE_IS_POD); - - // Save the preload size - store(handle, - self->sfizz_oversampling_uri, - &self->oversampling, - sizeof(int), - self->atom_int_uri, - LV2_STATE_IS_POD); - // Save the CCs (used only) self->sfz_blob_mutex->lock(); const InstrumentDescription desc = parseDescriptionBlob( From 14c9864b25c6fc6d74708ce23ee73d6ee895b55a Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 24 Jul 2021 17:29:39 +0200 Subject: [PATCH 142/193] Tweak the shading rectangle size --- plugins/editor/src/editor/GUIComponents.cpp | 4 +++- plugins/editor/src/editor/GUIComponents.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/editor/src/editor/GUIComponents.cpp b/plugins/editor/src/editor/GUIComponents.cpp index 5b3f743dc..752e6b7e8 100644 --- a/plugins/editor/src/editor/GUIComponents.cpp +++ b/plugins/editor/src/editor/GUIComponents.cpp @@ -816,13 +816,15 @@ void SKnobCCBox::updateViewSizes() // remove knob side areas CCoord side = std::max(0.0, knobSize_.getWidth() - knobSize_.getHeight()); knobSize_.extend(-0.5 * side, 0.0); + shadingRectangleSize_ = knobSize_; + shadingRectangleSize_.bottom -= ypad; // label_->setViewSize(nameLabelSize_); knob_->setViewSize(knobSize_); ccLabel_->setViewSize(ccLabelSize_); valueEdit_->setViewSize(valueEditSize_); - shadingRectangle_->setViewSize(knobSize_); + shadingRectangle_->setViewSize(shadingRectangleSize_); invalid(); } diff --git a/plugins/editor/src/editor/GUIComponents.h b/plugins/editor/src/editor/GUIComponents.h index f1769db7a..c8cbd162d 100644 --- a/plugins/editor/src/editor/GUIComponents.h +++ b/plugins/editor/src/editor/GUIComponents.h @@ -348,6 +348,7 @@ class SKnobCCBox : public CViewContainer, ViewListenerAdapter { SharedPointer menuEntry_; CRect nameLabelSize_; CRect knobSize_; + CRect shadingRectangleSize_; CRect ccLabelSize_; CRect valueEditSize_; CRect rectangleSize_; From 57a41fd0a752ce06ab6acea51f0444ab2c160f01 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 24 Jul 2021 19:09:26 +0200 Subject: [PATCH 143/193] Add a check for sustain and sostenuto in the LV2 If the midi message is a sustain or sostenuto, pass it on regardless of the plugin-side automation compile time switch. Also hide sustain/sostenuto CCs from the UI controls; most of the time they're for pedal noises and things like this. --- .../common/plugin/InstrumentDescription.cpp | 3 ++ plugins/common/plugin/InstrumentDescription.h | 1 + plugins/lv2/sfizz.cpp | 30 +++++++++++++++---- plugins/lv2/sfizz_lv2_plugin.h | 3 ++ plugins/lv2/sfizz_ui.cpp | 2 +- plugins/vst/SfizzVstEditor.cpp | 2 +- src/sfizz/Synth.cpp | 6 +++- src/sfizz/SynthMessaging.cpp | 7 +++++ src/sfizz/SynthPrivate.h | 1 + 9 files changed, 46 insertions(+), 9 deletions(-) diff --git a/plugins/common/plugin/InstrumentDescription.cpp b/plugins/common/plugin/InstrumentDescription.cpp index 7378fd03c..071655b10 100644 --- a/plugins/common/plugin/InstrumentDescription.cpp +++ b/plugins/common/plugin/InstrumentDescription.cpp @@ -103,6 +103,7 @@ std::string getDescriptionBlob(sfizz_synth_t* handle) synth.sendMessage(*client, 0, "/key/slots", "", nullptr); synth.sendMessage(*client, 0, "/sw/last/slots", "", nullptr); synth.sendMessage(*client, 0, "/cc/slots", "", nullptr); + synth.sendMessage(*client, 0, "/sustain_or_sostenuto/slots", "", nullptr); blob.shrink_to_fit(); return blob; @@ -152,6 +153,8 @@ InstrumentDescription parseDescriptionBlob(absl::string_view blob) copyArgToBitSpan(args[0], desc.keyswitchUsed.span()); else if (Messages::matchOSC("/cc/slots", path, indices) && !strcmp(sig, "b")) copyArgToBitSpan(args[0], desc.ccUsed.span()); + else if (Messages::matchOSC("/sustain_or_sostenuto/slots", path, indices) && !strcmp(sig, "b")) + copyArgToBitSpan(args[0], desc.sustainOrSostenuto.span()); else if (Messages::matchOSC("/key&/label", path, indices) && !strcmp(sig, "s")) desc.keyLabel[indices[0]] = args[0].s; else if (Messages::matchOSC("/sw/last/&/label", path, indices) && !strcmp(sig, "s")) diff --git a/plugins/common/plugin/InstrumentDescription.h b/plugins/common/plugin/InstrumentDescription.h index b943dcba7..094d06b3b 100644 --- a/plugins/common/plugin/InstrumentDescription.h +++ b/plugins/common/plugin/InstrumentDescription.h @@ -26,6 +26,7 @@ struct InstrumentDescription { std::string image; BitArray<128> keyUsed {}; BitArray<128> keyswitchUsed {}; + BitArray<128> sustainOrSostenuto {}; BitArray ccUsed {}; std::array keyLabel {}; std::array keyswitchLabel {}; diff --git a/plugins/lv2/sfizz.cpp b/plugins/lv2/sfizz.cpp index 6f22f8fcb..a3afccf62 100644 --- a/plugins/lv2/sfizz.cpp +++ b/plugins/lv2/sfizz.cpp @@ -683,6 +683,12 @@ sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, int delay, const LV2_Atom_Obj } } +static bool +sfizz_is_sustain_or_sostenuto(sfizz_plugin_t* self, unsigned cc) +{ + return (self->sustain_or_sostenuto[cc / 8] & (1 << (cc % 8))); +} + static void sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev) { @@ -704,14 +710,23 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev) (int)msg[1], msg[2]); break; - // Note(jpc) CC must be mapped by host, not handled here. - // See LV2 midi:binding. -#if defined(SFIZZ_LV2_PSA) case LV2_MIDI_MSG_CONTROLLER: { unsigned cc = msg[1]; float value = float(msg[2]) * (1.0f / 127.0f); + // Send + if (sfizz_is_sustain_or_sostenuto(self, cc)) { + sfizz_automate_hdcc(self->synth, + (int)ev->time.frames, + (int)cc, + value); + break; + } + +// Note(jpc) CC must be mapped by host, not handled here. +// See LV2 midi:binding. +#if defined(SFIZZ_LV2_PSA) switch (cc) { default: @@ -733,9 +748,9 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev) sfizz_all_sound_off(self->synth); break; } +#endif } break; -#endif case LV2_MIDI_MSG_CHANNEL_PRESSURE: sfizz_send_channel_aftertouch(self->synth, (int)ev->time.frames, @@ -1218,7 +1233,7 @@ sfizz_lv2_update_sfz_info(sfizz_plugin_t *self) // const InstrumentDescription desc = parseDescriptionBlob(blob); for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { - if (desc.ccUsed.test(cc)) { + if (desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc)) { // Mark all the used CCs for automation with default values self->ccauto[cc] = desc.ccDefault[cc]; self->have_ccauto = true; @@ -1226,6 +1241,9 @@ sfizz_lv2_update_sfz_info(sfizz_plugin_t *self) self->cc_current[cc] = desc.ccDefault[cc]; } } + + memcpy(self->sustain_or_sostenuto, desc.sustainOrSostenuto.data(), + sizeof(self->sustain_or_sostenuto)); } static bool @@ -1515,7 +1533,7 @@ save(LV2_Handle instance, self->sfz_blob_mutex->unlock(); for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { - if (desc.ccUsed.test(cc)) { + if (desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc)) { LV2_URID urid = sfizz_lv2_ccmap_map(self->ccmap, int(cc)); store(handle, urid, diff --git a/plugins/lv2/sfizz_lv2_plugin.h b/plugins/lv2/sfizz_lv2_plugin.h index 903876844..9c1918f96 100644 --- a/plugins/lv2/sfizz_lv2_plugin.h +++ b/plugins/lv2/sfizz_lv2_plugin.h @@ -127,6 +127,9 @@ struct sfizz_plugin_t const uint8_t *volatile sfz_blob_data {}; volatile uint32_t sfz_blob_size {}; + // Sostenuto or sustain + char sustain_or_sostenuto[16] {}; + // Current CC values in the synth (synchronized by `synth_mutex`) // updated by hdcc or file load float *cc_current {}; diff --git a/plugins/lv2/sfizz_ui.cpp b/plugins/lv2/sfizz_ui.cpp index 3756c7f83..7e8319894 100644 --- a/plugins/lv2/sfizz_ui.cpp +++ b/plugins/lv2/sfizz_ui.cpp @@ -447,7 +447,7 @@ sfizz_ui_update_description(sfizz_ui_t *self, const InstrumentDescription& desc) } for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { - bool ccUsed = desc.ccUsed.test(cc); + bool ccUsed = desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc); self->uiReceiveValue(editIdForCCUsed(cc), float(ccUsed)); if (ccUsed) { self->uiReceiveValue(editIdForCCDefault(cc), desc.ccDefault[cc]); diff --git a/plugins/vst/SfizzVstEditor.cpp b/plugins/vst/SfizzVstEditor.cpp index 9f9e614c6..5bb967250 100644 --- a/plugins/vst/SfizzVstEditor.cpp +++ b/plugins/vst/SfizzVstEditor.cpp @@ -246,7 +246,7 @@ bool SfizzVstEditor::processUpdate(FUnknown* changedUnknown, int32 message) } for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { - bool ccUsed = desc.ccUsed.test(cc); + bool ccUsed = desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc); uiReceiveValue(editIdForCCUsed(int(cc)), float(ccUsed)); if (ccUsed) { uiReceiveValue(editIdForCCDefault(int(cc)), desc.ccDefault[cc]); diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index fcc11f516..dc76759e4 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -271,6 +271,7 @@ void Synth::Impl::clear() filePool.setRamLoading(config::loadInRam); clearCCLabels(); currentUsedCCs_.clear(); + sustainOrSostenuto_.clear(); changedCCsThisCycle_.clear(); changedCCsLastCycle_.clear(); clearKeyLabels(); @@ -2113,8 +2114,11 @@ void Synth::Impl::collectUsedCCsFromModulations(BitArray& usedCC BitArray Synth::Impl::collectAllUsedCCs() { BitArray used; - for (const LayerPtr& layerPtr : layers_) + for (const LayerPtr& layerPtr : layers_) { collectUsedCCsFromRegion(used, layerPtr->getRegion()); + sustainOrSostenuto_.set(layerPtr->region_.sustainCC); + sustainOrSostenuto_.set(layerPtr->region_.sostenutoCC); + } collectUsedCCsFromModulations(used, resources_.getModMatrix()); return used; } diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index 36f4bc032..4d75451af 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -180,6 +180,13 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co client.receive<'b'>(delay, path, &blob); } break; + MATCH("/sustain_or_sostenuto/slots", "") { + const BitArray<128>& sustainOrSostenuto = impl.sustainOrSostenuto_; + sfizz_blob_t blob { sustainOrSostenuto.data(), + static_cast(sustainOrSostenuto.byte_size()) }; + client.receive<'b'>(delay, path, &blob); + } break; + //---------------------------------------------------------------------- MATCH("/mem/buffers", "") { diff --git a/src/sfizz/SynthPrivate.h b/src/sfizz/SynthPrivate.h index 2597f417b..44fd3b542 100644 --- a/src/sfizz/SynthPrivate.h +++ b/src/sfizz/SynthPrivate.h @@ -248,6 +248,7 @@ struct Synth::Impl final: public Parser::Listener { std::map keyLabelsMap_; BitArray<128> keySlots_; BitArray<128> swLastSlots_; + BitArray<128> sustainOrSostenuto_; std::vector keyswitchLabels_; std::map keyswitchLabelsMap_; From 7f4c8586567f078440c120f5248ba2ab294141c0 Mon Sep 17 00:00:00 2001 From: Pete Savage Date: Sat, 24 Jul 2021 21:21:45 +0100 Subject: [PATCH 144/193] Make kissfft optional --- cmake/SfizzDeps.cmake | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/cmake/SfizzDeps.cmake b/cmake/SfizzDeps.cmake index 2b8fb8b03..8713a08c0 100644 --- a/cmake/SfizzDeps.cmake +++ b/cmake/SfizzDeps.cmake @@ -165,13 +165,31 @@ add_library(sfizz::hiir_polyphase_iir2designer ALIAS sfizz_hiir_polyphase_iir2de target_link_libraries(sfizz_hiir_polyphase_iir2designer PUBLIC sfizz::hiir) # The kissfft library -add_library(sfizz_kissfft STATIC - "src/external/kiss_fft/kiss_fft.c" - "src/external/kiss_fft/tools/kiss_fftr.c") +if (SFIZZ_USE_SYSTEM_KISS_FFT) + find_path(KISSFFT_INCLUDE_DIR "kiss_fft.h" PATH_SUFFIXES "kissfft") + find_path(KISSFFTR_INCLUDE_DIR "kiss_fftr.h" PATH_SUFFIXES "kissfft") + find_library(KISSFFT_FFTR_LIBRARY "kiss_fftr_float" KISSFFTR_INCLUDE_DIR) + find_library(KISSFFT_FFT_LIBRARY "kiss_fft_float" KISSFFT_INCLUDE_DIR) + add_library(sfizz_kissfft INTERFACE) + if(NOT KISSFFT_FFT_LIBRARY) + message(FATAL_ERROR "Cannot find kiss fft") + endif() + if(NOT KISSFFT_FFTR_LIBRARY) + message(FATAL_ERROR "Cannot find kiss fftr") + endif() + target_include_directories(sfizz_kissfft INTERFACE "${KISSFFTR_INCLUDE_DIR}") + target_include_directories(sfizz_kissfft INTERFACE "${KISSFFT_INCLUDE_DIR}") + target_link_libraries(sfizz_kissfft INTERFACE "${KISSFFT_FFTR_LIBRARY}") + target_link_libraries(sfizz_kissfft INTERFACE "${KISSFFT_FFT_LIBRARY}") +else() + add_library(sfizz_kissfft STATIC + "src/external/kiss_fft/kiss_fft.c" + "src/external/kiss_fft/tools/kiss_fftr.c") + target_include_directories(sfizz_kissfft + PUBLIC "src/external/kiss_fft" + PUBLIC "src/external/kiss_fft/tools") +endif() add_library(sfizz::kissfft ALIAS sfizz_kissfft) -target_include_directories(sfizz_kissfft - PUBLIC "src/external/kiss_fft" - PUBLIC "src/external/kiss_fft/tools") # The cephes library add_library(sfizz_cephes STATIC From b52a49e67ee32e8da6280b3f536ca73994ce3bff Mon Sep 17 00:00:00 2001 From: Pete Savage Date: Sat, 24 Jul 2021 21:24:38 +0100 Subject: [PATCH 145/193] Added option for kissfft --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6805f67dc..994da3c86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ option_ex (SFIZZ_USE_SNDFILE "Enable use of the sndfile library" OFF) option_ex (SFIZZ_USE_VCPKG "Assume that sfizz is build using vcpkg" OFF) option_ex (SFIZZ_USE_SYSTEM_ABSEIL "Use Abseil libraries preinstalled on system" OFF) option_ex (SFIZZ_USE_SYSTEM_SIMDE "Use SIMDe libraries preinstalled on system" OFF) +option_ex (SFIZZ_USE_SYSTEM_KISS_FFT "Use KISS FFT libraries preinstalled on system" OFF) option_ex (SFIZZ_STATIC_DEPENDENCIES "Link dependencies statically" OFF) option_ex (SFIZZ_RELEASE_ASSERTS "Forced assertions in release builds" OFF) option_ex (SFIZZ_PROFILE_BUILD "Profile the build time" OFF) From c548bccb71f504ef32054b2a7079c2b7d1f53ad0 Mon Sep 17 00:00:00 2001 From: Pete Savage Date: Sun, 25 Jul 2021 14:29:56 +0100 Subject: [PATCH 146/193] Add ability to use system pugixml --- cmake/SfizzDeps.cmake | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cmake/SfizzDeps.cmake b/cmake/SfizzDeps.cmake index 8713a08c0..e5d14ca8e 100644 --- a/cmake/SfizzDeps.cmake +++ b/cmake/SfizzDeps.cmake @@ -139,9 +139,18 @@ if(TARGET sfizz::openmp) endif() # The pugixml library -add_library(sfizz_pugixml STATIC "src/external/pugixml/src/pugixml.cpp") +if(SFIZZ_USE_SYSTEM_PUGIXML) + find_package(PkgConfig REQUIRED) + pkg_check_modules(PUGIXML "pugixml" REQUIRED) + add_library(sfizz_pugixml INTERFACE) + target_include_directories(sfizz_pugixml INTERFACE ${PUGIXML_INCLUDE_DIRS}) + target_link_libraries(sfizz_pugixml INTERFACE ${PUGIXML_LIBRARIES}) + link_directories(${PUGIXML_LIBRARY_DIRS}) +else() + add_library(sfizz_pugixml STATIC "src/external/pugixml/src/pugixml.cpp") + target_include_directories(sfizz_pugixml PUBLIC "src/external/pugixml/src") +endif() add_library(sfizz::pugixml ALIAS sfizz_pugixml) -target_include_directories(sfizz_pugixml PUBLIC "src/external/pugixml/src") # The spline library add_library(sfizz_spline STATIC "src/external/spline/spline/spline.cpp") From 5bca4394a9a9c1a936c5cf3c908a89d664ac3fbc Mon Sep 17 00:00:00 2001 From: Pete Savage Date: Sun, 25 Jul 2021 14:30:44 +0100 Subject: [PATCH 147/193] Add option for system pugixml --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 994da3c86..48a157b09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ option_ex (SFIZZ_USE_VCPKG "Assume that sfizz is build using vcpkg" OFF) option_ex (SFIZZ_USE_SYSTEM_ABSEIL "Use Abseil libraries preinstalled on system" OFF) option_ex (SFIZZ_USE_SYSTEM_SIMDE "Use SIMDe libraries preinstalled on system" OFF) option_ex (SFIZZ_USE_SYSTEM_KISS_FFT "Use KISS FFT libraries preinstalled on system" OFF) +option_ex (SFIZZ_USE_SYSTEM_PUGIXML "Use pugixml libraries preinstalled on system" OFF) option_ex (SFIZZ_STATIC_DEPENDENCIES "Link dependencies statically" OFF) option_ex (SFIZZ_RELEASE_ASSERTS "Forced assertions in release builds" OFF) option_ex (SFIZZ_PROFILE_BUILD "Profile the build time" OFF) From 14e6c3359c7368612cb66d10b4b8a8508646b74a Mon Sep 17 00:00:00 2001 From: Pete Savage Date: Sun, 25 Jul 2021 14:44:56 +0100 Subject: [PATCH 148/193] Added option to use system cxxopts --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 48a157b09..49bde5d2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ option_ex (SFIZZ_USE_SYSTEM_ABSEIL "Use Abseil libraries preinstalled on system" option_ex (SFIZZ_USE_SYSTEM_SIMDE "Use SIMDe libraries preinstalled on system" OFF) option_ex (SFIZZ_USE_SYSTEM_KISS_FFT "Use KISS FFT libraries preinstalled on system" OFF) option_ex (SFIZZ_USE_SYSTEM_PUGIXML "Use pugixml libraries preinstalled on system" OFF) +option_ex (SFIZZ_USE_SYSTEM_CXXOPTS "Use CXXOPTS libraries preinstalled on system" OFF) option_ex (SFIZZ_STATIC_DEPENDENCIES "Link dependencies statically" OFF) option_ex (SFIZZ_RELEASE_ASSERTS "Forced assertions in release builds" OFF) option_ex (SFIZZ_PROFILE_BUILD "Profile the build time" OFF) From 7c4dbe7e35e4948158b898ea6724671dba50b70d Mon Sep 17 00:00:00 2001 From: Pete Savage Date: Sun, 25 Jul 2021 14:45:45 +0100 Subject: [PATCH 149/193] Added option to use system cxxopts --- cmake/SfizzDeps.cmake | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/cmake/SfizzDeps.cmake b/cmake/SfizzDeps.cmake index e5d14ca8e..ea277a40a 100644 --- a/cmake/SfizzDeps.cmake +++ b/cmake/SfizzDeps.cmake @@ -67,9 +67,19 @@ add_library(sfizz::jsl ALIAS sfizz_jsl) target_include_directories(sfizz_jsl INTERFACE "external/jsl/include") # The cxxopts library -add_library(sfizz_cxxopts INTERFACE) +if(SFIZZ_USE_SYSTEM_CXXOPTS) + find_path(CXXOPTS_INCLUDE_DIR "cxxopts.hpp") + if(NOT CXXOPTS_INCLUDE_DIR) + message(FATAL_ERROR "Cannot find cxxopts") + endif() + add_library(sfizz_cxxopts INTERFACE) + target_include_directories(sfizz_cxxopts INTERFACE "${CXXOPTS_INCLUDE_DIR}") +else() + add_library(sfizz_cxxopts INTERFACE) + add_library(sfizz::cxxopts ALIAS sfizz_cxxopts) + target_include_directories(sfizz_cxxopts INTERFACE "external/cxxopts") +endif() add_library(sfizz::cxxopts ALIAS sfizz_cxxopts) -target_include_directories(sfizz_cxxopts INTERFACE "external/cxxopts") # The sndfile library if(SFIZZ_USE_SNDFILE OR SFIZZ_DEMOS OR SFIZZ_DEVTOOLS OR SFIZZ_BENCHMARKS) From be2c59c691663fdeb2c33fef503304c621edf002 Mon Sep 17 00:00:00 2001 From: Pete Savage Date: Mon, 26 Jul 2021 20:30:52 +0100 Subject: [PATCH 150/193] Add source permissions to preserve file perms --- plugins/lv2/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/lv2/CMakeLists.txt b/plugins/lv2/CMakeLists.txt index a8d97bb52..4bf680516 100644 --- a/plugins/lv2/CMakeLists.txt +++ b/plugins/lv2/CMakeLists.txt @@ -117,7 +117,8 @@ endif() # Installation if(NOT MSVC) install(DIRECTORY ${PROJECT_BINARY_DIR} DESTINATION ${LV2PLUGIN_INSTALL_DIR} - COMPONENT "lv2") + COMPONENT "lv2" + USE_SOURCE_PERMISSIONS) bundle_dylibs(lv2 "${LV2PLUGIN_INSTALL_DIR}/${PROJECT_NAME}.lv2/Contents/Binary/sfizz.so" COMPONENT "lv2") From c3bbe3b04e4a799bbff8c776dad7229cd857185a Mon Sep 17 00:00:00 2001 From: Pete Savage Date: Mon, 26 Jul 2021 20:31:26 +0100 Subject: [PATCH 151/193] Add source permissions to preserve file perms --- plugins/puredata/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/puredata/CMakeLists.txt b/plugins/puredata/CMakeLists.txt index c8a33a65d..c8c6fd1f6 100644 --- a/plugins/puredata/CMakeLists.txt +++ b/plugins/puredata/CMakeLists.txt @@ -49,7 +49,8 @@ copy_puredata_resources(sfizz_puredata # Installation if(NOT MSVC) install(DIRECTORY "${PUREDATA_BINARY_DIR}" DESTINATION "${PDPLUGIN_INSTALL_DIR}" - COMPONENT "puredata") + COMPONENT "puredata" + USE_SOURCE_PERMISSIONS) bundle_dylibs(puredata "${PDPLUGIN_INSTALL_DIR}/sfizz/sfizz${PUREDATA_SUFFIX}" COMPONENT "puredata") From 30c2881c2e389b64471b241d6a9f597954cad4ca Mon Sep 17 00:00:00 2001 From: Pete Savage Date: Mon, 26 Jul 2021 20:32:48 +0100 Subject: [PATCH 152/193] Add source permissions to preserve file perms --- plugins/vst/CMakeLists.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/vst/CMakeLists.txt b/plugins/vst/CMakeLists.txt index 894975f45..635658098 100644 --- a/plugins/vst/CMakeLists.txt +++ b/plugins/vst/CMakeLists.txt @@ -136,7 +136,8 @@ if(SFIZZ_VST) if(NOT MSVC) install(DIRECTORY "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}" DESTINATION "${VSTPLUGIN_INSTALL_DIR}" - COMPONENT "vst") + COMPONENT "vst" + USE_SOURCE_PERMISSIONS) bundle_dylibs(vst "${VSTPLUGIN_INSTALL_DIR}/${VSTPLUGIN_BUNDLE_NAME}/Contents/MacOS/sfizz" COMPONENT "vst") @@ -284,7 +285,8 @@ elseif(SFIZZ_AU) if(AUPLUGIN_INSTALL_DIR) install(DIRECTORY "${PROJECT_BINARY_DIR}/${AUPLUGIN_BUNDLE_NAME}" DESTINATION "${AUPLUGIN_INSTALL_DIR}" - COMPONENT "au") + COMPONENT "au" + USE_SOURCE_PERMISSIONS) bundle_dylibs(au "${AUPLUGIN_INSTALL_DIR}/${AUPLUGIN_BUNDLE_NAME}/Contents/MacOS/sfizz" COMPONENT "au") @@ -355,7 +357,8 @@ if(SFIZZ_VST2) if(VST2PLUGIN_INSTALL_DIR) install(DIRECTORY "${PROJECT_BINARY_DIR}/${VST2PLUGIN_BUNDLE_NAME}" DESTINATION "${VST2PLUGIN_INSTALL_DIR}" - COMPONENT "vst2") + COMPONENT "vst2" + USE_SOURCE_PERMISSIONS) if(APPLE) bundle_dylibs(vst2 "${VST2PLUGIN_INSTALL_DIR}/${VST2PLUGIN_BUNDLE_NAME}/Contents/Binary/sfizz.${CMAKE_SHARED_MODULE_SUFFIX}" From 9edafdd7885ed9b28e7ef287794d110a7f6d5f7a Mon Sep 17 00:00:00 2001 From: Pete Savage Date: Tue, 27 Jul 2021 08:14:57 +0100 Subject: [PATCH 153/193] Update so version to align better with packaging guidelines As per - https://docs.fedoraproject.org/en-US/packaging-guidelines/#_downstream_so_name_versioning --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f78bf315d..555d8af61 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -332,7 +332,7 @@ if(SFIZZ_SHARED) target_include_directories(sfizz_shared PUBLIC .) target_link_libraries(sfizz_shared PRIVATE sfizz::internal) target_compile_definitions(sfizz_shared PRIVATE SFIZZ_EXPORT_SYMBOLS) - set_target_properties(sfizz_shared PROPERTIES SOVERSION "${PROJECT_VERSION_MAJOR}" OUTPUT_NAME "sfizz" PUBLIC_HEADER "sfizz.h;sfizz.hpp;sfizz_message.h") + set_target_properties(sfizz_shared PROPERTIES VERSION "${PROJECT_VERSION}" SOVERSION "${PROJECT_VERSION_MAJOR}" OUTPUT_NAME "sfizz" PUBLIC_HEADER "sfizz.h;sfizz.hpp;sfizz_message.h") sfizz_enable_lto_if_needed(sfizz_shared) if(NOT MSVC) From 5255b719bf8c9178bd85e93991a3d78a4f320abd Mon Sep 17 00:00:00 2001 From: Pete Savage Date: Tue, 27 Jul 2021 09:38:52 +0100 Subject: [PATCH 154/193] Update to correctly require win32/64 builds --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8341c3907..082ff7966 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -410,6 +410,8 @@ jobs: - build_for_mod - build_for_mingw32 - build_for_mingw64 + - build_for_win32 + - build_for_win64 - archive_source_code steps: - name: Set install name From 720a3a05fb021396dc6d55ecc72b3314136bb097 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Tue, 27 Jul 2021 17:13:37 +0200 Subject: [PATCH 155/193] Update Synth.cpp --- src/sfizz/Synth.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 468623b21..06a7ad052 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -637,8 +637,6 @@ void Synth::Impl::finalizeSfzLoad() if (allZeros) { region.sampleId.reset(new FileId("*silence")); region.hasWavetableSample = false; - } else { - region.hasWavetableSample &= true; } } } From 4769e35ef69d8e5931e57dd744788902146e1194 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Fri, 16 Jul 2021 16:48:32 +0200 Subject: [PATCH 156/193] Use glib's find in path --- plugins/editor/src/editor/NativeHelpers.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/plugins/editor/src/editor/NativeHelpers.cpp b/plugins/editor/src/editor/NativeHelpers.cpp index 1fdfcedd0..f089b258d 100644 --- a/plugins/editor/src/editor/NativeHelpers.cpp +++ b/plugins/editor/src/editor/NativeHelpers.cpp @@ -198,12 +198,21 @@ static std::vector createForkEnviron() return newEnv; } -static constexpr char zenityPath[] = "/usr/bin/zenity"; +static const std::string zenityPath = [] { + auto glibPath = g_find_program_in_path("zenity"); + if (glibPath) { + std::string s { glibPath }; + g_free(glibPath); + return s; + } else { + return std::string("/usr/bin/zenity"); + } +}(); bool askQuestion(const char *text) { char *argv[] = { - const_cast(zenityPath), + const_cast(zenityPath.c_str()), const_cast("--question"), const_cast("--text"), const_cast(text), @@ -236,7 +245,7 @@ bool askQuestion(const char *text) bool isZenityAvailable() { - return access(zenityPath, X_OK) == 0; + return access(zenityPath.c_str(), X_OK) == 0; } std::string getOperatingSystemName() From 06cf476fb3b391ff7f3f4038da214b22a53d9738 Mon Sep 17 00:00:00 2001 From: Elmo Date: Tue, 17 Aug 2021 16:57:07 +0300 Subject: [PATCH 157/193] Add failing test case for staccato off grops --- tests/SynthT.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 314795d41..b3f32f1a8 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -1568,6 +1568,26 @@ TEST_CASE("[Synth] Off by alone and repeated") REQUIRE( numPlayingVoices(synth) == 3 ); } +TEST_CASE("[Synth] Off by with staccato notes") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + + synth.loadSfzString(fs::current_path(), R"( + group=1 off_by=1 sample=*sine ampeg_release=2 + )"); + synth.noteOn(0, 60, 85); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 1 ); + synth.noteOff(0, 60, 85); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 0 ); + REQUIRE( numActiveVoices(synth) == 1 ); + synth.noteOn(0, 62, 85); + synth.renderBlock(buffer); + REQUIRE( numActiveVoices(synth) == 1 ); +} + TEST_CASE("[Synth] Off by same note and group") { From 3aae318e82dd675e65329d18c4841ea9bf5bc74e Mon Sep 17 00:00:00 2001 From: Elmo Date: Tue, 17 Aug 2021 16:57:33 +0300 Subject: [PATCH 158/193] Fix releasing notes not muted by off groups --- src/sfizz/Voice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 00bc58402..a8e0a1026 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -1666,7 +1666,7 @@ bool Voice::checkOffGroup(const Region* other, int delay, int noteNumber) noexce if (region == nullptr || other == nullptr) return false; - if (impl.released()) + if (impl.released() && region->offMode == OffMode::normal) return false; if ((impl.triggerEvent_.type == TriggerEventType::NoteOn From 4bca25d7a388bbf69490340c860bc0c8241e9b2b Mon Sep 17 00:00:00 2001 From: jofemodo Date: Thu, 19 Aug 2021 14:31:47 +0200 Subject: [PATCH 159/193] Add "num_voices" option to CLI. --- clients/jack_client.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/clients/jack_client.cpp b/clients/jack_client.cpp index 806ddadb1..7cecc9e73 100644 --- a/clients/jack_client.cpp +++ b/clients/jack_client.cpp @@ -151,7 +151,8 @@ static void done(int sig) ABSL_FLAG(std::string, client_name, "sfizz", "Jack client name"); ABSL_FLAG(std::string, oversampling, "1x", "Internal oversampling factor (value values are x1, x2, x4, x8)"); -ABSL_FLAG(uint32_t, preload_size, 8192, "Preloaded value"); +ABSL_FLAG(uint32_t, preload_size, 8192, "Preloaded size"); +ABSL_FLAG(uint32_t, num_voices, 32, "Num of voices"); ABSL_FLAG(bool, state, false, "Output the synth state in the jack loop"); int main(int argc, char** argv) @@ -167,12 +168,15 @@ int main(int argc, char** argv) const std::string clientName = absl::GetFlag(FLAGS_client_name); const std::string oversampling = absl::GetFlag(FLAGS_oversampling); const uint32_t preload_size = absl::GetFlag(FLAGS_preload_size); + const uint32_t num_voices = absl::GetFlag(FLAGS_num_voices); const bool verboseState = absl::GetFlag(FLAGS_state); std::cout << "Flags" << '\n'; std::cout << "- Client name: " << clientName << '\n'; std::cout << "- Oversampling: " << oversampling << '\n'; - std::cout << "- Preloaded Size: " << preload_size << '\n'; + std::cout << "- Preloaded size: " << preload_size << '\n'; + std::cout << "- Num of voices: " << num_voices << '\n'; + const auto factor = [&]() { if (oversampling == "x1") return 1; if (oversampling == "x2") return 2; @@ -189,6 +193,7 @@ int main(int argc, char** argv) sfz::Sfizz synth; synth.setOversamplingFactor(factor); synth.setPreloadSize(preload_size); + synth.setNumVoices(num_voices); const char *importFormat = nullptr; if (!sfizz_load_or_import_file(synth.handle(), filesToParse[0], &importFormat)) { From 29ff2b93f6b8a6457e9bbd3ccef0558b7f50c7cf Mon Sep 17 00:00:00 2001 From: jofemodo Date: Thu, 19 Aug 2021 20:06:37 +0200 Subject: [PATCH 160/193] Implement a basic CLI for loading soundfonts and setting engine parameters. --- clients/jack_client.cpp | 154 +++++++++++++++++++++++++++------------- 1 file changed, 105 insertions(+), 49 deletions(-) diff --git a/clients/jack_client.cpp b/clients/jack_client.cpp index 7cecc9e73..0c2fa61be 100644 --- a/clients/jack_client.cpp +++ b/clients/jack_client.cpp @@ -43,6 +43,9 @@ #include #include + +sfz::Sfizz synth; + static jack_port_t* midiInputPort; static jack_port_t* outputPort1; static jack_port_t* outputPort2; @@ -149,26 +152,103 @@ static void done(int sig) // exit(0); } + +bool load_instrument(const char *fpath) { + const char *importFormat = nullptr; + if (!sfizz_load_or_import_file(synth.handle(), fpath, &importFormat)) { + std::cout << "Could not load the instrument file: " << fpath << '\n'; + return false; + } + + std::cout << "Instrument loaded: " << fpath << '\n'; + std::cout << "===========================" << '\n'; + std::cout << "Total:" << '\n'; + std::cout << "\tMasters: " << synth.getNumMasters() << '\n'; + std::cout << "\tGroups: " << synth.getNumGroups() << '\n'; + std::cout << "\tRegions: " << synth.getNumRegions() << '\n'; + std::cout << "\tCurves: " << synth.getNumCurves() << '\n'; + std::cout << "\tPreloadedSamples: " << synth.getNumPreloadedSamples() << '\n'; +#if 0 // not currently in public API + std::cout << "===========================" << '\n'; + std::cout << "Included files:" << '\n'; + for (auto& file : synth.getParser().getIncludedFiles()) + std::cout << '\t' << file << '\n'; + std::cout << "===========================" << '\n'; + std::cout << "Defines:" << '\n'; + for (auto& define : synth.getParser().getDefines()) + std::cout << '\t' << define.first << '=' << define.second << '\n'; +#endif + std::cout << "===========================" << '\n'; + std::cout << "Unknown opcodes:"; + for (auto& opcode : synth.getUnknownOpcodes()) + std::cout << opcode << ','; + std::cout << '\n'; + if (importFormat) { + std::cout << "===========================" << '\n'; + std::cout << "Import format: " << importFormat << '\n'; + } + // std::cout << std::flush; + + return true; +} + + +void cli_thread_proc() { + while (!shouldClose) { + std::cout << "\n> "; + + std::string command; + std::getline(std::cin, command); + std::size_t pos = command.find(" "); + std::string kw = command.substr(0, pos); + std::string args = command.substr(pos+1); + + if (kw=="load_instrument") { + load_instrument(args.c_str()); + } + else if (kw=="set_preload_size") { + try { + synth.setPreloadSize(stoi(args)); + } catch (...) { + std::cout << "ERROR: Can't set preload size!\n"; + } + } + else if (kw=="set_voices") { + try { + synth.setNumVoices(stoi(args)); + } catch (...) { + std::cout << "ERROR: Can't set num of voices!\n"; + } + } + else if (kw=="quit") { + shouldClose = true; + } + else if (kw.size()>0){ + std::cout << "ERROR: Unknown command '" << kw <<"'!\n"; + } + } +} + + ABSL_FLAG(std::string, client_name, "sfizz", "Jack client name"); ABSL_FLAG(std::string, oversampling, "1x", "Internal oversampling factor (value values are x1, x2, x4, x8)"); ABSL_FLAG(uint32_t, preload_size, 8192, "Preloaded size"); ABSL_FLAG(uint32_t, num_voices, 32, "Num of voices"); +ABSL_FLAG(bool, jack_autoconnect, false, "Autoconnect audio output"); ABSL_FLAG(bool, state, false, "Output the synth state in the jack loop"); int main(int argc, char** argv) { // std::ios::sync_with_stdio(false); auto arguments = absl::ParseCommandLine(argc, argv); - if (arguments.size() < 2) { - std::cout << "You need to specify an SFZ file to load." << '\n'; - return -1; - } + auto filesToParse = absl::MakeConstSpan(arguments).subspan(1); const std::string clientName = absl::GetFlag(FLAGS_client_name); const std::string oversampling = absl::GetFlag(FLAGS_oversampling); const uint32_t preload_size = absl::GetFlag(FLAGS_preload_size); const uint32_t num_voices = absl::GetFlag(FLAGS_num_voices); + const bool jack_autoconnect = absl::GetFlag(FLAGS_jack_autoconnect); const bool verboseState = absl::GetFlag(FLAGS_state); std::cout << "Flags" << '\n'; @@ -176,6 +256,8 @@ int main(int argc, char** argv) std::cout << "- Oversampling: " << oversampling << '\n'; std::cout << "- Preloaded size: " << preload_size << '\n'; std::cout << "- Num of voices: " << num_voices << '\n'; + std::cout << "- Audio Autoconnect: " << jack_autoconnect << '\n'; + std::cout << "- Verbose State: " << verboseState << '\n'; const auto factor = [&]() { if (oversampling == "x1") return 1; @@ -190,45 +272,10 @@ int main(int argc, char** argv) std::cout << " " << file << ','; std::cout << '\n'; - sfz::Sfizz synth; synth.setOversamplingFactor(factor); synth.setPreloadSize(preload_size); synth.setNumVoices(num_voices); - const char *importFormat = nullptr; - if (!sfizz_load_or_import_file(synth.handle(), filesToParse[0], &importFormat)) { - std::cout << "Could not load the instrument file: " << filesToParse[0] << '\n'; - return 1; - } - - std::cout << "==========" << '\n'; - std::cout << "Total:" << '\n'; - std::cout << "\tMasters: " << synth.getNumMasters() << '\n'; - std::cout << "\tGroups: " << synth.getNumGroups() << '\n'; - std::cout << "\tRegions: " << synth.getNumRegions() << '\n'; - std::cout << "\tCurves: " << synth.getNumCurves() << '\n'; - std::cout << "\tPreloadedSamples: " << synth.getNumPreloadedSamples() << '\n'; -#if 0 // not currently in public API - std::cout << "==========" << '\n'; - std::cout << "Included files:" << '\n'; - for (auto& file : synth.getParser().getIncludedFiles()) - std::cout << '\t' << file << '\n'; - std::cout << "==========" << '\n'; - std::cout << "Defines:" << '\n'; - for (auto& define : synth.getParser().getDefines()) - std::cout << '\t' << define.first << '=' << define.second << '\n'; -#endif - std::cout << "==========" << '\n'; - std::cout << "Unknown opcodes:"; - for (auto& opcode : synth.getUnknownOpcodes()) - std::cout << opcode << ','; - std::cout << '\n'; - if (importFormat) { - std::cout << "==========" << '\n'; - std::cout << "Import format: " << importFormat << '\n'; - } - // std::cout << std::flush; - jack_status_t status; client = jack_client_open(clientName.c_str(), JackNullOption, &status); if (client == nullptr) { @@ -270,26 +317,35 @@ int main(int argc, char** argv) return 1; } - auto systemPorts = jack_get_ports(client, nullptr, nullptr, JackPortIsPhysical | JackPortIsInput); - if (systemPorts == nullptr) { - std::cerr << "No physical output ports found" << '\n'; - return 1; - } + if (jack_autoconnect) { + auto systemPorts = jack_get_ports(client, nullptr, nullptr, JackPortIsPhysical | JackPortIsInput); + if (systemPorts == nullptr) { + std::cerr << "No physical output ports found" << '\n'; + return 1; + } - if (jack_connect(client, jack_port_name(outputPort1), systemPorts[0])) { - std::cerr << "Cannot connect to physical output ports (0)" << '\n'; + if (jack_connect(client, jack_port_name(outputPort1), systemPorts[0])) { + std::cerr << "Cannot connect to physical output ports (0)" << '\n'; + } + + if (jack_connect(client, jack_port_name(outputPort2), systemPorts[1])) { + std::cerr << "Cannot connect to physical output ports (1)" << '\n'; + } + jack_free(systemPorts); } - if (jack_connect(client, jack_port_name(outputPort2), systemPorts[1])) { - std::cerr << "Cannot connect to physical output ports (1)" << '\n'; + if (filesToParse[0]) { + load_instrument(filesToParse[0]); } - jack_free(systemPorts); + + std::thread cli_thread(cli_thread_proc); signal(SIGHUP, done); signal(SIGINT, done); signal(SIGTERM, done); signal(SIGQUIT, done); + while (!shouldClose){ if (verboseState) { std::cout << "Active voices: " << synth.getNumActiveVoices() << '\n'; From 48582af1194de26ceec4a47f94d1cd859b49c43c Mon Sep 17 00:00:00 2001 From: jofemodo Date: Fri, 20 Aug 2021 02:33:11 +0200 Subject: [PATCH 161/193] Add "set_oversampling" option to internal CLI. --- clients/jack_client.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/clients/jack_client.cpp b/clients/jack_client.cpp index 0c2fa61be..9095c8817 100644 --- a/clients/jack_client.cpp +++ b/clients/jack_client.cpp @@ -206,6 +206,13 @@ void cli_thread_proc() { if (kw=="load_instrument") { load_instrument(args.c_str()); } + else if (kw=="set_oversampling") { + try { + synth.setOversamplingFactor(stoi(args)); + } catch (...) { + std::cout << "ERROR: Can't set oversampling!\n"; + } + } else if (kw=="set_preload_size") { try { synth.setPreloadSize(stoi(args)); From 07670686f924b1da289396a86dc03af6a3890058 Mon Sep 17 00:00:00 2001 From: jofemodo Date: Fri, 20 Aug 2021 03:31:31 +0200 Subject: [PATCH 162/193] Improve internal CLI: Implement tokenization algorithm for command arguments, including double quotes. --- clients/jack_client.cpp | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/clients/jack_client.cpp b/clients/jack_client.cpp index 9095c8817..4de49455d 100644 --- a/clients/jack_client.cpp +++ b/clients/jack_client.cpp @@ -42,6 +42,7 @@ #include #include #include +#include sfz::Sfizz synth; @@ -193,6 +194,30 @@ bool load_instrument(const char *fpath) { } +std::vector string_tokenize(std::string str) { + std::vector tokens; + std::string part = ""; + for (size_t i=0; i "; @@ -202,9 +227,11 @@ void cli_thread_proc() { std::size_t pos = command.find(" "); std::string kw = command.substr(0, pos); std::string args = command.substr(pos+1); + std::vector tokens = string_tokenize(args); if (kw=="load_instrument") { - load_instrument(args.c_str()); + //args.erase(std::remove(str.begin(), str.end(), '\"'), str.end()); + load_instrument(tokens[0].c_str()); } else if (kw=="set_oversampling") { try { From c3df3d3b652f8bca0a8738749d3714ee2b74a492 Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 22 Aug 2021 15:25:50 -0400 Subject: [PATCH 163/193] Fix build with clang & C++17 (bessel functions not defined) --- src/sfizz/MathHelpers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sfizz/MathHelpers.h b/src/sfizz/MathHelpers.h index 0d2fa1b3c..556526da0 100644 --- a/src/sfizz/MathHelpers.h +++ b/src/sfizz/MathHelpers.h @@ -26,7 +26,7 @@ #include #endif -#if __cplusplus >= 201703L +#if __cplusplus >= 201703L && defined(__cpp_lib_math_special_functions) static double i0(double x) { return std::cyl_bessel_i(0.0, x); } #else // external Bessel function from cephes From 16789b6a6e804a510694020a045d9db2d197e341 Mon Sep 17 00:00:00 2001 From: jofemodo Date: Thu, 26 Aug 2021 12:54:21 +0200 Subject: [PATCH 164/193] Add mutex lock on internal command line process. --- clients/jack_client.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/clients/jack_client.cpp b/clients/jack_client.cpp index 4de49455d..799a6dbf8 100644 --- a/clients/jack_client.cpp +++ b/clients/jack_client.cpp @@ -194,7 +194,7 @@ bool load_instrument(const char *fpath) { } -std::vector string_tokenize(std::string str) { +std::vector string_tokenize(const std::string str) { std::vector tokens; std::string part = ""; for (size_t i=0; i tokens = string_tokenize(args); if (kw=="load_instrument") { - //args.erase(std::remove(str.begin(), str.end(), '\"'), str.end()); - load_instrument(tokens[0].c_str()); + try { + std::lock_guard lock { processMutex }; + load_instrument(tokens[0].c_str()); + } catch (...) { + std::cout << "ERROR: Can't load instrument!\n"; + } } else if (kw=="set_oversampling") { try { + std::lock_guard lock { processMutex }; synth.setOversamplingFactor(stoi(args)); } catch (...) { std::cout << "ERROR: Can't set oversampling!\n"; @@ -242,6 +247,7 @@ void cli_thread_proc() { } else if (kw=="set_preload_size") { try { + std::lock_guard lock { processMutex }; synth.setPreloadSize(stoi(args)); } catch (...) { std::cout << "ERROR: Can't set preload size!\n"; @@ -249,6 +255,7 @@ void cli_thread_proc() { } else if (kw=="set_voices") { try { + std::lock_guard lock { processMutex }; synth.setNumVoices(stoi(args)); } catch (...) { std::cout << "ERROR: Can't set num of voices!\n"; From c70f50197ba9d505a3f24dc29843ec60e45588d2 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 28 Aug 2021 13:28:46 +0200 Subject: [PATCH 165/193] Have the DS importer generate the correct loop mode --- src/sfizz/import/foreign_instruments/DecentSampler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sfizz/import/foreign_instruments/DecentSampler.cpp b/src/sfizz/import/foreign_instruments/DecentSampler.cpp index e20f3ab74..149ba86d1 100644 --- a/src/sfizz/import/foreign_instruments/DecentSampler.cpp +++ b/src/sfizz/import/foreign_instruments/DecentSampler.cpp @@ -202,7 +202,7 @@ void DecentSamplerInstrumentImporter::emitRegionalOpcodes(std::ostream& os, pugi break; case hash("loopEnabled"): os << "loop_mode=" - << ((xmlOpcode.value == "true") ? "loop_continuous" : "one_shot") << "\n"; + << ((xmlOpcode.value == "true") ? "loop_continuous" : "no_loop") << "\n"; break; case hash("attack"): convertToReal("ampeg_attack"); From e6bc3305209e2f88f10e2454382f0c211632263b Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Fri, 10 Sep 2021 13:55:26 +0200 Subject: [PATCH 166/193] Use the `offed_` boolean for the check --- src/sfizz/Voice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index a8e0a1026..f08def4e0 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -1666,7 +1666,7 @@ bool Voice::checkOffGroup(const Region* other, int delay, int noteNumber) noexce if (region == nullptr || other == nullptr) return false; - if (impl.released() && region->offMode == OffMode::normal) + if (impl.offed_) return false; if ((impl.triggerEvent_.type == TriggerEventType::NoteOn From 174d58fd877eb3f060908811b49bd01685fa2fc7 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 20 Sep 2021 03:03:36 +0200 Subject: [PATCH 167/193] Fix the VST thread synchronization on activate/deactivate --- plugins/vst/SfizzVstProcessor.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp index cf01f04f5..1e0a71346 100644 --- a/plugins/vst/SfizzVstProcessor.cpp +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -740,8 +740,13 @@ void SfizzVstProcessor::doBackgroundWork() for (;;) { bool isNotified = _semaToWorker.timed_wait(kBackgroundIdleInterval.count()); - if (!_workRunning) + if (!_workRunning) { + // if the quit signal is sent, the semaphore is also signaled + // make sure the count is kept consistent + if (!isNotified) + _semaToWorker.wait(); break; + } const char* id = nullptr; RTMessagePtr msg; From e3dcaa7d9b63c9b422f60d2d43e7c86313cbaafa Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 20 Sep 2021 03:19:54 +0200 Subject: [PATCH 168/193] Unfork and update VST3 SDK --- .gitmodules | 2 +- plugins/vst/external/VST_SDK/VST3_SDK/pluginterfaces | 2 +- plugins/vst/external/VST_SDK/VST3_SDK/public.sdk | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index a1f99fd75..441fc8070 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,7 +13,7 @@ shallow = true [submodule "vst/external/VST_SDK/VST3_SDK/public.sdk"] path = plugins/vst/external/VST_SDK/VST3_SDK/public.sdk - url = https://github.com/sfztools/vst3_public_sdk.git + url = https://github.com/steinbergmedia/vst3_public_sdk.git shallow = true [submodule "vst/external/VST_SDK/VST3_SDK/vstgui4"] path = plugins/editor/external/vstgui4 diff --git a/plugins/vst/external/VST_SDK/VST3_SDK/pluginterfaces b/plugins/vst/external/VST_SDK/VST3_SDK/pluginterfaces index b8566ef3b..93cef1afb 160000 --- a/plugins/vst/external/VST_SDK/VST3_SDK/pluginterfaces +++ b/plugins/vst/external/VST_SDK/VST3_SDK/pluginterfaces @@ -1 +1 @@ -Subproject commit b8566ef3b2a0cba60a96e3ef2001224c865c8b36 +Subproject commit 93cef1afb7061e488625045ba5a82abaa83d27fe diff --git a/plugins/vst/external/VST_SDK/VST3_SDK/public.sdk b/plugins/vst/external/VST_SDK/VST3_SDK/public.sdk index d69d011fa..9589800ed 160000 --- a/plugins/vst/external/VST_SDK/VST3_SDK/public.sdk +++ b/plugins/vst/external/VST_SDK/VST3_SDK/public.sdk @@ -1 +1 @@ -Subproject commit d69d011fa08c3977928f7da0010f8938f93fd370 +Subproject commit 9589800ed94573354bc29de45eec5744523fbfcb From 6735d77e637db26622d14681decd3a24d5eb4393 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 20 Sep 2021 03:21:24 +0200 Subject: [PATCH 169/193] vst: use the keyswitch change flag from 3.7.3 --- plugins/vst/SfizzVstController.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index a3d38f07d..dcf66b67e 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -364,8 +364,7 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) } if (Vst::IComponentHandler* componentHandler = getComponentHandler()) - // NOTE(jpc) I think that's the right one, but it needs confirmation - componentHandler->restartComponent(Vst::kNoteExpressionChanged); + componentHandler->restartComponent(Vst::kKeyswitchChanged); // update the parameter titles and notify for (uint32 cc = 0; cc < sfz::config::numCCs; ++cc) { From 7c059ec75056a0beef56e0981187d004fcb6f953 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 20 Sep 2021 03:40:54 +0200 Subject: [PATCH 170/193] Add stringconvert.cpp to SDK sources --- plugins/vst/cmake/Vst3.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/vst/cmake/Vst3.cmake b/plugins/vst/cmake/Vst3.cmake index c05706ddc..f461453fb 100644 --- a/plugins/vst/cmake/Vst3.cmake +++ b/plugins/vst/cmake/Vst3.cmake @@ -30,7 +30,8 @@ add_library(vst3sdk STATIC EXCLUDE_FROM_ALL "${VST3SDK_BASEDIR}/public.sdk/source/vst/vstnoteexpressiontypes.cpp" "${VST3SDK_BASEDIR}/public.sdk/source/vst/vstparameters.cpp" "${VST3SDK_BASEDIR}/public.sdk/source/vst/vstpresetfile.cpp" - "${VST3SDK_BASEDIR}/public.sdk/source/vst/vstrepresentation.cpp") + "${VST3SDK_BASEDIR}/public.sdk/source/vst/vstrepresentation.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/utility/stringconvert.cpp") if(WIN32) target_sources(vst3sdk PRIVATE "${VST3SDK_BASEDIR}/public.sdk/source/common/threadchecker_win32.cpp") From 0bc02f12cf64386ef0d1310c6875116bf4b9e45a Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 20 Sep 2021 07:02:09 +0200 Subject: [PATCH 171/193] Fix attempt for X11 runloop problems --- plugins/vst/SfizzVstEditor.cpp | 22 ++-- plugins/vst/X11RunLoop.cpp | 210 +++++++++++++++++++++++---------- plugins/vst/X11RunLoop.h | 46 ++++---- 3 files changed, 185 insertions(+), 93 deletions(-) diff --git a/plugins/vst/SfizzVstEditor.cpp b/plugins/vst/SfizzVstEditor.cpp index 5bb967250..2e1506ca9 100644 --- a/plugins/vst/SfizzVstEditor.cpp +++ b/plugins/vst/SfizzVstEditor.cpp @@ -59,11 +59,8 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p config = &x11config; #endif - Editor* editor = editor_.get(); - if (!editor) { - editor = new Editor(*this); - editor_.reset(editor); - } + Editor* editor = new Editor(*this); + editor_.reset(editor); if (!frame->open(parent, platformType, config)) { fprintf(stderr, "[sfizz] error opening frame\n"); @@ -122,12 +119,21 @@ void PLUGIN_API SfizzVstEditor::close() for (FObject* update : updates_) update->removeDependent(this); - if (editor_) + if (editor_) { editor_->close(); + editor_ = nullptr; + } + if (frame->getNbReference() != 1) frame->forget(); - else + else { frame->close(); +#if !defined(__APPLE__) && !defined(_WIN32) + // if vstgui is done using the runloop, destroy it + if (!RunLoop::get()) + _runLoop = nullptr; +#endif + } this->frame = nullptr; } @@ -158,8 +164,6 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message) // notifier of X11 events is working. If there is, remove this and // avoid polluting Linux hosts which implement the loop correctly. runLoop->processSomeEvents(); - - runLoop->cleanupDeadHandlers(); } } #endif diff --git a/plugins/vst/X11RunLoop.cpp b/plugins/vst/X11RunLoop.cpp index 990778468..dec080746 100644 --- a/plugins/vst/X11RunLoop.cpp +++ b/plugins/vst/X11RunLoop.cpp @@ -1,33 +1,36 @@ -// SPDX-License-Identifier: GPL-3.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 #if !defined(__APPLE__) && !defined(_WIN32) #include "X11RunLoop.h" #include "vstgui/lib/platform/linux/x11platform.h" #include "base/source/fobject.h" +#include +#include namespace VSTGUI { -RunLoop::RunLoop(Steinberg::FUnknown* runLoop) - : runLoop(runLoop) -{ -} +struct RunLoop::Impl { + struct EventHandler; + struct TimerHandler; -RunLoop::~RunLoop() {} + using EventHandlers = std::vector>; + using TimerHandlers = std::vector>; -SharedPointer RunLoop::get() -{ - return X11::RunLoop::get().cast(); -} + EventHandlers eventHandlers; + TimerHandlers timerHandlers; + Steinberg::FUnknownPtr runLoop; +}; -struct RunLoop::EventHandler final : Steinberg::Linux::IEventHandler, public Steinberg::FObject { +//------------------------------------------------------------------------------ +struct RunLoop::Impl::EventHandler final : Steinberg::Linux::IEventHandler, public Steinberg::FObject { X11::IEventHandler* handler { nullptr }; bool alive { false }; - void PLUGIN_API onFDIsSet(Steinberg::Linux::FileDescriptor) override - { - if (alive && handler) - handler->onEvent(); - } + void PLUGIN_API onFDIsSet(Steinberg::Linux::FileDescriptor) override; DELEGATE_REFCOUNT(Steinberg::FObject) DEFINE_INTERFACES @@ -35,15 +38,11 @@ struct RunLoop::EventHandler final : Steinberg::Linux::IEventHandler, public Ste END_DEFINE_INTERFACES(Steinberg::FObject) }; -struct RunLoop::TimerHandler final : Steinberg::Linux::ITimerHandler, public Steinberg::FObject { +struct RunLoop::Impl::TimerHandler final : Steinberg::Linux::ITimerHandler, public Steinberg::FObject { X11::ITimerHandler* handler { nullptr }; bool alive { false }; - void PLUGIN_API onTimer() override - { - if (alive && handler) - handler->onTimer(); - } + void PLUGIN_API onTimer() override; DELEGATE_REFCOUNT(Steinberg::FObject) DEFINE_INTERFACES @@ -51,45 +50,126 @@ struct RunLoop::TimerHandler final : Steinberg::Linux::ITimerHandler, public Ste END_DEFINE_INTERFACES(Steinberg::FObject) }; +//------------------------------------------------------------------------------ +void PLUGIN_API RunLoop::Impl::EventHandler::onFDIsSet(Steinberg::Linux::FileDescriptor) +{ + SharedPointer runLoop = RunLoop::get(); + if (!runLoop) { + fprintf(stderr, "[x11] event has fired without active runloop\n"); + return; + } + + if (alive && handler) + handler->onEvent(); +} + +void PLUGIN_API RunLoop::Impl::TimerHandler::onTimer() +{ + SharedPointer runLoop = RunLoop::get(); + if (!runLoop) { + fprintf(stderr, "[x11] timer has fired without active runloop\n"); + return; + } + + if (alive && handler) + handler->onTimer(); +} + +//------------------------------------------------------------------------------ +RunLoop::RunLoop(Steinberg::FUnknown* runLoop) + : impl(new Impl) +{ + impl->runLoop = runLoop; +} + +RunLoop::~RunLoop() +{ + //dumpCurrentState(); + + if (0) { + // remove any leftover handlers + for (size_t i = 0; i < impl->eventHandlers.size(); ++i) { + const auto& eh = impl->eventHandlers[i]; + if (eh->alive && eh->handler) { + impl->runLoop->unregisterEventHandler(eh.get()); + } + } + for (size_t i = 0; i < impl->timerHandlers.size(); ++i) { + const auto& th = impl->timerHandlers[i]; + if (th->alive && th->handler) { + impl->runLoop->unregisterTimer(th.get()); + } + } + } +} + +SharedPointer RunLoop::get() +{ + return X11::RunLoop::get().cast(); +} void RunLoop::processSomeEvents() { - for (size_t i = 0; i < eventHandlers.size(); ++i) { - const auto& eh = eventHandlers[i]; + for (size_t i = 0; i < impl->eventHandlers.size(); ++i) { + const auto& eh = impl->eventHandlers[i]; if (eh->alive && eh->handler) { eh->handler->onEvent(); } } } -void RunLoop::cleanupDeadHandlers() +void RunLoop::dumpCurrentState() { - for (size_t i = 0; i < eventHandlers.size(); ++i) { - const auto& eh = eventHandlers[i]; - if (!eh->alive) { - runLoop->unregisterEventHandler(eh); - eventHandlers.erase(eventHandlers.begin() + i--); - } + fprintf(stderr, "=== X11 runloop ===\n"); + + fprintf(stderr, "\t" "Event slots:\n"); + for (size_t i = 0, n = impl->eventHandlers.size(); i < n; ++i) { + Impl::EventHandler *eh = impl->eventHandlers[i].get(); + fprintf(stderr, "\t\t" "(%lu) alive=%d handler=%p type=%s\n", i, eh->alive, eh->handler, (eh->alive && eh->handler) ? typeid(*eh->handler).name() : ""); } - for (size_t i = 0; i < timerHandlers.size(); ++i) { - const auto& th = timerHandlers[i]; - if (!th->alive) { - runLoop->unregisterTimer(th); - timerHandlers.erase(timerHandlers.begin() + i--); - } + + fprintf(stderr, "\t" "Timer slots:\n"); + for (size_t i = 0, n = impl->timerHandlers.size(); i < n; ++i) { + Impl::TimerHandler *th = impl->timerHandlers[i].get(); + fprintf(stderr, "\t\t" "(%lu) alive=%d handler=%p type=%s\n", i, th->alive, th->handler, (th->alive && th->handler) ? typeid(*th->handler).name() : ""); + } + + fprintf(stderr, "===/X11 runloop ===\n"); +} + +template +static void insertHandler(std::vector>& list, Steinberg::IPtr handler) +{ + size_t i = 0; + size_t n = list.size(); + while (i < n && list[i]->alive) + ++i; + if (i < n) + list[i] = handler; + else + list.emplace_back(handler); +} + +template +static size_t findHandler(const std::vector>& list, U* handler) +{ + for (size_t i = 0, n = list.size(); i < n; ++i) { + if (list[i]->alive && list[i]->handler == handler) + return i; } + return ~size_t(0); } bool RunLoop::registerEventHandler(int fd, X11::IEventHandler* handler) { - if (!runLoop) + if (!impl->runLoop) return false; - auto smtgHandler = Steinberg::owned(new EventHandler()); + auto smtgHandler = Steinberg::owned(new Impl::EventHandler); smtgHandler->handler = handler; smtgHandler->alive = true; - if (runLoop->registerEventHandler(smtgHandler, fd) == Steinberg::kResultTrue) { - eventHandlers.push_back(smtgHandler); + if (impl->runLoop->registerEventHandler(smtgHandler, fd) == Steinberg::kResultTrue) { + insertHandler(impl->eventHandlers, smtgHandler); return true; } return false; @@ -97,29 +177,31 @@ bool RunLoop::registerEventHandler(int fd, X11::IEventHandler* handler) bool RunLoop::unregisterEventHandler(X11::IEventHandler* handler) { - if (!runLoop) + if (!impl->runLoop) return false; - for (size_t i = 0; i < eventHandlers.size(); ++i) { - const auto& eh = eventHandlers[i]; - if (eh->alive && eh->handler == handler) { - eh->alive = false; - return true; - } - } - return false; + size_t index = findHandler(impl->eventHandlers, handler); + if (index == ~size_t(0)) + return false; + + Impl::EventHandler *eh = impl->eventHandlers[index].get(); + if (!impl->runLoop->unregisterEventHandler(eh)) + return false; + + eh->alive = false; + return true; } bool RunLoop::registerTimer(uint64_t interval, X11::ITimerHandler* handler) { - if (!runLoop) + if (!impl->runLoop) return false; - auto smtgHandler = Steinberg::owned(new TimerHandler()); + auto smtgHandler = Steinberg::owned(new Impl::TimerHandler); smtgHandler->handler = handler; smtgHandler->alive = true; - if (runLoop->registerTimer(smtgHandler, interval) == Steinberg::kResultTrue) { - timerHandlers.push_back(smtgHandler); + if (impl->runLoop->registerTimer(smtgHandler, interval) == Steinberg::kResultTrue) { + insertHandler(impl->timerHandlers, smtgHandler); return true; } return false; @@ -127,17 +209,19 @@ bool RunLoop::registerTimer(uint64_t interval, X11::ITimerHandler* handler) bool RunLoop::unregisterTimer(X11::ITimerHandler* handler) { - if (!runLoop) + if (!impl->runLoop) return false; - for (size_t i = 0; i < timerHandlers.size(); ++i) { - const auto& th = timerHandlers[i]; - if (th->alive && th->handler == handler) { - th->alive = false; - return true; - } - } - return false; + size_t index = findHandler(impl->timerHandlers, handler); + if (index == ~size_t(0)) + return false; + + Impl::TimerHandler *th = impl->timerHandlers[index].get(); + if (!impl->runLoop->unregisterTimer(th)) + return false; + + th->alive = false; + return true; } } // namespace VSTGUI diff --git a/plugins/vst/X11RunLoop.h b/plugins/vst/X11RunLoop.h index cb47b34c3..5755a06ba 100644 --- a/plugins/vst/X11RunLoop.h +++ b/plugins/vst/X11RunLoop.h @@ -1,16 +1,28 @@ -// SPDX-License-Identifier: GPL-3.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 + /* - This is a modified version the X11 run loop from vst3editor.cpp. + This runloop connects to X11 VSTGUI, it connects VST3 and VSTGUI together. + The Windows and macOS runloops do not need this, the OS-provided + functionality is used instead. - This version is edited to add more safeguards to protect against host bugs. - It also permits to call event processing externally in case the host has a - defective X11 event loop notifier. + Previously, this was based on VSTGUI code provided by Steinberg. + This is replaced with a rewrite, because the original code has too many + issues. For example, it has no robustness in case handlers get added or + removed within the execution of the handler. + + This version allows to call event processing externally, in case the host + has a defective X11 event loop notifier. (some versions of Bitwig do) */ #pragma once #if !defined(__APPLE__) && !defined(_WIN32) #include "vstgui/lib/platform/linux/x11frame.h" #include "pluginterfaces/gui/iplugview.h" +#include namespace VSTGUI { @@ -22,24 +34,16 @@ class RunLoop final : public X11::IRunLoop, public AtomicReferenceCounted { static SharedPointer get(); void processSomeEvents(); - void cleanupDeadHandlers(); + void dumpCurrentState(); // X11::IRunLoop - bool registerEventHandler(int fd, X11::IEventHandler* handler); - bool unregisterEventHandler(X11::IEventHandler* handler); - bool registerTimer(uint64_t interval, X11::ITimerHandler* handler); - bool unregisterTimer(X11::ITimerHandler* handler); - -private: - struct EventHandler; - struct TimerHandler; - -private: - using EventHandlers = std::vector>; - using TimerHandlers = std::vector>; - EventHandlers eventHandlers; - TimerHandlers timerHandlers; - Steinberg::FUnknownPtr runLoop; + bool registerEventHandler(int fd, X11::IEventHandler* handler) override; + bool unregisterEventHandler(X11::IEventHandler* handler) override; + bool registerTimer(uint64_t interval, X11::ITimerHandler* handler) override; + bool unregisterTimer(X11::ITimerHandler* handler) override; + + struct Impl; + std::unique_ptr impl; }; } // namespace VSTGUI From b2368d6e80592d54a8e65c28a8257b94a9a1db8e Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 20 Sep 2021 07:59:38 +0200 Subject: [PATCH 172/193] Add a couple of includes used in the file [ci skip] --- plugins/vst/X11RunLoop.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/vst/X11RunLoop.cpp b/plugins/vst/X11RunLoop.cpp index dec080746..04725c5a1 100644 --- a/plugins/vst/X11RunLoop.cpp +++ b/plugins/vst/X11RunLoop.cpp @@ -9,6 +9,8 @@ #include "vstgui/lib/platform/linux/x11platform.h" #include "base/source/fobject.h" #include +#include +#include #include namespace VSTGUI { From 99d907e5c7090580a99ca130ae9651cb848dc733 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 24 Jul 2021 16:59:22 +0200 Subject: [PATCH 173/193] Remember the last keyswitch used in the states --- plugins/lv2/sfizz.cpp | 31 +++++++++++++++++++++++++++++++ plugins/lv2/sfizz_lv2.h | 1 + plugins/lv2/sfizz_lv2_plugin.h | 2 ++ plugins/vst/SfizzVstProcessor.cpp | 12 ++++++++++++ plugins/vst/SfizzVstState.cpp | 11 +++++++++++ plugins/vst/SfizzVstState.h | 3 ++- 6 files changed, 59 insertions(+), 1 deletion(-) diff --git a/plugins/lv2/sfizz.cpp b/plugins/lv2/sfizz.cpp index 9bc3c4a16..407ed8042 100644 --- a/plugins/lv2/sfizz.cpp +++ b/plugins/lv2/sfizz.cpp @@ -120,6 +120,7 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self) self->sfizz_num_voices_uri = map->map(map->handle, SFIZZ__numVoices); self->sfizz_preload_size_uri = map->map(map->handle, SFIZZ__preloadSize); self->sfizz_oversampling_uri = map->map(map->handle, SFIZZ__oversampling); + self->sfizz_last_keyswitch_uri = map->map(map->handle, SFIZZ__lastKeyswitch); self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus); self->sfizz_check_modification_uri = map->map(map->handle, SFIZZ__checkModification); self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob); @@ -323,6 +324,14 @@ sfizz_lv2_receive_message(void* data, int delay, const char* path, const char* s sfizz_plugin_t *self = (sfizz_plugin_t *)data; + if (!strcmp(path, "/sw/last/current") && sig) + { + if (sig[0] == 'i') + self->last_keyswitch = args[0].i; + else if (sig[0] == 'N') + self->last_keyswitch = -1; + } + // transmit to UI as OSC blob uint8_t *osc_temp = self->osc_temp; uint32_t osc_size = sfizz_prepare_message(osc_temp, OSC_TEMP_SIZE, path, sig, args); @@ -1301,6 +1310,7 @@ restore(LV2_Handle instance, } // Set default values + self->last_keyswitch = -1; sfizz_lv2_get_default_sfz_path(self, self->sfz_file_path, MAX_PATH_SIZE); sfizz_lv2_get_default_scala_path(self, self->scala_file_path, MAX_PATH_SIZE); @@ -1351,6 +1361,13 @@ restore(LV2_Handle instance, } } + value = retrieve(handle, self->sfizz_last_keyswitch_uri, &size, &type, &val_flags); + if (value) + { + int last_keyswitch = *(const int*)value; + self->last_keyswitch = last_keyswitch; + } + // Collect all CC values present in the state std::unique_ptr[]> cc_values( new absl::optional[sfz::config::numCCs]); @@ -1405,6 +1422,11 @@ restore(LV2_Handle instance, } } + if (self->last_keyswitch >= 0 && self->last_keyswitch <= 127) { + sfizz_send_hd_note_on(self->synth, 0, self->last_keyswitch, 1.0f); + sfizz_send_hd_note_off(self->synth, 1, self->last_keyswitch, 0.0f); + } + spin_mutex_unlock(self->synth_mutex); return status; @@ -1474,6 +1496,15 @@ save(LV2_Handle instance, absl::string_view((const char*)self->sfz_blob_data, self->sfz_blob_size)); self->sfz_blob_mutex->unlock(); + if (self->last_keyswitch >= 0 && self->last_keyswitch <= 127) { + store(handle, + self->sfizz_last_keyswitch_uri, + &self->last_keyswitch, + sizeof(int), + self->atom_int_uri, + LV2_STATE_IS_POD); + } + for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { if (desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc)) { LV2_URID urid = sfizz_lv2_ccmap_map(self->ccmap, int(cc)); diff --git a/plugins/lv2/sfizz_lv2.h b/plugins/lv2/sfizz_lv2.h index f48f2606f..c9935ec08 100644 --- a/plugins/lv2/sfizz_lv2.h +++ b/plugins/lv2/sfizz_lv2.h @@ -39,6 +39,7 @@ #define SFIZZ__numVoices SFIZZ_URI ":" "numvoices" #define SFIZZ__preloadSize SFIZZ_URI ":" "preload_size" #define SFIZZ__oversampling SFIZZ_URI ":" "oversampling" +#define SFIZZ__lastKeyswitch SFIZZ_URI ":" "last_keyswitch" // These ones are just for the worker #define SFIZZ__logStatus SFIZZ_URI ":" "log_status" #define SFIZZ__checkModification SFIZZ_URI ":" "check_modification" diff --git a/plugins/lv2/sfizz_lv2_plugin.h b/plugins/lv2/sfizz_lv2_plugin.h index 9c1918f96..fad8342ac 100644 --- a/plugins/lv2/sfizz_lv2_plugin.h +++ b/plugins/lv2/sfizz_lv2_plugin.h @@ -86,6 +86,7 @@ struct sfizz_plugin_t LV2_URID sfizz_num_voices_uri {}; LV2_URID sfizz_preload_size_uri {}; LV2_URID sfizz_oversampling_uri {}; + LV2_URID sfizz_last_keyswitch_uri {}; LV2_URID sfizz_log_status_uri {}; LV2_URID sfizz_check_modification_uri {}; LV2_URID sfizz_active_voices_uri {}; @@ -120,6 +121,7 @@ struct sfizz_plugin_t float sample_rate {}; std::atomic must_update_midnam {}; volatile bool must_automate_cc {}; + int last_keyswitch { -1 }; // Current instrument description std::mutex *sfz_blob_mutex {}; diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp index 1e0a71346..e50801faa 100644 --- a/plugins/vst/SfizzVstProcessor.cpp +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -227,6 +227,10 @@ void SfizzVstProcessor::syncStateToSynth() synth->setScalaRootKey(_state.scalaRootKey); synth->setTuningFrequency(_state.tuningFrequency); synth->loadStretchTuningByRatio(_state.stretchedTuning); + if (_state.lastKeyswitch >= 0 && _state.lastKeyswitch <= 127) { + synth->hdNoteOn(0, _state.lastKeyswitch, 1.0f); + synth->hdNoteOff(1, _state.lastKeyswitch, 0.0f); + } } tresult PLUGIN_API SfizzVstProcessor::canProcessSampleSize(int32 symbolicSampleSize) @@ -669,6 +673,14 @@ bool SfizzVstProcessor::processUpdate(FUnknown* changedUnknown, int32 message) void SfizzVstProcessor::receiveOSC(int delay, const char* path, const char* sig, const sfizz_arg_t* args) { + if (!strcmp(path, "/sw/last/current") && sig) + { + if (sig[0] == 'i') + _state.lastKeyswitch = args[0].i; + else if (sig[0] == 'N') + _state.lastKeyswitch = -1; + } + uint8_t* oscTemp = _oscTemp.get(); uint32 oscSize = sfizz_prepare_message(oscTemp, kOscTempSize, path, sig, args); if (oscSize <= kOscTempSize) { diff --git a/plugins/vst/SfizzVstState.cpp b/plugins/vst/SfizzVstState.cpp index 1783f817f..160d7abbe 100644 --- a/plugins/vst/SfizzVstState.cpp +++ b/plugins/vst/SfizzVstState.cpp @@ -73,6 +73,14 @@ tresult SfizzVstState::load(IBStream* state) oscillatorQuality = defaults.oscillatorQuality; } + if (version >= 4) { + if (!s.readInt32(lastKeyswitch)) + return kResultFalse; + } + else { + lastKeyswitch = -1; + } + controllers.clear(); if (version >= 2) { uint32 count; @@ -135,6 +143,9 @@ tresult SfizzVstState::store(IBStream* state) const if (!s.writeInt32(oscillatorQuality)) return kResultFalse; + if (!s.writeInt32(lastKeyswitch)) + return kResultFalse; + { uint32 ccCount = 0; uint32 ccLimit = uint32(std::min(controllers.size(), size_t(0x10000))); diff --git a/plugins/vst/SfizzVstState.h b/plugins/vst/SfizzVstState.h index 7f50f4c57..93a7a4ec4 100644 --- a/plugins/vst/SfizzVstState.h +++ b/plugins/vst/SfizzVstState.h @@ -27,9 +27,10 @@ class SfizzVstState { float stretchedTuning = 0.0; int32 sampleQuality = 2; int32 oscillatorQuality = 1; + int32 lastKeyswitch = -1; std::vector> controllers; - static constexpr uint64 currentStateVersion = 3; + static constexpr uint64 currentStateVersion = 4; tresult load(IBStream* state); tresult store(IBStream* state) const; From 8c1b5a11fe1d448cde205f8c4172b2993d9276a8 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Fri, 10 Sep 2021 13:59:20 +0200 Subject: [PATCH 174/193] Avoid a copy --- clients/jack_client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/jack_client.cpp b/clients/jack_client.cpp index 799a6dbf8..7438e801d 100644 --- a/clients/jack_client.cpp +++ b/clients/jack_client.cpp @@ -194,7 +194,7 @@ bool load_instrument(const char *fpath) { } -std::vector string_tokenize(const std::string str) { +std::vector string_tokenize(const std::string& str) { std::vector tokens; std::string part = ""; for (size_t i=0; i Date: Fri, 10 Sep 2021 14:04:24 +0200 Subject: [PATCH 175/193] Formatting --- clients/jack_client.cpp | 71 +++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/clients/jack_client.cpp b/clients/jack_client.cpp index 7438e801d..63d0da667 100644 --- a/clients/jack_client.cpp +++ b/clients/jack_client.cpp @@ -44,7 +44,6 @@ #include #include - sfz::Sfizz synth; static jack_port_t* midiInputPort; @@ -153,9 +152,9 @@ static void done(int sig) // exit(0); } - -bool load_instrument(const char *fpath) { - const char *importFormat = nullptr; +bool loadInstrument(const char* fpath) +{ + const char* importFormat = nullptr; if (!sfizz_load_or_import_file(synth.handle(), fpath, &importFormat)) { std::cout << "Could not load the instrument file: " << fpath << '\n'; return false; @@ -193,32 +192,35 @@ bool load_instrument(const char *fpath) { return true; } - -std::vector string_tokenize(const std::string& str) { +std::vector stringTokenize(const std::string& str) +{ std::vector tokens; std::string part = ""; - for (size_t i=0; i "; @@ -226,51 +228,45 @@ void cli_thread_proc() { std::getline(std::cin, command); std::size_t pos = command.find(" "); std::string kw = command.substr(0, pos); - std::string args = command.substr(pos+1); - std::vector tokens = string_tokenize(args); + std::string args = command.substr(pos + 1); + std::vector tokens = stringTokenize(args); - if (kw=="load_instrument") { + if (kw == "loadInstrument") { try { std::lock_guard lock { processMutex }; - load_instrument(tokens[0].c_str()); + loadInstrument(tokens[0].c_str()); } catch (...) { std::cout << "ERROR: Can't load instrument!\n"; } - } - else if (kw=="set_oversampling") { + } else if (kw == "set_oversampling") { try { std::lock_guard lock { processMutex }; synth.setOversamplingFactor(stoi(args)); } catch (...) { std::cout << "ERROR: Can't set oversampling!\n"; } - } - else if (kw=="set_preload_size") { + } else if (kw == "set_preload_size") { try { std::lock_guard lock { processMutex }; synth.setPreloadSize(stoi(args)); } catch (...) { std::cout << "ERROR: Can't set preload size!\n"; } - } - else if (kw=="set_voices") { + } else if (kw == "set_voices") { try { std::lock_guard lock { processMutex }; synth.setNumVoices(stoi(args)); } catch (...) { std::cout << "ERROR: Can't set num of voices!\n"; } - } - else if (kw=="quit") { + } else if (kw == "quit") { shouldClose = true; - } - else if (kw.size()>0){ - std::cout << "ERROR: Unknown command '" << kw <<"'!\n"; + } else if (kw.size() > 0) { + std::cout << "ERROR: Unknown command '" << kw << "'!\n"; } } } - ABSL_FLAG(std::string, client_name, "sfizz", "Jack client name"); ABSL_FLAG(std::string, oversampling, "1x", "Internal oversampling factor (value values are x1, x2, x4, x8)"); ABSL_FLAG(uint32_t, preload_size, 8192, "Preloaded size"); @@ -280,10 +276,8 @@ ABSL_FLAG(bool, state, false, "Output the synth state in the jack loop"); int main(int argc, char** argv) { - // std::ios::sync_with_stdio(false); auto arguments = absl::ParseCommandLine(argc, argv); - auto filesToParse = absl::MakeConstSpan(arguments).subspan(1); const std::string clientName = absl::GetFlag(FLAGS_client_name); const std::string oversampling = absl::GetFlag(FLAGS_oversampling); @@ -376,23 +370,22 @@ int main(int argc, char** argv) } if (filesToParse[0]) { - load_instrument(filesToParse[0]); + loadInstrument(filesToParse[0]); } - std::thread cli_thread(cli_thread_proc); + std::thread cli_thread(cliThreadProc); signal(SIGHUP, done); signal(SIGINT, done); signal(SIGTERM, done); signal(SIGQUIT, done); - - while (!shouldClose){ + while (!shouldClose) { if (verboseState) { std::cout << "Active voices: " << synth.getNumActiveVoices() << '\n'; #ifndef NDEBUG - std::cout << "Allocated buffers: " << synth.getAllocatedBuffers() << '\n'; - std::cout << "Total size: " << synth.getAllocatedBytes() << '\n'; + std::cout << "Allocated buffers: " << synth.getAllocatedBuffers() << '\n'; + std::cout << "Total size: " << synth.getAllocatedBytes() << '\n'; #endif } std::this_thread::sleep_for(std::chrono::seconds(1)); From 6034c9f9a5647fd6f10e92cadb3668ca793e3ba6 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 21 Sep 2021 11:30:23 +0200 Subject: [PATCH 176/193] Fix overeager find/replace --- clients/jack_client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/jack_client.cpp b/clients/jack_client.cpp index 63d0da667..8f5f30d12 100644 --- a/clients/jack_client.cpp +++ b/clients/jack_client.cpp @@ -231,7 +231,7 @@ void cliThreadProc() std::string args = command.substr(pos + 1); std::vector tokens = stringTokenize(args); - if (kw == "loadInstrument") { + if (kw == "load_instrument") { try { std::lock_guard lock { processMutex }; loadInstrument(tokens[0].c_str()); From ff8562b14086b0af2b72e2545d141156a614c0f5 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 21 Sep 2021 03:39:26 +0200 Subject: [PATCH 177/193] Updates for new vstgui --- cmake/SfizzConfig.cmake | 6 +++--- plugins/editor/cmake/Vstgui.cmake | 3 +++ plugins/editor/external/vstgui4 | 2 +- plugins/editor/src/editor/GUIHelpers.cpp | 25 ++++++++++++++++++------ 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/cmake/SfizzConfig.cmake b/cmake/SfizzConfig.cmake index 1cb4b0e6a..aa3a225ad 100644 --- a/cmake/SfizzConfig.cmake +++ b/cmake/SfizzConfig.cmake @@ -18,9 +18,9 @@ set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) # Set C++ compatibility level if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC" AND CMAKE_CXX_STANDARD LESS 17) set(CMAKE_CXX_STANDARD 17) -elseif((SFIZZ_LV2_UI OR SFIZZ_VST OR SFIZZ_AU OR SFIZZ_VST2) AND CMAKE_CXX_STANDARD LESS 14) - # if the UI is part of the build, make it 14 - set(CMAKE_CXX_STANDARD 14) +elseif((SFIZZ_LV2_UI OR SFIZZ_VST OR SFIZZ_AU OR SFIZZ_VST2) AND CMAKE_CXX_STANDARD LESS 17) + # if the UI is part of the build, make it 17 + set(CMAKE_CXX_STANDARD 17) endif() # Set build profiling options diff --git a/plugins/editor/cmake/Vstgui.cmake b/plugins/editor/cmake/Vstgui.cmake index 0fb111ddb..8f7ebe252 100644 --- a/plugins/editor/cmake/Vstgui.cmake +++ b/plugins/editor/cmake/Vstgui.cmake @@ -39,6 +39,7 @@ add_library(sfizz_vstgui STATIC EXCLUDE_FROM_ALL "${VSTGUI_BASEDIR}/vstgui/lib/cfileselector.cpp" "${VSTGUI_BASEDIR}/vstgui/lib/cfont.cpp" "${VSTGUI_BASEDIR}/vstgui/lib/cframe.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cgradient.cpp" "${VSTGUI_BASEDIR}/vstgui/lib/cgradientview.cpp" "${VSTGUI_BASEDIR}/vstgui/lib/cgraphicspath.cpp" "${VSTGUI_BASEDIR}/vstgui/lib/clayeredviewcontainer.cpp" @@ -57,6 +58,7 @@ add_library(sfizz_vstgui STATIC EXCLUDE_FROM_ALL "${VSTGUI_BASEDIR}/vstgui/lib/cview.cpp" "${VSTGUI_BASEDIR}/vstgui/lib/cviewcontainer.cpp" "${VSTGUI_BASEDIR}/vstgui/lib/cvstguitimer.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/events.cpp" "${VSTGUI_BASEDIR}/vstgui/lib/genericstringlistdatabrowsersource.cpp" "${VSTGUI_BASEDIR}/vstgui/lib/pixelbuffer.cpp" "${VSTGUI_BASEDIR}/vstgui/lib/vstguidebug.cpp" @@ -69,6 +71,7 @@ if(WIN32) "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/direct2d/d2dbitmap.cpp" "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.cpp" "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/direct2d/d2dfont.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/direct2d/d2dgradient.cpp" "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.cpp" "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32datapackage.cpp" "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32dragging.cpp" diff --git a/plugins/editor/external/vstgui4 b/plugins/editor/external/vstgui4 index 2cf61f5e1..f6db2b250 160000 --- a/plugins/editor/external/vstgui4 +++ b/plugins/editor/external/vstgui4 @@ -1 +1 @@ -Subproject commit 2cf61f5e1fefe4dcd3d19119ddf7b182719a2a5b +Subproject commit f6db2b250ce9c08ff3f6939f7a0744d5d3a0a133 diff --git a/plugins/editor/src/editor/GUIHelpers.cpp b/plugins/editor/src/editor/GUIHelpers.cpp index b5da74431..901891b5c 100644 --- a/plugins/editor/src/editor/GUIHelpers.cpp +++ b/plugins/editor/src/editor/GUIHelpers.cpp @@ -5,6 +5,9 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "GUIHelpers.h" +#include "utility/vstgui_before.h" +#include +#include "utility/vstgui_after.h" class SFrameDisabler::KeyAndMouseHook : public CBaseObject, public IKeyboardHook, @@ -13,12 +16,10 @@ class SFrameDisabler::KeyAndMouseHook : public CBaseObject, void setEnabled(bool value) { enabled_ = value; } protected: - int32_t onKeyDown(const VstKeyCode&, CFrame*) { return enabled_ ? -1 : 1; } - int32_t onKeyUp(const VstKeyCode&, CFrame*) { return enabled_ ? -1 : 1; } - void onMouseEntered(CView*, CFrame*) {} - void onMouseExited(CView*, CFrame*) {} - CMouseEventResult onMouseMoved(CFrame*, const CPoint&, const CButtonState&) { return enabled_ ? kMouseEventNotHandled : kMouseEventHandled; } - CMouseEventResult onMouseDown(CFrame*, const CPoint&, const CButtonState&) { return enabled_ ? kMouseEventNotHandled : kMouseEventHandled; } + void onKeyboardEvent(KeyboardEvent& event, CFrame* frame) override; + void onMouseEntered(CView* view, CFrame* frame) override {} + void onMouseExited(CView* view, CFrame* frame) override {} + void onMouseEvent(MouseEvent& event, CFrame* frame) override; private: bool enabled_ = true; @@ -51,3 +52,15 @@ void SFrameDisabler::disable() hook_->setEnabled(false); delayedEnabler_->stop(); } + +void SFrameDisabler::KeyAndMouseHook::onKeyboardEvent(KeyboardEvent& event, CFrame* frame) +{ + if (!enabled_) + event.consumed = true; +} + +void SFrameDisabler::KeyAndMouseHook::onMouseEvent(MouseEvent& event, CFrame* frame) +{ + if (!enabled_) + event.consumed = true; +} From 526525be6aed84036a64d5d9ab94b914a6f69d43 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 21 Sep 2021 19:41:23 +0200 Subject: [PATCH 178/193] Update filesystem to fix macOS c++17 --- external/filesystem | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/filesystem b/external/filesystem index 2a8b380f8..7bc5c1730 160000 --- a/external/filesystem +++ b/external/filesystem @@ -1 +1 @@ -Subproject commit 2a8b380f8d4e77b389c42a194ab9c70d8e3a0f1e +Subproject commit 7bc5c17305fe70518ca72162e244af1d12455a91 From 4b6b380ce4faf64adeb19dffd806f8d170281878 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 21 Sep 2021 20:44:16 +0200 Subject: [PATCH 179/193] Set macOS requirement to 10.14 to get c++17 working; fix this later --- cmake/SfizzConfig.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/SfizzConfig.cmake b/cmake/SfizzConfig.cmake index aa3a225ad..715144a9d 100644 --- a/cmake/SfizzConfig.cmake +++ b/cmake/SfizzConfig.cmake @@ -52,7 +52,7 @@ endif() # Set macOS compatibility level if(APPLE) - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9") + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") endif() # Do not define macros `min` and `max` From 3fd0d4ee5d5ef00c8ad32f492b4056fb591c6089 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 22 Sep 2021 03:12:52 +0200 Subject: [PATCH 180/193] Add jsl::aligned_unique_ptr and jsl::make_aligned --- .../jsl/bits/memory/aligned_unique_ptr.tcc | 39 +++++++++++++++++ external/jsl/include/jsl/memory | 42 +++++++++++++++++++ tests/CMakeLists.txt | 1 + tests/MemoryT.cpp | 41 ++++++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 external/jsl/include/jsl/bits/memory/aligned_unique_ptr.tcc create mode 100644 external/jsl/include/jsl/memory create mode 100644 tests/MemoryT.cpp diff --git a/external/jsl/include/jsl/bits/memory/aligned_unique_ptr.tcc b/external/jsl/include/jsl/bits/memory/aligned_unique_ptr.tcc new file mode 100644 index 000000000..168c934ab --- /dev/null +++ b/external/jsl/include/jsl/bits/memory/aligned_unique_ptr.tcc @@ -0,0 +1,39 @@ +// -*- C++ -*- +// SPDX-License-Identifier: BSL-1.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 +#include "../../memory" + +namespace jsl { + +template +void aligned_ptr_delete::operator()(T *p) const noexcept +{ + aligned_allocator allocator; + allocator.destroy(p); + allocator.deallocate(p, 0); +} + +template +aligned_unique_ptr::single_element, Al> +make_aligned(Args &&... args) +{ + struct allocation_delete { + void operator()(T *p) const noexcept + { + aligned_allocator allocator; + allocator.destroy(p); + } + }; + aligned_allocator allocator; + std::unique_ptr allocation(allocator.allocate(1, nullptr)); + allocator.construct(allocation.get(), std::forward(args)...); + return aligned_unique_ptr(allocation.release()); +} + +} // namespace jsl diff --git a/external/jsl/include/jsl/memory b/external/jsl/include/jsl/memory new file mode 100644 index 000000000..a5ff5ec50 --- /dev/null +++ b/external/jsl/include/jsl/memory @@ -0,0 +1,42 @@ +// -*- C++ -*- +// SPDX-License-Identifier: BSL-1.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 +#include "./allocator" +#include + +namespace jsl { + +template +struct aligned_ptr_delete { + void operator()(T *p) const noexcept; +}; + +template +using aligned_unique_ptr = std::unique_ptr>; + +//------------------------------------------------------------------------------ + +template struct make_aligned_traits; +template struct make_aligned_traits { struct invalid {}; }; +template struct make_aligned_traits { struct invalid {}; }; +template struct make_aligned_traits { using single_element = T; }; + +//------------------------------------------------------------------------------ + +template +aligned_unique_ptr::single_element, Al> +make_aligned(Args &&... args); + +template +typename make_aligned_traits::invalid +make_aligned(Args &&... args) = delete; + +} // namespace jsl + +#include "bits/memory/aligned_unique_ptr.tcc" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2e5f70a9d..9ae219dbd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -49,6 +49,7 @@ set(SFIZZ_TEST_SOURCES LFOT.cpp MessagingT.cpp OversamplerT.cpp + MemoryT.cpp DataHelpers.h DataHelpers.cpp ) diff --git a/tests/MemoryT.cpp b/tests/MemoryT.cpp new file mode 100644 index 000000000..bf7b04975 --- /dev/null +++ b/tests/MemoryT.cpp @@ -0,0 +1,41 @@ +// 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 "catch2/catch.hpp" +#include +#include +#include + +TEST_CASE("[Memory] Aligned unique pointers") +{ + constexpr size_t numAllocations = 128; + constexpr size_t alignment = 1024; + + static size_t numLiveObjects; + numLiveObjects = 0; + + struct Object { + explicit Object(size_t id) : id(id) { ++numLiveObjects; } + ~Object() { --numLiveObjects; } + size_t id {}; + }; + + std::vector> ptrs(numAllocations); + + for (size_t i = 0; i < numAllocations; ++i) { + ptrs[i] = jsl::make_aligned(i); + REQUIRE(numLiveObjects == i + 1); + + Object *obj = ptrs[i].get(); + REQUIRE(obj->id == i); + + uintptr_t addr = reinterpret_cast(obj); + REQUIRE(addr % alignment == 0); + } + + ptrs.clear(); + REQUIRE(numLiveObjects == 0); +} From f0b827d423b87fc9fa6ec775fe5ac88cf26f7547 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 22 Sep 2021 05:02:22 +0200 Subject: [PATCH 181/193] Implement C++17 aligned-new support for macOS < 10.14 --- cmake/SfizzConfig.cmake | 26 ++++++- src/CMakeLists.txt | 6 +- .../utility/c++17/AlignedMemorySupport.cpp | 74 +++++++++++++++++++ 3 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 src/sfizz/utility/c++17/AlignedMemorySupport.cpp diff --git a/cmake/SfizzConfig.cmake b/cmake/SfizzConfig.cmake index 715144a9d..9d134fe90 100644 --- a/cmake/SfizzConfig.cmake +++ b/cmake/SfizzConfig.cmake @@ -1,6 +1,7 @@ include(CMakeDependentOption) include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) +include(CheckCXXSourceCompiles) include(GNUWarnings) set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard to be used") @@ -52,7 +53,30 @@ endif() # Set macOS compatibility level if(APPLE) - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9") +endif() + +# If using C++17, check if aligned-new has runtime support on the platform; +# on macOS, this depends on the deployment target. +if(CMAKE_CXX_STANDARD LESS 17) + # not necessary on older C++, it will call ordinary new and delete + set(SFIZZ_IMPLEMENT_CXX17_ALIGNED_NEW_SUPPORT FALSE) +else() + check_cxx_source_compiles(" +struct Test { alignas(1024) int z; }; +int main() { new Test; return 0; } +" SFIZZ_HAVE_CXX17_ALIGNED_NEW) + # if support is absent, sfizz will provide a substitute implementation + if(SFIZZ_HAVE_CXX17_ALIGNED_NEW) + set(SFIZZ_IMPLEMENT_CXX17_ALIGNED_NEW_SUPPORT FALSE) + else() + # on macOS, this mandatory flag tells that allocation functions are user-provided + check_cxx_compiler_flag("-faligned-allocation" SFIZZ_HAVE_CXXFLAG_FALIGNED_ALLOCATION) + if(SFIZZ_HAVE_CXXFLAG_FALIGNED_ALLOCATION) + add_compile_options("$<$:-faligned-allocation>") + endif() + set(SFIZZ_IMPLEMENT_CXX17_ALIGNED_NEW_SUPPORT TRUE) + endif() endif() # Do not define macros `min` and `max` diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 555d8af61..13f17efd3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -193,7 +193,8 @@ set(SFIZZ_SOURCES sfizz/effects/impl/ResonantStringAVX.cpp sfizz/effects/impl/ResonantArray.cpp sfizz/effects/impl/ResonantArraySSE.cpp - sfizz/effects/impl/ResonantArrayAVX.cpp) + sfizz/effects/impl/ResonantArrayAVX.cpp + sfizz/utility/c++17/AlignedMemorySupport.cpp) include(SfizzSIMDSourceFiles) sfizz_add_simd_sources(SFIZZ_SOURCES ".") @@ -303,6 +304,9 @@ endif() if(SFIZZ_RELEASE_ASSERTS) target_compile_definitions(sfizz_internal PUBLIC "SFIZZ_ENABLE_RELEASE_ASSERT=1") endif() +if(SFIZZ_IMPLEMENT_CXX17_ALIGNED_NEW_SUPPORT) + target_compile_definitions(sfizz_internal PRIVATE "SFIZZ_IMPLEMENT_CXX17_ALIGNED_NEW_SUPPORT=1") +endif() sfizz_enable_fast_math(sfizz_internal) # Check that sfizz and cmake-side definitions are matching diff --git a/src/sfizz/utility/c++17/AlignedMemorySupport.cpp b/src/sfizz/utility/c++17/AlignedMemorySupport.cpp new file mode 100644 index 000000000..834f207f0 --- /dev/null +++ b/src/sfizz/utility/c++17/AlignedMemorySupport.cpp @@ -0,0 +1,74 @@ +// 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 + +#if defined(SFIZZ_IMPLEMENT_CXX17_ALIGNED_NEW_SUPPORT) +#include +#if defined(_WIN32) +# include +#else +# include +#endif + +void* operator new(std::size_t count, std::align_val_t al, const std::nothrow_t&) noexcept +{ + void *ptr; +#if defined(_WIN32) + ptr = ::_aligned_malloc(count, static_cast(al)); +#else + if (::posix_memalign(&ptr, static_cast(al), count) != 0) + ptr = nullptr; +#endif + return ptr; +} + +void operator delete(void* ptr, std::align_val_t, const std::nothrow_t&) noexcept +{ +#if defined(_WIN32) + ::_aligned_free(ptr); +#else + ::free(ptr); +#endif +} + +//------------------------------------------------------------------------------ + +void* operator new(std::size_t count, std::align_val_t al) +{ + void *ptr = operator new(count, al, std::nothrow); + if (!ptr) + throw std::bad_alloc(); + return ptr; +} + +void operator delete(void* ptr, std::align_val_t al) noexcept +{ + operator delete(ptr, al, std::nothrow); +} + +//------------------------------------------------------------------------------ + +void* operator new[](std::size_t count, std::align_val_t al) +{ + return operator new(count, al); +} + +void* operator new[](std::size_t count, std::align_val_t al, const std::nothrow_t& tag) noexcept +{ + return operator new(count, al, tag); +} + +void operator delete[](void* ptr, std::align_val_t al) noexcept +{ + operator delete(ptr, al); +} + +void operator delete[](void* ptr, std::align_val_t al, const std::nothrow_t& tag) noexcept +{ + operator delete(ptr, al, tag); +} + + +#endif // defined(SFIZZ_IMPLEMENT_CXX17_ALIGNED_NEW_SUPPORT) From 8bf488897b4a8a234e976668fcdc95b9002594e8 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 22 Sep 2021 05:34:40 +0200 Subject: [PATCH 182/193] tests: workaround for incomplete C++17 runtime on macOS --- tests/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9ae219dbd..fa7084584 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -56,6 +56,10 @@ set(SFIZZ_TEST_SOURCES add_executable(sfizz_tests ${SFIZZ_TEST_SOURCES}) target_link_libraries(sfizz_tests PRIVATE sfizz::internal sfizz::static sfizz::spin_mutex sfizz::jsl sfizz::filesystem) +if(APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "10.12") + # workaround for incomplete C++17 runtime on macOS + target_compile_definitions(sfizz_tests PRIVATE "CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS") +endif() sfizz_enable_lto_if_needed(sfizz_tests) sfizz_enable_fast_math(sfizz_tests) catch_discover_tests(sfizz_tests) From 7552e5e8b89a1b022b8ad02f2e0898e5f949f3bb Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Thu, 23 Sep 2021 01:40:57 +0200 Subject: [PATCH 183/193] Include headers which seem needed on BSD-like --- src/sfizz/Buffer.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sfizz/Buffer.h b/src/sfizz/Buffer.h index e8a5135aa..c522f7e12 100644 --- a/src/sfizz/Buffer.h +++ b/src/sfizz/Buffer.h @@ -26,10 +26,12 @@ #pragma once #include "Config.h" #include "utility/LeakDetector.h" +#include #include #include #include #include +#include #include #include From 867cf2b459f8e2f5bf4a148538add2a95ad55ca8 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Thu, 23 Sep 2021 20:54:35 +0200 Subject: [PATCH 184/193] Fix a problem in the X11 runloop --- plugins/vst/X11RunLoop.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/vst/X11RunLoop.cpp b/plugins/vst/X11RunLoop.cpp index 04725c5a1..2d8ded27e 100644 --- a/plugins/vst/X11RunLoop.cpp +++ b/plugins/vst/X11RunLoop.cpp @@ -187,7 +187,7 @@ bool RunLoop::unregisterEventHandler(X11::IEventHandler* handler) return false; Impl::EventHandler *eh = impl->eventHandlers[index].get(); - if (!impl->runLoop->unregisterEventHandler(eh)) + if (impl->runLoop->unregisterEventHandler(eh) != Steinberg::kResultTrue) return false; eh->alive = false; @@ -219,7 +219,7 @@ bool RunLoop::unregisterTimer(X11::ITimerHandler* handler) return false; Impl::TimerHandler *th = impl->timerHandlers[index].get(); - if (!impl->runLoop->unregisterTimer(th)) + if (impl->runLoop->unregisterTimer(th) != Steinberg::kResultTrue) return false; th->alive = false; From 61fb6b0106c4d20b6f0d1cac0ef352aae89b065c Mon Sep 17 00:00:00 2001 From: redtide Date: Sat, 9 Oct 2021 12:55:57 +0200 Subject: [PATCH 185/193] Use matrix builds on GitHub Actions --- .github/workflows/build.yml | 152 +++++++++--------------------------- 1 file changed, 36 insertions(+), 116 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 082ff7966..3ffc726a8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -140,15 +140,24 @@ jobs: name: MOD devices tarball path: ${{runner.workspace}}/build/${{env.install_name}}.tar.gz - build_for_win32: + build_for_windows: runs-on: windows-2019 + strategy: + matrix: + include: + - platform: x86 + pkg_platform: Win32 + release_arch: Win32 + bits: 32 + - platform: x64 + pkg_platform: Win64 + release_arch: x64 + bits: 64 steps: - name: Set install name run: | - echo platform=x86 >> "${Env:GITHUB_ENV}" - echo release_arch=Win32 >> "${Env:GITHUB_ENV}" echo "install_ref=$(${Env:GITHUB_REF}.split('/')[-1])" >> "${Env:GITHUB_ENV}" - echo "install_name=sfizz-$(${Env:GITHUB_REF}.split('/')[-1])-win32" >> "${Env:GITHUB_ENV}" + echo "install_name=sfizz-$(${Env:GITHUB_REF}.split('/')[-1])-win${{matrix.bits}}" >> "${Env:GITHUB_ENV}" - uses: actions/checkout@v2 with: submodules: recursive @@ -158,42 +167,7 @@ jobs: - name: Configure CMake working-directory: ${{runner.workspace}}/build run: | - cmake "${Env:GITHUB_WORKSPACE}" -G"Visual Studio 16 2019" -A"${Env:release_arch}" -DCMAKE_BUILD_TYPE="${Env:BUILD_TYPE}" -DCMAKE_CXX_STANDARD=17 -DSFIZZ_TESTS=ON -DSFIZZ_VST=ON -DSFIZZ_LV2=ON -DSFIZZ_PUREDATA=ON - - name: Build tests - working-directory: ${{runner.workspace}}/build - run: cmake --build . --config "${Env:BUILD_TYPE}" -j 2 --target sfizz_tests - - name: Test - run: ${{runner.workspace}}/build/tests/Release/sfizz_tests - - name: Build all - working-directory: ${{runner.workspace}}/build - run: cmake --build . --config "${Env:BUILD_TYPE}" -j 2 - - name: Create installer - working-directory: ${{runner.workspace}}/build - run: iscc /O"." /F"${Env:install_name}" /dARCH="${Env:platform}" innosetup.iss - - uses: actions/upload-artifact@v2 - with: - name: Win32 installer - path: ${{runner.workspace}}/build/${{env.install_name}}.exe - - build_for_win64: - runs-on: windows-2019 - steps: - - name: Set install name - run: | - echo platform=x64 >> "${Env:GITHUB_ENV}" - echo release_arch=x64 >> "${Env:GITHUB_ENV}" - echo "install_ref=$(${Env:GITHUB_REF}.split('/')[-1])" >> "${Env:GITHUB_ENV}" - echo "install_name=sfizz-$(${Env:GITHUB_REF}.split('/')[-1])-win64" >> "${Env:GITHUB_ENV}" - - uses: actions/checkout@v2 - with: - submodules: recursive - - name: Create Build Environment - working-directory: ${{runner.workspace}} - run: cmake -E make_directory build - - name: Configure CMake - working-directory: ${{runner.workspace}}/build - run: | - cmake "${Env:GITHUB_WORKSPACE}" -G"Visual Studio 16 2019" -A"${Env:release_arch}" -DCMAKE_BUILD_TYPE="${Env:BUILD_TYPE}" -DCMAKE_CXX_STANDARD=17 -DSFIZZ_TESTS=ON -DSFIZZ_VST=ON -DSFIZZ_LV2=ON -DSFIZZ_PUREDATA=ON + cmake "${Env:GITHUB_WORKSPACE}" -G"Visual Studio 16 2019" -A"${{matrix.release_arch}}" -DCMAKE_BUILD_TYPE="${Env:BUILD_TYPE}" -DCMAKE_CXX_STANDARD=17 -DSFIZZ_TESTS=ON -DSFIZZ_VST=ON -DSFIZZ_LV2=ON -DSFIZZ_PUREDATA=ON - name: Build tests working-directory: ${{runner.workspace}}/build run: cmake --build . --config "${Env:BUILD_TYPE}" -j 2 --target sfizz_tests @@ -203,94 +177,42 @@ jobs: working-directory: ${{runner.workspace}}/build run: cmake --build . --config "${Env:BUILD_TYPE}" -j 2 - name: Install pluginval + if: ${{ matrix.platform == 'x64' }} run: | Invoke-WebRequest https://github.com/Tracktion/pluginval/releases/download/latest_release/pluginval_Windows.zip -OutFile pluginval.zip Expand-Archive pluginval.zip -DestinationPath pluginval echo "$(Get-Location)\pluginval" | Out-File -FilePath ${Env:GITHUB_PATH} -Encoding utf8 -Append pluginval\pluginval --version - name: Validate VST3 + if: ${{ matrix.platform == 'x64' }} working-directory: ${{runner.workspace}}/build run: pluginval --validate-in-process --validate sfizz.vst3 - name: Create installer working-directory: ${{runner.workspace}}/build - run: iscc /O"." /F"${Env:install_name}" /dARCH="${Env:platform}" innosetup.iss + run: iscc /O"." /F"${Env:install_name}" /dARCH="${{matrix.platform}}" innosetup.iss - uses: actions/upload-artifact@v2 with: - name: Win64 installer + name: ${{matrix.pkg_platform}} installer path: ${{runner.workspace}}/build/${{env.install_name}}.exe - build_for_mingw32: - runs-on: ubuntu-18.04 - container: - image: archlinux - steps: - - name: Set install name - run: | - echo "install_ref=${GITHUB_REF##*/}" >> "$GITHUB_ENV" - echo "install_name=sfizz-${GITHUB_REF##*/}-mingw32" >> "$GITHUB_ENV" - - name: Configure pacman repositories - shell: bash - run: | - cat >>/etc/pacman.conf <> "$GITHUB_ENV" - echo "install_name=sfizz-${GITHUB_REF##*/}-mingw64" >> "$GITHUB_ENV" + echo "install_name=sfizz-${GITHUB_REF##*/}-mingw${{matrix.bits}}" >> "$GITHUB_ENV" - name: Configure pacman repositories shell: bash run: | @@ -313,7 +235,7 @@ jobs: shell: bash run: | cp -vf "$GITHUB_WORKSPACE"/scripts/mingw_dwrite_3.h \ - /usr/x86_64-w64-mingw32/include/dwrite_3.h + /usr/${{matrix.platform}}-w64-mingw32/include/dwrite_3.h - name: Create Build Environment shell: bash working-directory: ${{runner.workspace}} @@ -322,7 +244,7 @@ jobs: shell: bash working-directory: ${{runner.workspace}}/build run: | - x86_64-w64-mingw32-cmake "$GITHUB_WORKSPACE" -G Ninja \ + ${{matrix.platform}}-w64-mingw32-cmake "$GITHUB_WORKSPACE" -G Ninja \ -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ -DENABLE_LTO=OFF \ -DSFIZZ_JACK=OFF \ @@ -333,16 +255,16 @@ jobs: - name: Build shell: bash working-directory: ${{runner.workspace}}/build - run: x86_64-w64-mingw32-cmake --build . --config "$BUILD_TYPE" -j 2 + run: ${{matrix.platform}}-w64-mingw32-cmake --build . --config "$BUILD_TYPE" -j 2 - name: Install working-directory: ${{runner.workspace}}/build shell: bash run: | - DESTDIR="$(pwd)/$install_name" x86_64-w64-mingw32-cmake --build . --config "$BUILD_TYPE" --target install + DESTDIR="$(pwd)/$install_name" ${{matrix.platform}}-w64-mingw32-cmake --build . --config "$BUILD_TYPE" --target install tar czvf "$install_name".tar.gz "$install_name" - uses: actions/upload-artifact@v2 with: - name: Win64 MinGW tarball + name: ${{matrix.pkg_platform}} MinGW tarball path: ${{runner.workspace}}/build/${{env.install_name}}.tar.gz build_with_makefile: @@ -408,10 +330,8 @@ jobs: needs: - build_for_linux - build_for_mod - - build_for_mingw32 - - build_for_mingw64 - - build_for_win32 - - build_for_win64 + - build_for_mingw + - build_for_windows - archive_source_code steps: - name: Set install name From 956709c9c61d61aa479aecc6441b1dde961efdaf Mon Sep 17 00:00:00 2001 From: alexmitchell Date: Mon, 11 Oct 2021 15:42:42 +1030 Subject: [PATCH 186/193] add polyphony option to sfizz_render --- clients/sfizz_render.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clients/sfizz_render.cpp b/clients/sfizz_render.cpp index 48b09bc1e..8bb84ea3b 100644 --- a/clients/sfizz_render.cpp +++ b/clients/sfizz_render.cpp @@ -66,6 +66,7 @@ int main(int argc, char** argv) bool help { false }; bool useEOT { false }; int quality { 2 }; + int polyphony { 64 }; options.add_options() ("sfz", "SFZ file", cxxopts::value()) @@ -74,6 +75,7 @@ int main(int argc, char** argv) ("b,blocksize", "Block size for the sfizz callbacks", cxxopts::value(blockSize)) ("s,samplerate", "Output sample rate", cxxopts::value(sampleRate)) ("q,quality", "Resampling quality", cxxopts::value(quality)) + ("p,polyphony", "Polyphony max", cxxopts::value(polyphony)) ("v,verbose", "Verbose output", cxxopts::value(verbose)) ("log", "Produce logs", cxxopts::value()) ("use-eot", "End the rendering at the last End of Track Midi message", cxxopts::value(useEOT)) @@ -114,11 +116,13 @@ int main(int argc, char** argv) LOG_INFO("Output file: " << outputPath.string()); LOG_INFO("Block size: " << blockSize); LOG_INFO("Sample rate: " << sampleRate); + LOG_INFO("Polyphony Max: " << polyphony); sfz::Synth synth; synth.setSamplesPerBlock(blockSize); synth.setSampleRate(sampleRate); synth.setSampleQuality(sfz::Synth::ProcessMode::ProcessFreewheeling, quality); + synth.setNumVoices(polyphony); synth.enableFreeWheeling(); if (params.count("log") > 0) From 4ec32024ce352a299dd2186cbf7ad89ab01fc801 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 14 Oct 2021 23:37:21 +0200 Subject: [PATCH 187/193] Evaluate the curve in extended CCs --- src/sfizz/modulations/sources/Controller.cpp | 21 ++++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/sfizz/modulations/sources/Controller.cpp b/src/sfizz/modulations/sources/Controller.cpp index 7d6caea9e..eafffd56c 100644 --- a/src/sfizz/modulations/sources/Controller.cpp +++ b/src/sfizz/modulations/sources/Controller.cpp @@ -93,12 +93,11 @@ void ControllerSource::generate(const ModKey& sourceKey, NumericId voiceI const MidiState& ms = res.getMidiState(); bool canShortcut = false; - auto transformValue = [p, &curve](float x) { + auto transformValue = [&] (float x) { return curve.evalNormalized(x); }; - // Ignore the eventual curve for the extended CCs - auto quantize = [p](float x) { + auto quantize = [&] (float x) { if (p.step > 0.0f) return std::trunc(x / p.step) * p.step; @@ -112,7 +111,7 @@ void ControllerSource::generate(const ModKey& sourceKey, NumericId voiceI voice && voice->getTriggerEvent().type == TriggerEventType::NoteOn ? impl_->res_->getMidiState().getPolyAftertouch(voice->getTriggerEvent().number) : 0.0f; - sfz::fill(buffer, quantize(fillValue)); + sfz::fill(buffer, quantize(transformValue(fillValue))); canShortcut = true; break; } @@ -122,7 +121,7 @@ void ControllerSource::generate(const ModKey& sourceKey, NumericId voiceI voice && voice->getTriggerEvent().type == TriggerEventType::NoteOn ? voice->getTriggerEvent().value : 0.0f; - sfz::fill(buffer, quantize(fillValue)); + sfz::fill(buffer, quantize(transformValue(fillValue))); canShortcut = true; break; } @@ -132,42 +131,42 @@ void ControllerSource::generate(const ModKey& sourceKey, NumericId voiceI voice && voice->getTriggerEvent().type == TriggerEventType::NoteOff ? voice->getTriggerEvent().value : 0.0f; - sfz::fill(buffer, quantize(fillValue)); + sfz::fill(buffer, quantize(transformValue(fillValue))); canShortcut = true; break; } case ExtendedCCs::keyboardNoteNumber: { const auto voice = impl_->voiceManager_->getVoiceById(voiceId); const float fillValue = voice ? normalize7Bits(voice->getTriggerEvent().number) : 0.0f; - sfz::fill(buffer, quantize(fillValue)); + sfz::fill(buffer, quantize(transformValue(fillValue))); canShortcut = true; break; } case ExtendedCCs::keyboardNoteGate: { const auto voice = impl_->voiceManager_->getVoiceById(voiceId); const float fillValue = voice ? voice->getExtendedCCValues().noteGate : 0.0f; - sfz::fill(buffer, quantize(fillValue)); + sfz::fill(buffer, quantize(transformValue(fillValue))); canShortcut = true; break; } case ExtendedCCs::unipolarRandom: { const auto voice = impl_->voiceManager_->getVoiceById(voiceId); const float fillValue = voice ? voice->getExtendedCCValues().unipolar : 0.0f; - sfz::fill(buffer, quantize(fillValue)); + sfz::fill(buffer, quantize(transformValue(fillValue))); canShortcut = true; break; } case ExtendedCCs::bipolarRandom: { const auto voice = impl_->voiceManager_->getVoiceById(voiceId); const float fillValue = voice ? voice->getExtendedCCValues().bipolar : 0.0f; - sfz::fill(buffer, quantize(fillValue)); + sfz::fill(buffer, quantize(transformValue(fillValue))); canShortcut = true; break; } case ExtendedCCs::alternate: { const auto voice = impl_->voiceManager_->getVoiceById(voiceId); const float fillValue = voice ? voice->getExtendedCCValues().alternate : 0.0f; - sfz::fill(buffer, quantize(fillValue)); + sfz::fill(buffer, quantize(transformValue(fillValue))); canShortcut = true; break; } From cd6bba53788151e71bf36d8b0534a69dcedfefa6 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 14 Oct 2021 23:48:23 +0200 Subject: [PATCH 188/193] Reset note and octave offset when resetting the synth --- src/sfizz/Synth.cpp | 2 ++ src/sfizz/SynthMessaging.cpp | 8 ++++++++ tests/SynthT.cpp | 27 +++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index c7893385e..055ec82d3 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -263,6 +263,8 @@ void Synth::Impl::clear() rootPath_.clear(); numGroups_ = 0; numMasters_ = 0; + noteOffset_ = 0; + octaveOffset_ = 0; currentSwitch_ = absl::nullopt; defaultPath_ = ""; image_ = ""; diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index 9b00799fc..e2ff06f9a 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -87,6 +87,14 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co client.receive<'i'>(delay, path, int(impl.resources_.getFilePool().getNumPreloadedSamples())); } break; + MATCH("/octave_offset", "") { + client.receive<'i'>(delay, path, impl.octaveOffset_); + } break; + + MATCH("/note_offset", "") { + client.receive<'i'>(delay, path, impl.noteOffset_); + } break; + //---------------------------------------------------------------------- MATCH("/key/slots", "") { diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index b3f32f1a8..b0f3feda5 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -1937,3 +1937,30 @@ TEST_CASE("[Synth] Sequences also work on cc triggers") synth.renderBlock(buffer); REQUIRE( playingSamples(synth) == std::vector { "*sine", "*saw", "*sine" } ); } + +TEST_CASE("[Synth] Loading resets note and octave offsets") +{ + sfz::Synth synth; + std::vector messageList; + sfz::Client client(&messageList); + client.setReceiveCallback(&simpleMessageReceiver); + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/octave_offset.sfz", R"( + note_offset=1 octave_offset=-1 + sample=*sine + )"); + synth.dispatchMessage(client, 0, "/note_offset", "", nullptr); + synth.dispatchMessage(client, 0, "/octave_offset", "", nullptr); + synth.loadSfzString(fs::current_path() / "tests/TestFiles/octave_offset.sfz", R"( + sample=*sine + )"); + synth.dispatchMessage(client, 0, "/note_offset", "", nullptr); + synth.dispatchMessage(client, 0, "/octave_offset", "", nullptr); + std::vector expected { + "/note_offset,i : { 1 }", + "/octave_offset,i : { -1 }", + "/note_offset,i : { 0 }", + "/octave_offset,i : { 0 }", + }; + REQUIRE(messageList == expected); +} From d38130753e9bb88d5678e617882c924c757addd7 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 16 Oct 2021 12:49:39 +0200 Subject: [PATCH 189/193] Use the sfztools vstgui fork --- .gitmodules | 2 +- plugins/editor/external/vstgui4 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 441fc8070..7850d1b81 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,7 +17,7 @@ shallow = true [submodule "vst/external/VST_SDK/VST3_SDK/vstgui4"] path = plugins/editor/external/vstgui4 - url = https://github.com/steinbergmedia/vstgui.git + url = https://github.com/sfztools/vstgui.git shallow = true [submodule "external/st_audiofile/thirdparty/dr_libs"] path = external/st_audiofile/thirdparty/dr_libs diff --git a/plugins/editor/external/vstgui4 b/plugins/editor/external/vstgui4 index f6db2b250..0db8738ae 160000 --- a/plugins/editor/external/vstgui4 +++ b/plugins/editor/external/vstgui4 @@ -1 +1 @@ -Subproject commit f6db2b250ce9c08ff3f6939f7a0744d5d3a0a133 +Subproject commit 0db8738ae73ff76842eb0bfe90cdf2039690a5b5 From 6310aa1abc454d31db4fcf1cadfc579de7712b67 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 20 Oct 2021 16:08:33 +0200 Subject: [PATCH 190/193] Honor lorand/hirand in CC triggered regions --- src/sfizz/Layer.cpp | 17 +++++++- src/sfizz/Layer.h | 19 +++++++-- src/sfizz/Synth.cpp | 7 ++-- tests/DirectRegionT.cpp | 13 +++--- tests/RegionActivationT.cpp | 38 ++++++++--------- tests/RegionTriggersT.cpp | 81 +++++++++++++++++++++++++++---------- 6 files changed, 118 insertions(+), 57 deletions(-) diff --git a/src/sfizz/Layer.cpp b/src/sfizz/Layer.cpp index a90261da3..b06c2f713 100644 --- a/src/sfizz/Layer.cpp +++ b/src/sfizz/Layer.cpp @@ -129,7 +129,7 @@ bool Layer::registerNoteOff(int noteNumber, float velocity, float randValue) noe return false; } -bool Layer::registerCC(int ccNumber, float ccValue, bool dontTrigger) noexcept +void Layer::updateCCState(int ccNumber, float ccValue) noexcept { const Region& region = region_; @@ -151,8 +151,21 @@ bool Layer::registerCC(int ccNumber, float ccValue, bool dontTrigger) noexcept ccSwitched_.set(ccNumber, true); else ccSwitched_.set(ccNumber, false); +} + +bool Layer::registerCC(int ccNumber, float ccValue, float randValue) noexcept +{ + const Region& region = region_; + + updateCCState(ccNumber, ccValue); + + if (!region.triggerOnCC) + return false; + + const bool randOk = region.randRange.contains(randValue) + || (randValue >= 1.0f && region.randRange.isValid() && region.randRange.getEnd() >= 1.0f); - if (dontTrigger || !region.triggerOnCC) + if (!randOk) return false; if (auto triggerRange = region.ccTriggers.get(ccNumber)) { diff --git a/src/sfizz/Layer.h b/src/sfizz/Layer.h index ec7a78063..d049e2c01 100644 --- a/src/sfizz/Layer.h +++ b/src/sfizz/Layer.h @@ -80,15 +80,26 @@ struct Layer { */ bool registerNoteOff(int noteNumber, float velocity, float randValue) noexcept; /** - * @brief Register a new CC event. The region may be switched on or off using CCs so - * this function checks if it indeeds need to activate or not. + * @brief Update the internal state of the layer with respect to CC events (sustain, CC + * switch, etc). * * @param ccNumber * @param ccValue - * @return true if the region should trigger on this event * @return false */ - bool registerCC(int ccNumber, float ccValue, bool dontTrigger = false) noexcept; + void updateCCState(int ccNumber, float ccValue) noexcept; + /** + * @brief Register a new CC event,. This method updates the internal CC state with respect + * to CC events (sustain, CC switch, etc) and checks if the region should trigger on this + * event. + * + * @param ccNumber + * @param ccValue + * @param randValue + * @return true if the region should trigger on this event + * @return false otherwise + */ + bool registerCC(int ccNumber, float ccValue, float randValue) noexcept; /** * @brief Register a new pitch wheel event. * diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 055ec82d3..049af5243 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -727,7 +727,7 @@ void Synth::Impl::finalizeSfzLoad() // Defaults MidiState& midiState = resources_.getMidiState(); for (int cc = 0; cc < config::numCCs; cc++) { - layer.registerCC(cc, midiState.getCCValue(cc), true); + layer.updateCCState(cc, midiState.getCCValue(cc)); } @@ -1273,6 +1273,7 @@ void Synth::Impl::ccDispatch(int delay, int ccNumber, float value) noexcept { SisterVoiceRingBuilder ring; TriggerEvent triggerEvent { TriggerEventType::CC, ccNumber, value }; + const auto randValue = randNoteDistribution_(Random::randomGenerator); for (Layer* layer : ccActivationLists_[ccNumber]) { const Region& region = layer->getRegion(); @@ -1290,7 +1291,7 @@ void Synth::Impl::ccDispatch(int delay, int ccNumber, float value) noexcept } } - if (layer->registerCC(ccNumber, value)) { + if (layer->registerCC(ccNumber, value, randValue)) { checkOffGroups(®ion, delay, ccNumber); startVoice(layer, delay, triggerEvent, ring); } @@ -1952,7 +1953,7 @@ void Synth::Impl::resetAllControllers(int delay) noexcept for (const LayerPtr& layerPtr : layers_) { Layer& layer = *layerPtr; for (int cc = 0; cc < config::numCCs; ++cc) - layer.registerCC(cc, defaultCCValues_[cc], true); + layer.updateCCState(cc, defaultCCValues_[cc]); } } diff --git a/tests/DirectRegionT.cpp b/tests/DirectRegionT.cpp index f54bbfdc8..b86ff5f44 100644 --- a/tests/DirectRegionT.cpp +++ b/tests/DirectRegionT.cpp @@ -43,7 +43,7 @@ TEST_CASE("[Direct Region Tests] Release and release key") Layer layer { region, midiState }; layer.delayedSustainReleases_.reserve(config::delayedReleaseVoices); midiState.ccEvent(0, 64, 0.0f); - layer.registerCC(64, 0.0f); + layer.updateCCState(64, 0.0f); REQUIRE( !layer.registerNoteOn(63, 0.5f, 0.0f) ); REQUIRE( layer.registerNoteOff(63, 0.5f, 0.0f) ); } @@ -53,8 +53,7 @@ TEST_CASE("[Direct Region Tests] Release and release key") Layer layer { region, midiState }; layer.delayedSustainReleases_.reserve(config::delayedReleaseVoices); midiState.ccEvent(0, 64, 1.0f); - layer.registerCC(64, 1.0f); - REQUIRE( !layer.registerCC(64, 1.0f) ); + layer.updateCCState(64, 1.0f); REQUIRE( !layer.registerNoteOn(63, 0.5f, 0.0f) ); REQUIRE( layer.registerNoteOff(63, 0.5f, 0.0f) ); } @@ -65,7 +64,7 @@ TEST_CASE("[Direct Region Tests] Release and release key") Layer layer { region, midiState }; layer.delayedSustainReleases_.reserve(config::delayedReleaseVoices); midiState.ccEvent(0, 64, 0.0f); - layer.registerCC(64, 0.0f); + layer.updateCCState(64, 0.0f); REQUIRE( !layer.registerNoteOn(63, 0.5f, 0.0f) ); REQUIRE( layer.registerNoteOff(63, 0.5f, 0.0f) ); } @@ -76,7 +75,7 @@ TEST_CASE("[Direct Region Tests] Release and release key") Layer layer { region, midiState }; layer.delayedSustainReleases_.reserve(config::delayedReleaseVoices); midiState.ccEvent(0, 64, 1.0f); - layer.registerCC(64, 1.0f); + layer.updateCCState(64, 1.0f); midiState.noteOnEvent(0, 63, 0.5f); REQUIRE( !layer.registerNoteOn(63, 0.5f, 0.0f) ); REQUIRE( !layer.registerNoteOff(63, 0.5f, 0.0f) ); @@ -93,7 +92,7 @@ TEST_CASE("[Direct Region Tests] Release and release key") Layer layer { region, midiState }; layer.delayedSustainReleases_.reserve(config::delayedReleaseVoices); midiState.ccEvent(0, 64, 1.0f); - layer.registerCC(64, 1.0f); + layer.updateCCState(64, 1.0f); midiState.noteOnEvent(0, 63, 0.5f); REQUIRE( !layer.registerNoteOn(63, 0.5f, 0.0f) ); midiState.noteOnEvent(0, 64, 0.6f); @@ -114,7 +113,7 @@ TEST_CASE("[Direct Region Tests] Release and release key") Layer layer { region, midiState }; layer.delayedSustainReleases_.reserve(config::delayedReleaseVoices); midiState.ccEvent(0, 64, 1.0f); - layer.registerCC(64, 1.0f); + layer.updateCCState(64, 1.0f); midiState.noteOnEvent(0, 63, 0.5f); REQUIRE( !layer.registerNoteOn(63, 0.5f, 0.0f) ); midiState.noteOnEvent(0, 66, 0.6f); diff --git a/tests/RegionActivationT.cpp b/tests/RegionActivationT.cpp index fb69c9845..b8ae82186 100644 --- a/tests/RegionActivationT.cpp +++ b/tests/RegionActivationT.cpp @@ -21,7 +21,7 @@ TEST_CASE("Region activation", "Region tests") SECTION("Basic state") { sfz::Layer layer { region, midiState }; - layer.registerCC(4, 0_norm); + layer.updateCCState(4, 0_norm); REQUIRE(layer.isSwitchedOn()); } @@ -30,19 +30,19 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "locc4", "56" }); region.parseOpcode({ "hicc4", "59" }); sfz::Layer layer { region, midiState }; - layer.registerCC(4, 0_norm); + layer.updateCCState(4, 0_norm); REQUIRE(!layer.isSwitchedOn()); - layer.registerCC(4, 57_norm); + layer.updateCCState(4, 57_norm); REQUIRE(layer.isSwitchedOn()); - layer.registerCC(4, 56_norm); + layer.updateCCState(4, 56_norm); REQUIRE(layer.isSwitchedOn()); - layer.registerCC(4, 59_norm); + layer.updateCCState(4, 59_norm); REQUIRE(layer.isSwitchedOn()); - layer.registerCC(4, 43_norm); + layer.updateCCState(4, 43_norm); REQUIRE(!layer.isSwitchedOn()); - layer.registerCC(4, 65_norm); + layer.updateCCState(4, 65_norm); REQUIRE(!layer.isSwitchedOn()); - layer.registerCC(6, 57_norm); + layer.updateCCState(6, 57_norm); REQUIRE(!layer.isSwitchedOn()); } @@ -53,26 +53,26 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "locc54", "18" }); region.parseOpcode({ "hicc54", "27" }); sfz::Layer layer { region, midiState }; - layer.registerCC(4, 0_norm); - layer.registerCC(54, 0_norm); + layer.updateCCState(4, 0_norm); + layer.updateCCState(54, 0_norm); REQUIRE(!layer.isSwitchedOn()); - layer.registerCC(4, 57_norm); + layer.updateCCState(4, 57_norm); REQUIRE(!layer.isSwitchedOn()); - layer.registerCC(54, 19_norm); + layer.updateCCState(54, 19_norm); REQUIRE(layer.isSwitchedOn()); - layer.registerCC(54, 17_norm); + layer.updateCCState(54, 17_norm); REQUIRE(!layer.isSwitchedOn()); - layer.registerCC(54, 27_norm); + layer.updateCCState(54, 27_norm); REQUIRE(layer.isSwitchedOn()); - layer.registerCC(4, 56_norm); + layer.updateCCState(4, 56_norm); REQUIRE(layer.isSwitchedOn()); - layer.registerCC(4, 59_norm); + layer.updateCCState(4, 59_norm); REQUIRE(layer.isSwitchedOn()); - layer.registerCC(54, 2_norm); + layer.updateCCState(54, 2_norm); REQUIRE(!layer.isSwitchedOn()); - layer.registerCC(54, 26_norm); + layer.updateCCState(54, 26_norm); REQUIRE(layer.isSwitchedOn()); - layer.registerCC(4, 65_norm); + layer.updateCCState(4, 65_norm); REQUIRE(!layer.isSwitchedOn()); } diff --git a/tests/RegionTriggersT.cpp b/tests/RegionTriggersT.cpp index 5e5931a30..039fe4bbf 100644 --- a/tests/RegionTriggersT.cpp +++ b/tests/RegionTriggersT.cpp @@ -27,7 +27,7 @@ TEST_CASE("Basic triggers", "Region triggers") REQUIRE(layer.registerNoteOn(40, 64_norm, 0.5f)); REQUIRE(!layer.registerNoteOff(40, 64_norm, 0.5f)); REQUIRE(!layer.registerNoteOn(41, 64_norm, 0.5f)); - REQUIRE(!layer.registerCC(63, 64_norm)); + REQUIRE(!layer.registerCC(63, 64_norm, 0.0f)); } SECTION("lokey and hikey") { @@ -42,7 +42,7 @@ TEST_CASE("Basic triggers", "Region triggers") REQUIRE(!layer.registerNoteOn(43, 64_norm, 0.5f)); REQUIRE(!layer.registerNoteOff(42, 64_norm, 0.5f)); REQUIRE(!layer.registerNoteOff(42, 64_norm, 0.5f)); - REQUIRE(!layer.registerCC(63, 64_norm)); + REQUIRE(!layer.registerCC(63, 64_norm, 0.0f)); } SECTION("key and release trigger") { @@ -53,7 +53,7 @@ TEST_CASE("Basic triggers", "Region triggers") REQUIRE(layer.registerNoteOff(40, 64_norm, 0.5f)); REQUIRE(!layer.registerNoteOn(41, 64_norm, 0.5f)); REQUIRE(!layer.registerNoteOff(41, 64_norm, 0.5f)); - REQUIRE(!layer.registerCC(63, 64_norm)); + REQUIRE(!layer.registerCC(63, 64_norm, 0.0f)); } SECTION("key and release_key trigger") { @@ -64,7 +64,7 @@ TEST_CASE("Basic triggers", "Region triggers") REQUIRE(layer.registerNoteOff(40, 64_norm, 0.5f)); REQUIRE(!layer.registerNoteOn(41, 64_norm, 0.5f)); REQUIRE(!layer.registerNoteOff(41, 64_norm, 0.5f)); - REQUIRE(!layer.registerCC(63, 64_norm)); + REQUIRE(!layer.registerCC(63, 64_norm, 0.0f)); } // TODO: first and legato triggers SECTION("lovel and hivel") @@ -130,18 +130,36 @@ TEST_CASE("Basic triggers", "Region triggers") region.parseOpcode({ "on_locc47", "64" }); region.parseOpcode({ "on_hicc47", "68" }); Layer layer1 { region, midiState }; - REQUIRE(!layer1.registerCC(47, 63_norm)); - REQUIRE(layer1.registerCC(47, 64_norm)); - REQUIRE(layer1.registerCC(47, 65_norm)); + REQUIRE(!layer1.registerCC(47, 63_norm, 0.0f)); + REQUIRE(layer1.registerCC(47, 64_norm, 0.0f)); + REQUIRE(layer1.registerCC(47, 65_norm, 0.0f)); region.parseOpcode({ "hikey", "-1" }); Layer layer2 { region, midiState }; - REQUIRE(layer2.registerCC(47, 64_norm)); - REQUIRE(layer2.registerCC(47, 65_norm)); - REQUIRE(layer2.registerCC(47, 66_norm)); - REQUIRE(layer2.registerCC(47, 67_norm)); - REQUIRE(layer2.registerCC(47, 68_norm)); - REQUIRE(!layer2.registerCC(47, 69_norm)); - REQUIRE(!layer2.registerCC(40, 64_norm)); + REQUIRE(layer2.registerCC(47, 64_norm, 0.0f)); + REQUIRE(layer2.registerCC(47, 65_norm, 0.0f)); + REQUIRE(layer2.registerCC(47, 66_norm, 0.0f)); + REQUIRE(layer2.registerCC(47, 67_norm, 0.0f)); + REQUIRE(layer2.registerCC(47, 68_norm, 0.0f)); + REQUIRE(!layer2.registerCC(47, 69_norm, 0.0f)); + REQUIRE(!layer2.registerCC(40, 64_norm, 0.0f)); + } + + SECTION("lorand and hirand with CC triggers") + { + region.parseOpcode({ "on_locc47", "64" }); + region.parseOpcode({ "on_hicc47", "68" }); + region.parseOpcode({ "hikey", "-1" }); + region.parseOpcode({ "lorand", "0.35" }); + region.parseOpcode({ "hirand", "0.40" }); + Layer layer { region, midiState }; + REQUIRE(!layer.registerCC(47, 64_norm, 0.0f)); + REQUIRE(!layer.registerCC(47, 64_norm, 0.34f)); + REQUIRE(layer.registerCC(47, 64_norm, 0.35f)); + REQUIRE(layer.registerCC(47, 64_norm, 0.36f)); + REQUIRE(layer.registerCC(47, 64_norm, 0.37f)); + REQUIRE(layer.registerCC(47, 64_norm, 0.38f)); + REQUIRE(layer.registerCC(47, 64_norm, 0.39f)); + REQUIRE(!layer.registerCC(47, 64_norm, 0.40f)); } SECTION("on_loccN does not disable key triggering") @@ -150,9 +168,9 @@ TEST_CASE("Basic triggers", "Region triggers") region.parseOpcode({ "on_locc1", "127" }); region.parseOpcode({ "on_hicc1", "127" }); Layer layer { region, midiState }; - REQUIRE(!layer.registerCC(1, 126_norm)); - REQUIRE(!layer.registerCC(2, 127_norm)); - REQUIRE(layer.registerCC(1, 127_norm)); + REQUIRE(!layer.registerCC(1, 126_norm, 0.0f)); + REQUIRE(!layer.registerCC(2, 127_norm, 0.0f)); + REQUIRE(layer.registerCC(1, 127_norm, 0.0f)); REQUIRE(layer.registerNoteOn(64, 127_norm, 0.5f)); } @@ -163,8 +181,8 @@ TEST_CASE("Basic triggers", "Region triggers") region.parseOpcode({ "on_hicc1", "127" }); region.parseOpcode({ "key", "-1" }); Layer layer { region, midiState }; - REQUIRE(!layer.registerCC(1, 126_norm)); - REQUIRE(layer.registerCC(1, 127_norm)); + REQUIRE(!layer.registerCC(1, 126_norm, 0.0f)); + REQUIRE(layer.registerCC(1, 127_norm, 0.0f)); REQUIRE(!layer.registerNoteOn(64, 127_norm, 0.5f)); } @@ -175,9 +193,9 @@ TEST_CASE("Basic triggers", "Region triggers") region.parseOpcode({ "on_hicc1", "127" }); region.parseOpcode({ "hikey", "-1" }); Layer layer { region, midiState }; - REQUIRE(!layer.registerCC(1, 126_norm)); - REQUIRE(!layer.registerCC(2, 127_norm)); - REQUIRE(layer.registerCC(1, 127_norm)); + REQUIRE(!layer.registerCC(1, 126_norm, 0.0f)); + REQUIRE(!layer.registerCC(2, 127_norm, 0.0f)); + REQUIRE(layer.registerCC(1, 127_norm, 0.0f)); REQUIRE(!layer.registerNoteOn(64, 127_norm, 0.5f)); } } @@ -371,3 +389,22 @@ TEST_CASE("[Triggers] sw_vel, consider the previous velocity for triggers") REQUIRE(messageList == expected); } } + + +TEST_CASE("[Triggers] Honor lorand/hirand on CC triggers") +{ + Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + std::vector messageList; + Client client(&messageList); + client.setReceiveCallback(&simpleMessageReceiver); + synth.loadSfzString(fs::current_path() / "tests/TestFiles/sw_vel.sfz", R"( + sample=*sine hikey=-1 start_locc64=63 start_hicc64=127 lorand=0 hirand=0.5 + sample=*saw hikey=-1 start_locc64=63 start_hicc64=127 lorand=0.5 hirand=1 + )"); + + synth.hdcc(0, 64, 10_norm); + REQUIRE(numPlayingVoices(synth) == 0); + synth.hdcc(0, 64, 100_norm); + REQUIRE(numPlayingVoices(synth) == 1); +} From cba1f8a86a566e2454b15747f34effe466930b85 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 20 Oct 2021 17:09:16 +0200 Subject: [PATCH 191/193] Don't dispatch off notes when offing a CC-trig'd voice Check behavior --- src/sfizz/Synth.cpp | 3 ++- tests/RegionTriggersT.cpp | 20 +++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 049af5243..fbf5f1589 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -1164,7 +1164,8 @@ void Synth::Impl::checkOffGroups(const Region* region, int delay, int number) for (auto& voice : voiceManager_) { if (voice.checkOffGroup(region, delay, number)) { const TriggerEvent& event = voice.getTriggerEvent(); - noteOffDispatch(delay, event.number, event.value); + if (event.type == TriggerEventType::NoteOn) + noteOffDispatch(delay, event.number, event.value); } } } diff --git a/tests/RegionTriggersT.cpp b/tests/RegionTriggersT.cpp index 039fe4bbf..b25486831 100644 --- a/tests/RegionTriggersT.cpp +++ b/tests/RegionTriggersT.cpp @@ -390,7 +390,6 @@ TEST_CASE("[Triggers] sw_vel, consider the previous velocity for triggers") } } - TEST_CASE("[Triggers] Honor lorand/hirand on CC triggers") { Synth synth; @@ -408,3 +407,22 @@ TEST_CASE("[Triggers] Honor lorand/hirand on CC triggers") synth.hdcc(0, 64, 100_norm); REQUIRE(numPlayingVoices(synth) == 1); } + +TEST_CASE("[Triggers] Offed voices with CC triggers do not activate release triggers") +{ + Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + std::vector messageList; + Client client(&messageList); + client.setReceiveCallback(&simpleMessageReceiver); + synth.loadSfzString(fs::current_path() / "tests/TestFiles/sw_vel.sfz", R"( + sample=*sine hikey=-1 start_locc64=63 start_hicc64=127 group=1 off_by=2 + sample=*saw hikey=-1 start_locc64=0 start_hicc64=62 group=2 + sample=*noise trigger=release_key + )"); + + synth.hdcc(0, 64, 100_norm); + REQUIRE(playingSamples(synth) == std::vector { "*sine" }); + synth.hdcc(10, 64, 10_norm); + REQUIRE(playingSamples(synth) == std::vector { "*saw" }); +} From eadb1b4bebbdfaa3d4fd905cbeaa6db16c0993e8 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 20 Oct 2021 23:53:34 +0200 Subject: [PATCH 192/193] Add man pages for executables And correct a small bug in the jack client --- clients/CMakeLists.txt | 8 ++++++++ clients/jack_client.cpp | 2 +- clients/sfizz_jack.man.in | 37 +++++++++++++++++++++++++++++++++++++ clients/sfizz_render.man.in | 30 ++++++++++++++++++++++++++++++ cmake/LV2Config.cmake | 2 +- cmake/SfizzConfig.cmake | 3 +++ src/CMakeLists.txt | 2 -- 7 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 clients/sfizz_jack.man.in create mode 100644 clients/sfizz_render.man.in diff --git a/clients/CMakeLists.txt b/clients/CMakeLists.txt index a22bbe29c..cf10957b3 100644 --- a/clients/CMakeLists.txt +++ b/clients/CMakeLists.txt @@ -1,14 +1,22 @@ +string(TIMESTAMP MAN_TODAY "%Y-%m-%d") + if(SFIZZ_JACK) add_executable(sfizz_jack MidiHelpers.h jack_client.cpp) target_link_libraries(sfizz_jack PRIVATE sfizz::import sfizz::sfizz sfizz::jack sfizz::spin_mutex absl::flags_parse) sfizz_enable_lto_if_needed(sfizz_jack) + configure_file(sfizz_jack.man.in sfizz_jack.man @ONLY) install(TARGETS sfizz_jack DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT "jack" OPTIONAL) + install(FILES ${PROJECT_BINARY_DIR}/clients/sfizz_jack.man DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 + RENAME sfizz_jack COMPONENT "jack" OPTIONAL) endif() if(SFIZZ_RENDER) add_executable(sfizz_render MidiHelpers.h sfizz_render.cpp) target_link_libraries(sfizz_render PRIVATE sfizz::internal sfizz::fmidi sfizz::cxxopts st_audiofile_formats) sfizz_enable_lto_if_needed(sfizz_render) + configure_file(sfizz_render.man.in sfizz_render.man @ONLY) install(TARGETS sfizz_render DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT "render" OPTIONAL) + install(FILES ${PROJECT_BINARY_DIR}/clients/sfizz_render.man DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 + RENAME sfizz_render COMPONENT "render" OPTIONAL) endif() diff --git a/clients/jack_client.cpp b/clients/jack_client.cpp index 8f5f30d12..20d6e6d5f 100644 --- a/clients/jack_client.cpp +++ b/clients/jack_client.cpp @@ -369,7 +369,7 @@ int main(int argc, char** argv) jack_free(systemPorts); } - if (filesToParse[0]) { + if (!filesToParse.empty() && filesToParse[0]) { loadInstrument(filesToParse[0]); } diff --git a/clients/sfizz_jack.man.in b/clients/sfizz_jack.man.in new file mode 100644 index 000000000..35815adc1 --- /dev/null +++ b/clients/sfizz_jack.man.in @@ -0,0 +1,37 @@ +.TH sfizz_jack 1 "@MAN_TODAY@" "@CMAKE_PROJECT_VERSION@" "sfizz_jack man page" +.SH NAME +sfizz_jack \- Launch a sfizz instance as a JACK client +.SH SYNOPSIS +sfizz_jack [FILE] [OPTIONS...] +.SH DESCRIPTION +sfizz_jack wraps the sfizz SFZ library in a JACK client that can be controlled by a text interface. +.SH OPTIONS +.IP FILE +An optional SFZ file name to load at firsts +.IP "--client_name" +Name for the JACK client +.IP "--num_voices NUMBER" +Change the maximum polyphony +.IP "--preload_size NUMBER" +Bytes to preload in cache for samples +.IP "--state" +Output the state in the JACK loop +.IP "--jack_autoconnect" +Autoconnect the JACK outputs +.SH TEXT INTERFACE +It is possible it interact with the JACK client through the standard input. +The possible commands are +.IP "load_instrument FILE" +Load an instrument +.IP "set_preload_size NUMBER" +Set the number of bytes to preload in cache for samples +.IP "set_voices NUMBER" +Change the maximum polyphony +.IP quit +Quit +.SH SEE ALSO +sfizz_render(1) +.SH BUGS +See the main repository at @SFIZZ_REPOSITORY@. +.SH AUTHOR +Contributors can be seen on the main repository at @SFIZZ_REPOSITORY@. diff --git a/clients/sfizz_render.man.in b/clients/sfizz_render.man.in new file mode 100644 index 000000000..73a12e128 --- /dev/null +++ b/clients/sfizz_render.man.in @@ -0,0 +1,30 @@ +.TH sfizz_render 1 "@MAN_TODAY@" "@CMAKE_PROJECT_VERSION@" "sfizz_render man page" +.SH NAME +sfizz_render \- Render a MIDI file as a WAV file using an SFZ instrument description. +.SH SYNOPSIS +sfizz_render --sfz FILE --wav FILE --midi FILE [OPTIONS...] +.SH DESCRIPTION +sfizz_render wraps the sfizz SFZ library and can be used to render midi file as sound files using an SFZ description file and its associated samples. +.SH OPTIONS +.IP "-b, --blocksize NUMBER" +Block size for the sfizz callbacks +.IP "-s, --samplerate NUMBER" +Output sample rate +.IP "-q, --quality NUMBER" +Resampling quality, like the SFZ sample_quality opcode. A value of 1 will use a linear interpolation of source samples, while higher value will use increasingly better algorithms. +.IP "-p, --polyphony NUMBER" +Maximum polyphony +.IP "-v, --verbose" +Verbose output +.IP "--log PREFIX" +Produce logs +.IP "--use-eot" +End the rendering at the last End of Track Midi message +.IP "-h, --help" +Show help +.SH SEE ALSO +sfizz_jack(1) +.SH BUGS +See the main repository at @SFIZZ_REPOSITORY@. +.SH AUTHOR +Contributors can be seen on the main repository at @SFIZZ_REPOSITORY@. diff --git a/cmake/LV2Config.cmake b/cmake/LV2Config.cmake index a7ca3cd10..da2bd40ff 100644 --- a/cmake/LV2Config.cmake +++ b/cmake/LV2Config.cmake @@ -8,7 +8,7 @@ set(LV2PLUGIN_VERSION_MICRO 0) set(LV2PLUGIN_NAME "sfizz") set(LV2PLUGIN_COMMENT "SFZ sampler") set(LV2PLUGIN_URI "http://sfztools.github.io/sfizz") -set(LV2PLUGIN_REPOSITORY "https://github.com/sfztools/sfizz") +set(LV2PLUGIN_REPOSITORY SFIZZ_REPOSITORY) set(LV2PLUGIN_AUTHOR "SFZTools") set(LV2PLUGIN_EMAIL "paul@ferrand.cc") if(SFIZZ_USE_VCPKG) diff --git a/cmake/SfizzConfig.cmake b/cmake/SfizzConfig.cmake index 9d134fe90..74c0e6a9c 100644 --- a/cmake/SfizzConfig.cmake +++ b/cmake/SfizzConfig.cmake @@ -129,6 +129,9 @@ if(USE_LIBCPP) add_link_options(-lc++abi) # New command on CMake master, not in 3.12 release endif() +set(SFIZZ_REPOSITORY https://github.com/sfztools/sfizz) +include(GNUInstallDirs) + # Don't show build information when building a different project function(show_build_info_if_needed) if(CMAKE_PROJECT_NAME STREQUAL "sfizz") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 13f17efd3..ad678666f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,3 @@ -include(GNUInstallDirs) - set(FAUST_FILES sfizz/dsp/filters/filters_modulable.dsp sfizz/dsp/filters/rbj_filters.dsp From f32fb6375654c7c9efcc247d38b43ba10e5f7118 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 23 Oct 2021 11:27:56 +0200 Subject: [PATCH 193/193] When using GCC 8, explicitely link stdc++fs --- cmake/SfizzDeps.cmake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmake/SfizzDeps.cmake b/cmake/SfizzDeps.cmake index ea277a40a..85c9dbae4 100644 --- a/cmake/SfizzDeps.cmake +++ b/cmake/SfizzDeps.cmake @@ -246,6 +246,12 @@ else() file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/fs_std_impl.cpp" "#include ") add_library(sfizz_filesystem_impl STATIC "${CMAKE_CURRENT_BINARY_DIR}/fs_std_impl.cpp") target_include_directories(sfizz_filesystem_impl PUBLIC "external/filesystem/include") + # Add the needed linker option for GCC 8 + if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" + AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0 + AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) + target_link_libraries(sfizz_filesystem_impl PUBLIC stdc++fs) + endif() # add_library(sfizz_filesystem INTERFACE) target_compile_definitions(sfizz_filesystem INTERFACE "GHC_FILESYSTEM_FWD")