Add a listener of voice state changes

This commit is contained in:
Jean Pierre Cimalando 2020-05-18 11:59:31 +02:00
parent f0f4c34ad8
commit 2c3314d0ad
4 changed files with 54 additions and 10 deletions

View file

@ -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<int>(state));
}
void sfz::Synth::onParseFullBlock(const std::string& header, const std::vector<Opcode>& 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<Voice>(i, resources));
for (int i = 0; i < numVoices; ++i) {
auto voice = absl::make_unique<Voice>(i, resources);
voice->setStateListener(this);
voices.emplace_back(std::move(voice));
}
voiceViewArray.clear();
voiceViewArray.reserve(numVoices);

View file

@ -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<CCNamePair>& 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

View file

@ -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<float> 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);
}
}

View file

@ -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<float> 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 };