From bb5cd652f5348a37ef5df2641a5e27346509a669 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Sun, 8 Nov 2020 23:59:28 +0100 Subject: [PATCH] Move the Synth p-impl to the header file SynthPrivate --- src/CMakeLists.txt | 1 + src/sfizz/Synth.cpp | 272 +--------------------------------- src/sfizz/SynthMessaging.cpp | 2 +- src/sfizz/SynthPrivate.h | 277 +++++++++++++++++++++++++++++++++++ 4 files changed, 280 insertions(+), 272 deletions(-) create mode 100644 src/sfizz/SynthPrivate.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f5ad3e7e..f8171541 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -107,6 +107,7 @@ set (SFIZZ_HEADERS sfizz/SwapAndPop.h sfizz/Synth.h sfizz/SynthConfig.h + sfizz/SynthPrivate.h sfizz/Tuning.h sfizz/Voice.h sfizz/VoiceManager.h diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 66f19755..d2788663 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -4,31 +4,23 @@ // 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 "Synth.h" +#include "SynthPrivate.h" #include "Config.h" #include "Debug.h" -#include "Effects.h" #include "Macros.h" #include "modulations/ModId.h" #include "modulations/ModKey.h" #include "modulations/ModMatrix.h" -#include "modulations/sources/ADSREnvelope.h" -#include "modulations/sources/Controller.h" -#include "modulations/sources/FlexEnvelope.h" -#include "modulations/sources/LFO.h" #include "PolyphonyGroup.h" #include "pugixml.hpp" #include "Region.h" #include "RegionSet.h" #include "Resources.h" #include "ScopedFTZ.h" -#include "SisterVoiceRing.h" #include "StringViewHelpers.h" -#include "TriggerEvent.h" #include "utility/SpinMutex.h" #include "utility/XmlHelpers.h" #include "Voice.h" -#include "VoiceManager.h" #include #include #include @@ -42,268 +34,6 @@ namespace sfz { -struct Synth::Impl final: public Parser::Listener { - Impl(); - ~Impl(); - - /** - * @brief The parser callback; this is called by the parent object each time - * a new region, group, master, global, curve or control set of opcodes - * appears in the parser - * - * @param header the header for the set of opcodes - * @param members the opcode members - */ - void onParseFullBlock(const std::string& header, const std::vector& members) final; - - /** - * @brief The parser callback when an error occurs. - */ - void onParseError(const SourceRange& range, const std::string& message) final; - - /** - * @brief The parser callback when a warning occurs. - */ - void onParseWarning(const SourceRange& range, const std::string& message) final; - - /** - * @brief Reset all CCs; to be used on CC 121 - * - * @param delay the delay for the controller reset - * - */ - void resetAllControllers(int delay) noexcept; - - /** - * @brief Remove all regions, resets all voices and clears everything - * to bring back the synth in its original state. - * - * The callback mutex should be taken to call this function. - */ - void clear(); - - /** - * @brief Helper function to dispatch opcodes - * - * @param members the opcodes of the block - */ - void handleGlobalOpcodes(const std::vector& members); - /** - * @brief Helper function to dispatch opcodes - * - * @param members the opcodes of the block - */ - void handleMasterOpcodes(const std::vector& members); - /** - * @brief Helper function to dispatch opcodes - * - * @param members the opcodes of the block - */ - void handleGroupOpcodes(const std::vector& members, const std::vector& masterMembers); - /** - * @brief Helper function to dispatch opcodes - * - * @param members the opcodes of the block - */ - void handleControlOpcodes(const std::vector& members); - /** - * @brief Helper function to dispatch opcodes - * - * @param members the opcodes of the block - */ - void handleEffectOpcodes(const std::vector& members); - /** - * @brief Helper function to merge all the currently active opcodes - * as set by the successive callbacks and create a new region to store - * in the synth. - * - * @param regionOpcodes the opcodes that are specific to the region - */ - void buildRegion(const std::vector& regionOpcodes); - /** - * @brief Resets and possibly changes the number of voices (polyphony) in - * the synth. - * - * @param numVoices - */ - void resetVoices(int numVoices); - /** - * @brief Make the stored settings take effect in all the voices - */ - void applySettingsPerVoice(); - - /** - * @brief Establish all connections of the modulation matrix. - */ - void setupModMatrix(); - - /** - * @brief Get the modification time of all included sfz files - * - * @return fs::file_time_type - */ - fs::file_time_type checkModificationTime(); - - /** - * @brief Check all regions and start voices for note on events - * - * @param delay - * @param noteNumber - * @param velocity - */ - void noteOnDispatch(int delay, int noteNumber, float velocity) noexcept; - - /** - * @brief Check all regions and start voices for note off events - * - * @param delay - * @param noteNumber - * @param velocity - */ - void noteOffDispatch(int delay, int noteNumber, float velocity) noexcept; - - /** - * @brief Check all regions and start voices for cc events - * - * @param delay - * @param ccNumber - * @param value - */ - void ccDispatch(int delay, int ccNumber, float value) noexcept; - - /** - * @brief Start a voice for a specific region. - * This will do the needed polyphony checks and voice stealing. - * - * @param region - * @param delay - * @param triggerEvent - * @param ring - */ - void startVoice(Region* region, int delay, const TriggerEvent& triggerEvent, SisterVoiceRingBuilder& ring) noexcept; - - /** - * @brief Start all delayed release voices of the region if necessary - * - * @param region - * @param delay - * @param ring - */ - void startDelayedReleaseVoices(Region* region, int delay, SisterVoiceRingBuilder& ring) noexcept; - - /** - * @brief Finalize SFZ loading, following a successful execution of the - * parsing step. - */ - void finalizeSfzLoad(); - - template - static void updateUsedCCsFromCCMap(std::bitset& usedCCs, const CCMap map) noexcept - { - for (auto& mod : map) - usedCCs[mod.cc] = true; - } - - static void updateUsedCCsFromRegion(std::bitset& usedCCs, const Region& region); - static void updateUsedCCsFromModulations(std::bitset& usedCCs, const ModMatrix& mm); - - /** - * @brief Set the default value for a CC - * - * @param ccNumber - * @param value - */ - void setDefaultHdcc(int ccNumber, float value); - - int numGroups_ { 0 }; - int numMasters_ { 0 }; - - // Opcode memory; these are used to build regions, as a new region - // will integrate opcodes from the group, master and global block - std::vector globalOpcodes_; - std::vector masterOpcodes_; - std::vector groupOpcodes_; - - // Names for the CC and notes as set by label_cc and label_key - std::vector ccLabels_; - std::vector keyLabels_; - std::vector keyswitchLabels_; - - // Set as sw_default if present in the file - absl::optional currentSwitch_; - std::vector unknownOpcodes_; - using RegionViewVector = std::vector; - using VoiceViewVector = std::vector; - using RegionPtr = std::unique_ptr; - using RegionSetPtr = std::unique_ptr; - std::vector regions_; - VoiceManager voiceManager_; - - // These are more general "groups" than sfz and encapsulates the full hierarchy - RegionSet* currentSet_ { nullptr }; - std::vector sets_; - - std::array lastKeyswitchLists_; - std::array downKeyswitchLists_; - std::array upKeyswitchLists_; - RegionViewVector previousKeyswitchLists_; - std::array noteActivationLists_; - std::array ccActivationLists_; - - // Effect factory and buses - EffectFactory effectFactory_; - typedef std::unique_ptr EffectBusPtr; - std::vector effectBuses_; // 0 is "main", 1-N are "fx1"-"fxN" - - int samplesPerBlock_ { config::defaultSamplesPerBlock }; - float sampleRate_ { config::defaultSampleRate }; - float volume_ { Default::globalVolume }; - int numVoices_ { config::numVoices }; - Oversampling oversamplingFactor_ { config::defaultOversamplingFactor }; - - // Distribution used to generate random value for the *rand opcodes - std::uniform_real_distribution randNoteDistribution_ { 0, 1 }; - - SpinMutex callbackGuard_; - - // Singletons passed as references to the voices - Resources resources_; - - // Control opcodes - std::string defaultPath_ { "" }; - int noteOffset_ { 0 }; - int octaveOffset_ { 0 }; - - // Modulation source generators - std::unique_ptr genController_; - std::unique_ptr genLFO_; - std::unique_ptr genFlexEnvelope_; - std::unique_ptr genADSREnvelope_; - - // Settings per voice - struct { - size_t maxFilters { 0 }; - size_t maxEQs { 0 }; - size_t maxLFOs { 0 }; - size_t maxFlexEGs { 0 }; - bool havePitchEG { false }; - bool haveFilterEG { false }; - } settingsPerVoice_; - - Duration dispatchDuration_ { 0 }; - - std::chrono::time_point lastGarbageCollection_; - - Parser parser_; - fs::file_time_type modificationTime_ { }; - - std::array defaultCCValues_; - - // Messaging - sfizz_receive_t* broadcastReceiver = nullptr; - void* broadcastData = nullptr; -}; - Synth::Synth() : impl_(new Impl) // NOLINT: (paul) I don't get why clang-tidy complains here { diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index aaeb56fd..9aef0213 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -4,7 +4,7 @@ // license. You should have receive a LICENSE.md file along with the code. // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz -#include "Synth.h" +#include "SynthPrivate.h" #include "StringViewHelpers.h" #include #include diff --git a/src/sfizz/SynthPrivate.h b/src/sfizz/SynthPrivate.h new file mode 100644 index 00000000..f65a9ae8 --- /dev/null +++ b/src/sfizz/SynthPrivate.h @@ -0,0 +1,277 @@ +#pragma once + +#include "Synth.h" +#include "Effects.h" +#include "SisterVoiceRing.h" +#include "TriggerEvent.h" +#include "VoiceManager.h" +#include "modulations/sources/ADSREnvelope.h" +#include "modulations/sources/Controller.h" +#include "modulations/sources/FlexEnvelope.h" +#include "modulations/sources/LFO.h" + +namespace sfz { + +struct Synth::Impl final: public Parser::Listener { + Impl(); + ~Impl(); + + /** + * @brief The parser callback; this is called by the parent object each time + * a new region, group, master, global, curve or control set of opcodes + * appears in the parser + * + * @param header the header for the set of opcodes + * @param members the opcode members + */ + void onParseFullBlock(const std::string& header, const std::vector& members) final; + + /** + * @brief The parser callback when an error occurs. + */ + void onParseError(const SourceRange& range, const std::string& message) final; + + /** + * @brief The parser callback when a warning occurs. + */ + void onParseWarning(const SourceRange& range, const std::string& message) final; + + /** + * @brief Reset all CCs; to be used on CC 121 + * + * @param delay the delay for the controller reset + * + */ + void resetAllControllers(int delay) noexcept; + + /** + * @brief Remove all regions, resets all voices and clears everything + * to bring back the synth in its original state. + * + * The callback mutex should be taken to call this function. + */ + void clear(); + + /** + * @brief Helper function to dispatch opcodes + * + * @param members the opcodes of the block + */ + void handleGlobalOpcodes(const std::vector& members); + /** + * @brief Helper function to dispatch opcodes + * + * @param members the opcodes of the block + */ + void handleMasterOpcodes(const std::vector& members); + /** + * @brief Helper function to dispatch opcodes + * + * @param members the opcodes of the block + */ + void handleGroupOpcodes(const std::vector& members, const std::vector& masterMembers); + /** + * @brief Helper function to dispatch opcodes + * + * @param members the opcodes of the block + */ + void handleControlOpcodes(const std::vector& members); + /** + * @brief Helper function to dispatch opcodes + * + * @param members the opcodes of the block + */ + void handleEffectOpcodes(const std::vector& members); + /** + * @brief Helper function to merge all the currently active opcodes + * as set by the successive callbacks and create a new region to store + * in the synth. + * + * @param regionOpcodes the opcodes that are specific to the region + */ + void buildRegion(const std::vector& regionOpcodes); + /** + * @brief Resets and possibly changes the number of voices (polyphony) in + * the synth. + * + * @param numVoices + */ + void resetVoices(int numVoices); + /** + * @brief Make the stored settings take effect in all the voices + */ + void applySettingsPerVoice(); + + /** + * @brief Establish all connections of the modulation matrix. + */ + void setupModMatrix(); + + /** + * @brief Get the modification time of all included sfz files + * + * @return fs::file_time_type + */ + fs::file_time_type checkModificationTime(); + + /** + * @brief Check all regions and start voices for note on events + * + * @param delay + * @param noteNumber + * @param velocity + */ + void noteOnDispatch(int delay, int noteNumber, float velocity) noexcept; + + /** + * @brief Check all regions and start voices for note off events + * + * @param delay + * @param noteNumber + * @param velocity + */ + void noteOffDispatch(int delay, int noteNumber, float velocity) noexcept; + + /** + * @brief Check all regions and start voices for cc events + * + * @param delay + * @param ccNumber + * @param value + */ + void ccDispatch(int delay, int ccNumber, float value) noexcept; + + /** + * @brief Start a voice for a specific region. + * This will do the needed polyphony checks and voice stealing. + * + * @param region + * @param delay + * @param triggerEvent + * @param ring + */ + void startVoice(Region* region, int delay, const TriggerEvent& triggerEvent, SisterVoiceRingBuilder& ring) noexcept; + + /** + * @brief Start all delayed release voices of the region if necessary + * + * @param region + * @param delay + * @param ring + */ + void startDelayedReleaseVoices(Region* region, int delay, SisterVoiceRingBuilder& ring) noexcept; + + /** + * @brief Finalize SFZ loading, following a successful execution of the + * parsing step. + */ + void finalizeSfzLoad(); + + template + static void updateUsedCCsFromCCMap(std::bitset& usedCCs, const CCMap map) noexcept + { + for (auto& mod : map) + usedCCs[mod.cc] = true; + } + + static void updateUsedCCsFromRegion(std::bitset& usedCCs, const Region& region); + static void updateUsedCCsFromModulations(std::bitset& usedCCs, const ModMatrix& mm); + + /** + * @brief Set the default value for a CC + * + * @param ccNumber + * @param value + */ + void setDefaultHdcc(int ccNumber, float value); + + int numGroups_ { 0 }; + int numMasters_ { 0 }; + + // Opcode memory; these are used to build regions, as a new region + // will integrate opcodes from the group, master and global block + std::vector globalOpcodes_; + std::vector masterOpcodes_; + std::vector groupOpcodes_; + + // Names for the CC and notes as set by label_cc and label_key + std::vector ccLabels_; + std::vector keyLabels_; + std::vector keyswitchLabels_; + + // Set as sw_default if present in the file + absl::optional currentSwitch_; + std::vector unknownOpcodes_; + using RegionViewVector = std::vector; + using VoiceViewVector = std::vector; + using RegionPtr = std::unique_ptr; + using RegionSetPtr = std::unique_ptr; + std::vector regions_; + VoiceManager voiceManager_; + + // These are more general "groups" than sfz and encapsulates the full hierarchy + RegionSet* currentSet_ { nullptr }; + std::vector sets_; + + std::array lastKeyswitchLists_; + std::array downKeyswitchLists_; + std::array upKeyswitchLists_; + RegionViewVector previousKeyswitchLists_; + std::array noteActivationLists_; + std::array ccActivationLists_; + + // Effect factory and buses + EffectFactory effectFactory_; + typedef std::unique_ptr EffectBusPtr; + std::vector effectBuses_; // 0 is "main", 1-N are "fx1"-"fxN" + + int samplesPerBlock_ { config::defaultSamplesPerBlock }; + float sampleRate_ { config::defaultSampleRate }; + float volume_ { Default::globalVolume }; + int numVoices_ { config::numVoices }; + Oversampling oversamplingFactor_ { config::defaultOversamplingFactor }; + + // Distribution used to generate random value for the *rand opcodes + std::uniform_real_distribution randNoteDistribution_ { 0, 1 }; + + SpinMutex callbackGuard_; + + // Singletons passed as references to the voices + Resources resources_; + + // Control opcodes + std::string defaultPath_ { "" }; + int noteOffset_ { 0 }; + int octaveOffset_ { 0 }; + + // Modulation source generators + std::unique_ptr genController_; + std::unique_ptr genLFO_; + std::unique_ptr genFlexEnvelope_; + std::unique_ptr genADSREnvelope_; + + // Settings per voice + struct { + size_t maxFilters { 0 }; + size_t maxEQs { 0 }; + size_t maxLFOs { 0 }; + size_t maxFlexEGs { 0 }; + bool havePitchEG { false }; + bool haveFilterEG { false }; + } settingsPerVoice_; + + Duration dispatchDuration_ { 0 }; + + std::chrono::time_point lastGarbageCollection_; + + Parser parser_; + fs::file_time_type modificationTime_ { }; + + std::array defaultCCValues_; + + // Messaging + sfizz_receive_t* broadcastReceiver = nullptr; + void* broadcastData = nullptr; +}; + +} // namespace sfz