From 2c3314d0adbc0e5acb0fad65fb0c5d5f5a569898 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 18 May 2020 11:59:31 +0200 Subject: [PATCH] Add a listener of voice state changes --- src/sfizz/Synth.cpp | 14 ++++++++++++-- src/sfizz/Synth.h | 8 +++++++- src/sfizz/Voice.cpp | 16 +++++++++++++--- src/sfizz/Voice.h | 26 ++++++++++++++++++++++---- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index b46a18b6..2c889bf0 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -44,6 +44,13 @@ sfz::Synth::~Synth() resources.filePool.emptyFileLoadingQueues(); } +void sfz::Synth::onVoiceStateChanged(int idNumber, Voice::State state) +{ + (void)idNumber; + (void)state; + DBG("Voice " << idNumber << ": state " << static_cast(state)); +} + void sfz::Synth::onParseFullBlock(const std::string& header, const std::vector& members) { switch (hash(header)) { @@ -1094,8 +1101,11 @@ void sfz::Synth::resetVoices(int numVoices) voices.clear(); voices.reserve(numVoices); - for (int i = 0; i < numVoices; ++i) - voices.push_back(absl::make_unique(i, resources)); + for (int i = 0; i < numVoices; ++i) { + auto voice = absl::make_unique(i, resources); + voice->setStateListener(this); + voices.emplace_back(std::move(voice)); + } voiceViewArray.clear(); voiceViewArray.reserve(numVoices); diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index 0819880e..883fedf4 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -60,7 +60,7 @@ namespace sfz { * The jack_client.cpp file contains examples of the most classical usage of the * synth and can be used as a reference. */ -class Synth final : public Parser::Listener { +class Synth final : public Voice::StateListener, public Parser::Listener { public: /** * @brief Construct a new Synth object with no voices. If you want sound @@ -493,6 +493,12 @@ public: */ const std::vector& getCCLabels() const noexcept { return ccLabels; } +protected: + /** + * @brief The voice callback which is called during a change of state. + */ + void onVoiceStateChanged(int idNumber, Voice::State state) override; + protected: /** * @brief The parser callback; this is called by the parent object each time diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 2f3ebe05..fc65892c 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -15,7 +15,7 @@ #include "absl/algorithm/container.h" sfz::Voice::Voice(int voiceNumber, sfz::Resources& resources) -: voiceNumber(voiceNumber), resources(resources) +: voiceNumber(voiceNumber), stateListener(nullptr), resources(resources) { filters.reserve(config::filtersPerVoice); equalizers.reserve(config::eqsPerVoice); @@ -36,7 +36,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, triggerValue = value; this->region = region; - state = State::playing; + switchState(State::playing); ASSERT(delay >= 0); if (delay < 0) @@ -647,7 +647,7 @@ bool sfz::Voice::checkOffGroup(int delay, uint32_t group) noexcept void sfz::Voice::reset() noexcept { - state = State::idle; + switchState(State::idle); region = nullptr; currentPromise.reset(); sourcePosition = 0; @@ -766,3 +766,13 @@ void sfz::Voice::updateChannelPowers(AudioSpan buffer) channelEnvelopeFilters[i].tickLowpass(std::abs(input[s])); } } + + +void sfz::Voice::switchState(State s) +{ + if (s != state) { + state = s; + if (stateListener) + stateListener->onVoiceStateChanged(voiceNumber, s); + } +} diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 0c7937c7..a9b347d2 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -48,6 +48,22 @@ public: { return voiceNumber; } + + enum class State { + idle, + playing + }; + + class StateListener { + public: + virtual void onVoiceStateChanged(int /*idNumber*/, State /*state*/) {} + }; + + /** + * @brief Sets the listener which is called when the voice state changes. + */ + void setStateListener(StateListener *l) noexcept { stateListener = l; } + /** * @brief Change the sample rate of the voice. This is used to compute all * pitch related transformations so it needs to be propagated from the synth @@ -278,14 +294,16 @@ private: void setupOscillatorUnison(); void updateChannelPowers(AudioSpan buffer); + /** + * @brief Modify the voice state and notify any listeners. + */ + void switchState(State s); + const int voiceNumber {}; + StateListener* stateListener = nullptr; Region* region { nullptr }; - enum class State { - idle, - playing - }; State state { State::idle }; bool noteIsOff { false };