commit
7782ded3e5
10 changed files with 260 additions and 43 deletions
|
|
@ -21,6 +21,7 @@ void sfz::MidiState::noteOnEvent(int delay, int noteNumber, float velocity) noex
|
||||||
if (noteNumber >= 0 && noteNumber < 128) {
|
if (noteNumber >= 0 && noteNumber < 128) {
|
||||||
lastNoteVelocities[noteNumber] = velocity;
|
lastNoteVelocities[noteNumber] = velocity;
|
||||||
noteOnTimes[noteNumber] = internalClock + static_cast<unsigned>(delay);
|
noteOnTimes[noteNumber] = internalClock + static_cast<unsigned>(delay);
|
||||||
|
lastNotePlayed = noteNumber;
|
||||||
activeNotes++;
|
activeNotes++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,6 +106,11 @@ float sfz::MidiState::getNoteVelocity(int noteNumber) const noexcept
|
||||||
return lastNoteVelocities[noteNumber];
|
return lastNoteVelocities[noteNumber];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float sfz::MidiState::getLastVelocity() const noexcept
|
||||||
|
{
|
||||||
|
return lastNoteVelocities[lastNotePlayed];
|
||||||
|
}
|
||||||
|
|
||||||
void sfz::MidiState::insertEventInVector(EventVector& events, int delay, float value)
|
void sfz::MidiState::insertEventInVector(EventVector& events, int delay, float value)
|
||||||
{
|
{
|
||||||
const auto insertionPoint = absl::c_upper_bound(events, delay, MidiEventDelayComparator {});
|
const auto insertionPoint = absl::c_upper_bound(events, delay, MidiEventDelayComparator {});
|
||||||
|
|
@ -168,6 +174,7 @@ void sfz::MidiState::reset() noexcept
|
||||||
|
|
||||||
activeNotes = 0;
|
activeNotes = 0;
|
||||||
internalClock = 0;
|
internalClock = 0;
|
||||||
|
lastNotePlayed = 0;
|
||||||
absl::c_fill(noteOnTimes, 0);
|
absl::c_fill(noteOnTimes, 0);
|
||||||
absl::c_fill(noteOffTimes, 0);
|
absl::c_fill(noteOffTimes, 0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,13 @@ public:
|
||||||
*/
|
*/
|
||||||
float getNoteVelocity(int noteNumber) const noexcept;
|
float getNoteVelocity(int noteNumber) const noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the velocity of the last note played
|
||||||
|
*
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
float getLastVelocity() const noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Register a pitch bend event
|
* @brief Register a pitch bend event
|
||||||
*
|
*
|
||||||
|
|
@ -184,6 +191,11 @@ private:
|
||||||
*/
|
*/
|
||||||
MidiNoteArray<float> lastNoteVelocities;
|
MidiNoteArray<float> lastNoteVelocities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Last note played
|
||||||
|
*/
|
||||||
|
int lastNotePlayed { 0 };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Current known values for the CCs.
|
* @brief Current known values for the CCs.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1484,6 +1484,9 @@ bool sfz::Region::registerNoteOn(int noteNumber, float velocity, float randValue
|
||||||
if (!triggerOnNote)
|
if (!triggerOnNote)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (velocityOverride == VelocityOverride::previous)
|
||||||
|
velocity = midiState.getLastVelocity();
|
||||||
|
|
||||||
const bool velOk = velocityRange.containsWithEnd(velocity);
|
const bool velOk = velocityRange.containsWithEnd(velocity);
|
||||||
const bool randOk = randRange.contains(randValue) || (randValue == 1.0f && randRange.getEnd() == 1.0f);
|
const bool randOk = randRange.contains(randValue) || (randValue == 1.0f && randRange.getEnd() == 1.0f);
|
||||||
const bool firstLegatoNote = (trigger == Trigger::first && midiState.getActiveNotes() == 1);
|
const bool firstLegatoNote = (trigger == Trigger::first && midiState.getActiveNotes() == 1);
|
||||||
|
|
|
||||||
|
|
@ -998,8 +998,8 @@ void Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept
|
||||||
Impl& impl = *impl_;
|
Impl& impl = *impl_;
|
||||||
const auto normalizedVelocity = normalizeVelocity(velocity);
|
const auto normalizedVelocity = normalizeVelocity(velocity);
|
||||||
ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration };
|
ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration };
|
||||||
impl.resources_.midiState.noteOnEvent(delay, noteNumber, normalizedVelocity);
|
|
||||||
impl.noteOnDispatch(delay, noteNumber, normalizedVelocity);
|
impl.noteOnDispatch(delay, noteNumber, normalizedVelocity);
|
||||||
|
impl.resources_.midiState.noteOnEvent(delay, noteNumber, normalizedVelocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept
|
void Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept
|
||||||
|
|
@ -1010,7 +1010,6 @@ void Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept
|
||||||
Impl& impl = *impl_;
|
Impl& impl = *impl_;
|
||||||
const auto normalizedVelocity = normalizeVelocity(velocity);
|
const auto normalizedVelocity = normalizeVelocity(velocity);
|
||||||
ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration };
|
ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration };
|
||||||
impl.resources_.midiState.noteOffEvent(delay, noteNumber, normalizedVelocity);
|
|
||||||
|
|
||||||
// FIXME: Some keyboards (e.g. Casio PX5S) can send a real note-off velocity. In this case, do we have a
|
// 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?
|
// way in sfz to specify that a release trigger should NOT use the note-on velocity?
|
||||||
|
|
@ -1021,6 +1020,7 @@ void Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept
|
||||||
voice.registerNoteOff(delay, noteNumber, replacedVelocity);
|
voice.registerNoteOff(delay, noteNumber, replacedVelocity);
|
||||||
|
|
||||||
impl.noteOffDispatch(delay, noteNumber, replacedVelocity);
|
impl.noteOffDispatch(delay, noteNumber, replacedVelocity);
|
||||||
|
impl.resources_.midiState.noteOffEvent(delay, noteNumber, normalizedVelocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::Impl::startVoice(Region* region, int delay, const TriggerEvent& triggerEvent, SisterVoiceRingBuilder& ring) noexcept
|
void Synth::Impl::startVoice(Region* region, int delay, const TriggerEvent& triggerEvent, SisterVoiceRingBuilder& ring) noexcept
|
||||||
|
|
@ -1061,7 +1061,6 @@ void Synth::Impl::noteOnDispatch(int delay, int noteNumber, float velocity) noex
|
||||||
{
|
{
|
||||||
const auto randValue = randNoteDistribution_(Random::randomGenerator);
|
const auto randValue = randNoteDistribution_(Random::randomGenerator);
|
||||||
SisterVoiceRingBuilder ring;
|
SisterVoiceRingBuilder ring;
|
||||||
const TriggerEvent triggerEvent { TriggerEventType::NoteOn, noteNumber, velocity };
|
|
||||||
|
|
||||||
if (!lastKeyswitchLists_[noteNumber].empty()) {
|
if (!lastKeyswitchLists_[noteNumber].empty()) {
|
||||||
if (currentSwitch_ && *currentSwitch_ != noteNumber) {
|
if (currentSwitch_ && *currentSwitch_ != noteNumber) {
|
||||||
|
|
@ -1089,6 +1088,10 @@ void Synth::Impl::noteOnDispatch(int delay, int noteNumber, float velocity) noex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TriggerEvent triggerEvent { TriggerEventType::NoteOn, noteNumber, velocity };
|
||||||
|
if (region->velocityOverride == VelocityOverride::previous)
|
||||||
|
triggerEvent.value = resources_.midiState.getLastVelocity();
|
||||||
|
|
||||||
startVoice(region, delay, triggerEvent, ring);
|
startVoice(region, delay, triggerEvent, ring);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1150,7 +1153,6 @@ void Synth::Impl::performHdcc(int delay, int ccNumber, float normValue, bool asM
|
||||||
ASSERT(ccNumber >= 0);
|
ASSERT(ccNumber >= 0);
|
||||||
|
|
||||||
ScopedTiming logger { dispatchDuration_, ScopedTiming::Operation::addToDuration };
|
ScopedTiming logger { dispatchDuration_, ScopedTiming::Operation::addToDuration };
|
||||||
resources_.midiState.ccEvent(delay, ccNumber, normValue);
|
|
||||||
|
|
||||||
changedCCsThisCycle_.set(ccNumber);
|
changedCCsThisCycle_.set(ccNumber);
|
||||||
|
|
||||||
|
|
@ -1172,6 +1174,7 @@ void Synth::Impl::performHdcc(int delay, int ccNumber, float normValue, bool asM
|
||||||
voice.registerCC(delay, ccNumber, normValue);
|
voice.registerCC(delay, ccNumber, normValue);
|
||||||
|
|
||||||
ccDispatch(delay, ccNumber, normValue);
|
ccDispatch(delay, ccNumber, normValue);
|
||||||
|
resources_.midiState.ccEvent(delay, ccNumber, normValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::Impl::setDefaultHdcc(int ccNumber, float value)
|
void Synth::Impl::setDefaultHdcc(int ccNumber, float value)
|
||||||
|
|
|
||||||
|
|
@ -1210,6 +1210,45 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
|
||||||
#undef GET_EQ_OR_BREAK
|
#undef GET_EQ_OR_BREAK
|
||||||
|
|
||||||
#undef GET_REGION_OR_BREAK
|
#undef GET_REGION_OR_BREAK
|
||||||
|
|
||||||
|
MATCH("/num_active_voices", "") {
|
||||||
|
client.receive<'i'>(delay, path, impl.voiceManager_.getNumActiveVoices());
|
||||||
|
} break;
|
||||||
|
|
||||||
|
#define GET_VOICE_OR_BREAK(idx) \
|
||||||
|
if (static_cast<int>(idx) >= impl.numVoices_) \
|
||||||
|
break; \
|
||||||
|
const auto& voice = impl.voiceManager_[idx]; \
|
||||||
|
if (voice.isFree()) \
|
||||||
|
break;
|
||||||
|
|
||||||
|
MATCH("/voice&/trigger_value", "") {
|
||||||
|
GET_VOICE_OR_BREAK(indices[0])
|
||||||
|
client.receive<'f'>(delay, path, voice.getTriggerEvent().value);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
MATCH("/voice&/trigger_number", "") {
|
||||||
|
GET_VOICE_OR_BREAK(indices[0])
|
||||||
|
client.receive<'i'>(delay, path, voice.getTriggerEvent().number);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
MATCH("/voice&/trigger_type", "") {
|
||||||
|
GET_VOICE_OR_BREAK(indices[0])
|
||||||
|
const auto& event = voice.getTriggerEvent();
|
||||||
|
switch (event.type) {
|
||||||
|
case TriggerEventType::CC:
|
||||||
|
client.receive<'s'>(delay, path, "cc");
|
||||||
|
break;
|
||||||
|
case TriggerEventType::NoteOn:
|
||||||
|
client.receive<'s'>(delay, path, "note_on");
|
||||||
|
break;
|
||||||
|
case TriggerEventType::NoteOff:
|
||||||
|
client.receive<'s'>(delay, path, "note_on");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
#undef MATCH
|
#undef MATCH
|
||||||
// TODO...
|
// TODO...
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,3 +82,11 @@ TEST_CASE("[MidiState] Extended CCs")
|
||||||
sfz::MidiState state;
|
sfz::MidiState state;
|
||||||
state.ccEvent(0, 142, 64_norm); // should not trap
|
state.ccEvent(0, 142, 64_norm); // should not trap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,19 @@
|
||||||
// license. You should have receive a LICENSE.md file along with the code.
|
// 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 not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||||
|
|
||||||
|
#include "TestHelpers.h"
|
||||||
|
#include "sfizz/Synth.h"
|
||||||
#include "sfizz/Region.h"
|
#include "sfizz/Region.h"
|
||||||
#include "sfizz/SfzHelpers.h"
|
#include "sfizz/SfzHelpers.h"
|
||||||
#include "catch2/catch.hpp"
|
#include "catch2/catch.hpp"
|
||||||
using namespace Catch::literals;
|
using namespace Catch::literals;
|
||||||
using namespace sfz::literals;
|
using namespace sfz::literals;
|
||||||
|
using namespace sfz;
|
||||||
|
|
||||||
TEST_CASE("Basic triggers", "Region triggers")
|
TEST_CASE("Basic triggers", "Region triggers")
|
||||||
{
|
{
|
||||||
sfz::MidiState midiState;
|
MidiState midiState;
|
||||||
sfz::Region region { 0, midiState };
|
Region region { 0, midiState };
|
||||||
|
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
SECTION("key")
|
SECTION("key")
|
||||||
|
|
@ -163,8 +166,8 @@ TEST_CASE("Basic triggers", "Region triggers")
|
||||||
|
|
||||||
TEST_CASE("Legato triggers", "Region triggers")
|
TEST_CASE("Legato triggers", "Region triggers")
|
||||||
{
|
{
|
||||||
sfz::MidiState midiState;
|
MidiState midiState;
|
||||||
sfz::Region region { 0, midiState };
|
Region region { 0, midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
SECTION("First note playing")
|
SECTION("First note playing")
|
||||||
{
|
{
|
||||||
|
|
@ -200,3 +203,140 @@ TEST_CASE("Legato triggers", "Region triggers")
|
||||||
REQUIRE(!region.registerNoteOn(42, 64_norm, 0.5f));
|
REQUIRE(!region.registerNoteOn(42, 64_norm, 0.5f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Triggers] sw_vel, basic")
|
||||||
|
{
|
||||||
|
Synth synth;
|
||||||
|
std::vector<std::string> messageList;
|
||||||
|
Client client(&messageList);
|
||||||
|
client.setReceiveCallback(&simpleMessageReceiver);
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/sw_vel.sfz", R"(
|
||||||
|
<region> key=60 sample=kick.wav
|
||||||
|
<region> key=62 sw_previous=60 sw_vel=previous sample=snare.wav
|
||||||
|
)");
|
||||||
|
synth.noteOn(0, 60, 127);
|
||||||
|
synth.noteOn(10, 62, 10);
|
||||||
|
synth.dispatchMessage(client, 0, "/num_active_voices", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/voice0/trigger_value", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/voice1/trigger_value", "", nullptr);
|
||||||
|
std::vector<std::string> expected {
|
||||||
|
"/num_active_voices,i : { 2 }",
|
||||||
|
"/voice0/trigger_value,f : { 1 }",
|
||||||
|
"/voice1/trigger_value,f : { 1 }",
|
||||||
|
};
|
||||||
|
REQUIRE(messageList == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Triggers] sw_vel, without sw_previous")
|
||||||
|
{
|
||||||
|
Synth synth;
|
||||||
|
std::vector<std::string> messageList;
|
||||||
|
Client client(&messageList);
|
||||||
|
client.setReceiveCallback(&simpleMessageReceiver);
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/sw_vel.sfz", R"(
|
||||||
|
<region> key=60 sample=kick.wav
|
||||||
|
<region> key=62 sw_vel=previous sample=snare.wav
|
||||||
|
)");
|
||||||
|
synth.noteOn(0, 60, 127);
|
||||||
|
synth.noteOn(10, 62, 10);
|
||||||
|
synth.dispatchMessage(client, 0, "/num_active_voices", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/voice0/trigger_value", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/voice1/trigger_value", "", nullptr);
|
||||||
|
std::vector<std::string> expected {
|
||||||
|
"/num_active_voices,i : { 2 }",
|
||||||
|
"/voice0/trigger_value,f : { 1 }",
|
||||||
|
"/voice1/trigger_value,f : { 1 }",
|
||||||
|
};
|
||||||
|
REQUIRE(messageList == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Triggers] sw_vel, with a note in between")
|
||||||
|
{
|
||||||
|
Synth synth;
|
||||||
|
std::vector<std::string> messageList;
|
||||||
|
Client client(&messageList);
|
||||||
|
client.setReceiveCallback(&simpleMessageReceiver);
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/sw_vel.sfz", R"(
|
||||||
|
<region> key=60 sample=kick.wav
|
||||||
|
<region> key=62 sw_vel=previous sample=snare.wav
|
||||||
|
<region> key=64 sample=closedhat.wav
|
||||||
|
)");
|
||||||
|
synth.noteOn(0, 60, 127);
|
||||||
|
synth.noteOn(5, 64, 63);
|
||||||
|
synth.noteOn(10, 62, 10);
|
||||||
|
synth.dispatchMessage(client, 0, "/num_active_voices", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/voice0/trigger_value", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/voice1/trigger_value", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/voice2/trigger_value", "", nullptr);
|
||||||
|
std::vector<std::string> expected {
|
||||||
|
"/num_active_voices,i : { 3 }",
|
||||||
|
"/voice0/trigger_value,f : { 1 }",
|
||||||
|
"/voice1/trigger_value,f : { 0.496063 }",
|
||||||
|
"/voice2/trigger_value,f : { 0.496063 }",
|
||||||
|
};
|
||||||
|
REQUIRE(messageList == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Triggers] sw_vel, with a note in between and sw_previous")
|
||||||
|
{
|
||||||
|
Synth synth;
|
||||||
|
std::vector<std::string> messageList;
|
||||||
|
Client client(&messageList);
|
||||||
|
client.setReceiveCallback(&simpleMessageReceiver);
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/sw_vel.sfz", R"(
|
||||||
|
<region> key=60 sample=kick.wav
|
||||||
|
<region> key=62 sw_previous=60 sw_vel=previous sample=snare.wav
|
||||||
|
<region> key=64 sample=closedhat.wav
|
||||||
|
)");
|
||||||
|
synth.noteOn(0, 60, 127);
|
||||||
|
synth.noteOn(5, 64, 63);
|
||||||
|
synth.noteOn(10, 62, 10);
|
||||||
|
synth.dispatchMessage(client, 0, "/num_active_voices", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/voice0/trigger_value", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/voice1/trigger_value", "", nullptr);
|
||||||
|
std::vector<std::string> expected {
|
||||||
|
"/num_active_voices,i : { 2 }",
|
||||||
|
"/voice0/trigger_value,f : { 1 }",
|
||||||
|
"/voice1/trigger_value,f : { 0.496063 }",
|
||||||
|
};
|
||||||
|
REQUIRE(messageList == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Triggers] sw_vel, consider the previous velocity for triggers")
|
||||||
|
{
|
||||||
|
Synth synth;
|
||||||
|
std::vector<std::string> messageList;
|
||||||
|
Client client(&messageList);
|
||||||
|
client.setReceiveCallback(&simpleMessageReceiver);
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/sw_vel.sfz", R"(
|
||||||
|
<region> key=60 sample=kick.wav
|
||||||
|
<region> key=62 sw_previous=60 sw_vel=previous sample=snare.wav lovel=63
|
||||||
|
)");
|
||||||
|
|
||||||
|
SECTION("Should trigger") {
|
||||||
|
synth.noteOn(0, 60, 127);
|
||||||
|
synth.noteOn(10, 62, 10);
|
||||||
|
synth.dispatchMessage(client, 0, "/num_active_voices", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/voice0/trigger_value", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/voice1/trigger_value", "", nullptr);
|
||||||
|
std::vector<std::string> expected {
|
||||||
|
"/num_active_voices,i : { 2 }",
|
||||||
|
"/voice0/trigger_value,f : { 1 }",
|
||||||
|
"/voice1/trigger_value,f : { 1 }",
|
||||||
|
};
|
||||||
|
REQUIRE(messageList == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Should not trigger") {
|
||||||
|
synth.noteOn(0, 60, 10);
|
||||||
|
synth.noteOn(10, 62, 127);
|
||||||
|
synth.dispatchMessage(client, 0, "/num_active_voices", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/voice0/trigger_value", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/voice1/trigger_value", "", nullptr);
|
||||||
|
std::vector<std::string> expected {
|
||||||
|
"/num_active_voices,i : { 1 }",
|
||||||
|
"/voice0/trigger_value,f : { 0.0787402 }",
|
||||||
|
};
|
||||||
|
REQUIRE(messageList == expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
#include "TestHelpers.h"
|
#include "TestHelpers.h"
|
||||||
#include "sfizz/Synth.h"
|
#include "sfizz/Synth.h"
|
||||||
#include "sfizz/Messaging.h"
|
|
||||||
#include "catch2/catch.hpp"
|
#include "catch2/catch.hpp"
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <absl/strings/str_cat.h>
|
#include <absl/strings/str_cat.h>
|
||||||
|
|
@ -14,40 +13,6 @@
|
||||||
using namespace Catch::literals;
|
using namespace Catch::literals;
|
||||||
using namespace sfz;
|
using namespace sfz;
|
||||||
|
|
||||||
void simpleMessageReceiver(void* data, int delay, const char* path, const char* sig, const sfizz_arg_t* args)
|
|
||||||
{
|
|
||||||
(void)delay;
|
|
||||||
auto& messageList = *reinterpret_cast<std::vector<std::string>*>(data);
|
|
||||||
|
|
||||||
std::string newMessage = absl::StrCat(path, ",", sig, " : { ");
|
|
||||||
for (unsigned i = 0, n = strlen(sig); i < n; ++i) {
|
|
||||||
switch(sig[i]){
|
|
||||||
case 'i':
|
|
||||||
absl::StrAppend(&newMessage, args[i].i);
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
absl::StrAppend(&newMessage, args[i].f);
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
absl::StrAppend(&newMessage, args[i].d);
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
absl::StrAppend(&newMessage, args[i].h);
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
absl::StrAppend(&newMessage, args[i].s);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == (n - 1))
|
|
||||||
absl::StrAppend(&newMessage, " }");
|
|
||||||
else
|
|
||||||
absl::StrAppend(&newMessage, ", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
messageList.push_back(std::move(newMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("[Values] Delay")
|
TEST_CASE("[Values] Delay")
|
||||||
{
|
{
|
||||||
Synth synth;
|
Synth synth;
|
||||||
|
|
|
||||||
|
|
@ -124,3 +124,37 @@ std::string createModulationDotGraph(std::vector<std::string> lines)
|
||||||
|
|
||||||
return graph;
|
return graph;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void simpleMessageReceiver(void* data, int delay, const char* path, const char* sig, const sfizz_arg_t* args)
|
||||||
|
{
|
||||||
|
(void)delay;
|
||||||
|
auto& messageList = *reinterpret_cast<std::vector<std::string>*>(data);
|
||||||
|
|
||||||
|
std::string newMessage = absl::StrCat(path, ",", sig, " : { ");
|
||||||
|
for (unsigned i = 0, n = strlen(sig); i < n; ++i) {
|
||||||
|
switch(sig[i]){
|
||||||
|
case 'i':
|
||||||
|
absl::StrAppend(&newMessage, args[i].i);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
absl::StrAppend(&newMessage, args[i].f);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
absl::StrAppend(&newMessage, args[i].d);
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
absl::StrAppend(&newMessage, args[i].h);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
absl::StrAppend(&newMessage, args[i].s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == (n - 1))
|
||||||
|
absl::StrAppend(&newMessage, " }");
|
||||||
|
else
|
||||||
|
absl::StrAppend(&newMessage, ", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
messageList.push_back(std::move(newMessage));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include "sfizz/Region.h"
|
#include "sfizz/Region.h"
|
||||||
#include "sfizz/Voice.h"
|
#include "sfizz/Voice.h"
|
||||||
#include "sfizz/Range.h"
|
#include "sfizz/Range.h"
|
||||||
|
#include "sfizz/Messaging.h"
|
||||||
#include "catch2/catch.hpp"
|
#include "catch2/catch.hpp"
|
||||||
#include "sfizz/modulations/ModKey.h"
|
#include "sfizz/modulations/ModKey.h"
|
||||||
|
|
||||||
|
|
@ -104,3 +105,8 @@ inline bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs,
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Simple helper function that feeds all received messages into a std::vector<std::string>* in data.
|
||||||
|
*/
|
||||||
|
void simpleMessageReceiver(void* data, int delay, const char* path, const char* sig, const sfizz_arg_t* args);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue