// 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 "Voice.h" #include "Resources.h" #include "Config.h" #include "Region.h" #include "PolyphonyGroup.h" #include "VoiceStealing.h" #include namespace sfz { struct VoiceManager : public Voice::StateListener { /** * @brief The voice callback which is called during a change of state. */ void onVoiceStateChanging(NumericId id, Voice::State state) final; /** * @brief Find the voice which is associated with the given identifier. * * @param id * @return const Voice* */ const Voice* getVoiceById(NumericId id) const noexcept; /** * @brief Find the voice which is associated with the given identifier. * * @param id * @return Voice* */ Voice* getVoiceById(NumericId id) noexcept; /** * @brief Reset all voices and clear the polyphony groups */ void reset(); /** * @brief Check if a compatible attack voice is playing for the release region. * * @param releaseRegion * @return true * @return false */ bool playingAttackVoice(const Region* releaseRegion) noexcept; /** * @brief Ensures that the polyphony groups are at least this size. * Call this each time a new `group=N` is given in an sfz file. * * @param groupIdx */ void ensureNumPolyphonyGroups(unsigned groupIdx) noexcept; /** * @brief Set the polyphony for a given group * If the number of polyphony groups is too small, it will * be increased. * * @param groupIdx * @param polyphony */ void setGroupPolyphony(unsigned groupIdx, unsigned polyphony) noexcept; /** * @brief Get a view into a given polyphony group * * @param idx * @return const PolyphonyGroup* */ const PolyphonyGroup* getPolyphonyGroupView(int idx) const noexcept; /** * @brief Clear all voices and polyphony groups. * Also resets the stealing algorithm to default. */ void clear(); /** * @brief Set the stealing algorithm * * @param algorithm */ void setStealingAlgorithm(StealingAlgorithm algorithm); /** * @brief Off voices as necessary depending on the trigger event and started region * * @param region * @param delay * @param triggerEvent */ void checkPolyphony(const Region* region, int delay, const TriggerEvent& triggerEvent) noexcept; /** * @brief Get the number of active voices * * @return size_t */ size_t getNumActiveVoices() const { return activeVoices_.size(); } /** * @brief Get the number of polyphony groups * * @return size_t */ size_t getNumPolyphonyGroups() const noexcept { return polyphonyGroups_.size(); } /** * @brief Find a voice that is not currently playing * * @return Voice* */ Voice* findFreeVoice() noexcept; /** * @brief Require a number of voices from this manager. * In practice, the manager will handle slightly more, in order to * allow voices to die off upon reaching higher polyphony count. * * @param numVoices * @param resources */ void requireNumVoices(int numVoices, Resources& resources); private: int numRequiredVoices_ { config::numVoices }; int numActualVoices_ { static_cast(config::numVoices * config::overflowVoiceMultiplier) }; std::vector list_; std::vector activeVoices_; // These are the `group=` groups where you can off voices std::vector polyphonyGroups_; std::unique_ptr stealer_ { absl::make_unique() }; /** * @brief Check the region polyphony, releasing voices if necessary * * @param region * @param delay */ void checkRegionPolyphony(const Region* region, int delay) noexcept; /** * @brief Check the note polyphony, releasing voices if necessary * * @param region * @param delay * @param triggerEvent */ void checkNotePolyphony(const Region* region, int delay, const TriggerEvent& triggerEvent) noexcept; /** * @brief Check the group polyphony, releasing voices if necessary * * @param region * @param delay */ void checkGroupPolyphony(const Region* region, int delay) noexcept; /** * @brief Check the region set polyphony at all levels, releasing voices if necessary * * @param region * @param delay */ void checkSetPolyphony(const Region* region, int delay) noexcept; /** * @brief Check the engine polyphony, fast releasing voices if necessary * * @param delay */ void checkEnginePolyphony(int delay) noexcept; public: // Vector shortcuts typename decltype(list_)::iterator begin() { return list_.begin(); } typename decltype(list_)::const_iterator cbegin() const { return list_.cbegin(); } typename decltype(list_)::iterator end() { return list_.end(); } typename decltype(list_)::const_iterator cend() const { return list_.cend(); } typename decltype(list_)::reference operator[] (size_t n) { return list_[n]; } typename decltype(list_)::const_reference operator[] (size_t n) const { return list_[n]; } }; } // namespace sfz