From 4fadda23de545fcd23828b4ed4d512b1fe076ab4 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Sat, 18 Jul 2020 13:33:12 +0200 Subject: [PATCH 1/5] Store the last number of active voices instead of computing it on each call to the getter --- src/sfizz/Synth.cpp | 11 +++-------- src/sfizz/Synth.h | 1 + 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index c2c7779c..caf410b1 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -627,11 +627,6 @@ sfz::Voice* sfz::Synth::findFreeVoice() noexcept int sfz::Synth::getNumActiveVoices() const noexcept { - auto activeVoices = 0; - for (const auto& voice : voices) { - if (!voice->isFree()) - activeVoices++; - } return activeVoices; } @@ -711,7 +706,7 @@ void sfz::Synth::renderBlock(AudioSpan buffer) noexcept return; } - int numActiveVoices { 0 }; + activeVoices = 0; { // Main render block ScopedTiming logger { callbackBreakdown.renderMethod, ScopedTiming::Operation::addToDuration }; tempSpan->fill(0.0f); @@ -730,7 +725,7 @@ void sfz::Synth::renderBlock(AudioSpan buffer) noexcept if (voice->isFree()) continue; - numActiveVoices++; + activeVoices++; renderVoiceToOutputs(*voice, *tempSpan); callbackBreakdown.data += voice->getLastDataDuration(); callbackBreakdown.amplitude += voice->getLastAmplitudeDuration(); @@ -770,7 +765,7 @@ void sfz::Synth::renderBlock(AudioSpan buffer) noexcept } callbackBreakdown.dispatch = dispatchDuration; - resources.logger.logCallbackTime(callbackBreakdown, numActiveVoices, numFrames); + resources.logger.logCallbackTime(callbackBreakdown, activeVoices, numFrames); // Reset the dispatch counter dispatchDuration = Duration(0); diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index bcd79542..a0f6f09a 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -731,6 +731,7 @@ private: float sampleRate { config::defaultSampleRate }; float volume { Default::globalVolume }; int numVoices { config::numVoices }; + int activeVoices { 0 }; Oversampling oversamplingFactor { config::defaultOversamplingFactor }; // Distribution used to generate random value for the *rand opcodes From 5d9222c771a4b101480f318023ef6813a56635d4 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Sat, 18 Jul 2020 13:33:25 +0200 Subject: [PATCH 2/5] Add a readable parameter about the number of active voices --- lv2/sfizz.c | 40 +++++++++++++++++++++++++++++++++------- lv2/sfizz.ttl.in | 21 +++++++++++++++++++-- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/lv2/sfizz.c b/lv2/sfizz.c index cdb18992..8fad9716 100644 --- a/lv2/sfizz.c +++ b/lv2/sfizz.c @@ -60,14 +60,15 @@ #define SFIZZ_URI "http://sfztools.github.io/sfizz" #define SFIZZ_PREFIX SFIZZ_URI "#" -#define SFIZZ__sfzFile "http://sfztools.github.io/sfizz:sfzfile" -#define SFIZZ__tuningfile "http://sfztools.github.io/sfizz:tuningfile" -#define SFIZZ__numVoices "http://sfztools.github.io/sfizz:numvoices" -#define SFIZZ__preloadSize "http://sfztools.github.io/sfizz:preload_size" -#define SFIZZ__oversampling "http://sfztools.github.io/sfizz:oversampling" +#define SFIZZ__sfzFile SFIZZ_URI ":" "sfzfile" +#define SFIZZ__tuningfile SFIZZ_URI ":" "tuningfile" +#define SFIZZ__numVoices SFIZZ_URI ":" "numvoices" +#define SFIZZ__activeVoices SFIZZ_URI ":" "activevoices" +#define SFIZZ__preloadSize SFIZZ_URI ":" "preload_size" +#define SFIZZ__oversampling SFIZZ_URI ":" "oversampling" // These ones are just for the worker -#define SFIZZ__logStatus "http://sfztools.github.io/sfizz:log_status" -#define SFIZZ__checkModification "http://sfztools.github.io/sfizz:check_modification" +#define SFIZZ__logStatus SFIZZ_URI ":" "log_status" +#define SFIZZ__checkModification SFIZZ_URI ":" "check_modification" #define CHANNEL_MASK 0x0F #define MIDI_CHANNEL(byte) (byte & CHANNEL_MASK) @@ -147,6 +148,7 @@ typedef struct LV2_URID sfizz_oversampling_uri; LV2_URID sfizz_log_status_uri; LV2_URID sfizz_check_modification_uri; + LV2_URID sfizz_active_voices_uri; LV2_URID time_position_uri; // Sfizz related data @@ -224,7 +226,9 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self) self->sfizz_preload_size_uri = map->map(map->handle, SFIZZ__preloadSize); self->sfizz_oversampling_uri = map->map(map->handle, SFIZZ__oversampling); self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus); + self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus); self->sfizz_check_modification_uri = map->map(map->handle, SFIZZ__checkModification); + self->sfizz_active_voices_uri = map->map(map->handle, SFIZZ__activeVoices); self->time_position_uri = map->map(map->handle, LV2_TIME__Position); } @@ -509,6 +513,23 @@ sfizz_lv2_send_file_path(sfizz_plugin_t *self, LV2_URID urid, const char *path) lv2_atom_forge_pop(&self->forge, &frame); } +static void +sfizz_lv2_send_active_voices(sfizz_plugin_t *self) +{ + LV2_Atom_Forge_Frame frame; + const int active_voices = sfizz_get_num_active_voices(self->synth); + + bool write_ok = + lv2_atom_forge_frame_time(&self->forge, 0) && + lv2_atom_forge_object(&self->forge, &frame, 0, self->patch_set_uri) && + lv2_atom_forge_key(&self->forge, self->patch_property_uri) && + lv2_atom_forge_urid(&self->forge, self->sfizz_active_voices_uri) && + lv2_atom_forge_key(&self->forge, self->patch_value_uri) && + lv2_atom_forge_int(&self->forge, active_voices); + + if (write_ok) + lv2_atom_forge_pop(&self->forge, &frame); +} static void sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, const LV2_Atom_Object *obj) @@ -770,6 +791,10 @@ run(LV2_Handle instance, uint32_t sample_count) { sfizz_lv2_send_file_path(self, self->sfizz_scala_file_uri, self->scala_file_path); } + else if (property->body == self->sfizz_active_voices_uri) + { + // We're sending it anyway, nothing to do + } } else if (obj->body.otype == self->time_position_uri) { @@ -802,6 +827,7 @@ run(LV2_Handle instance, uint32_t sample_count) sfizz_lv2_check_preload_size(self); sfizz_lv2_check_oversampling(self); sfizz_lv2_check_num_voices(self); + sfizz_lv2_send_active_voices(self); // Log the buffer usage self->sample_counter += (int)sample_count; diff --git a/lv2/sfizz.ttl.in b/lv2/sfizz.ttl.in index 6c661987..e2e835fa 100644 --- a/lv2/sfizz.ttl.in +++ b/lv2/sfizz.ttl.in @@ -35,6 +35,12 @@ midnam:update a lv2:Feature . "Accordage"@fr , "Accordatura"@it . +<@LV2PLUGIN_URI@#status> + a pg:Group ; + lv2:symbol "status" ; + lv2:name "status", + "Status"@fr . + <@LV2PLUGIN_URI@:sfzfile> a lv2:Parameter ; rdfs:label "SFZ file", @@ -50,6 +56,16 @@ midnam:update a lv2:Feature . "File Scala"@it ; rdfs:range atom:Path . +<@LV2PLUGIN_URI@:activevoices> + a lv2:Parameter ; + pg:group <@LV2PLUGIN_URI@#status> ; + rdfs:label "Active voices", + "Voix utilisées"@fr ; + rdfs:range atom:Int ; + lv2:minimum 0 ; + lv2:maximum 256 + . + <@LV2PLUGIN_URI@> a doap:Project, lv2:Plugin, lv2:InstrumentPlugin ; @@ -77,8 +93,9 @@ midnam:update a lv2:Feature . opts:supportedOption param:sampleRate ; opts:supportedOption bufsize:maxBlockLength, bufsize:nominalBlockLength ; - patch:writable <@LV2PLUGIN_URI@:sfzfile> ; - patch:writable <@LV2PLUGIN_URI@:tuningfile> ; + patch:writable <@LV2PLUGIN_URI@:sfzfile> , + <@LV2PLUGIN_URI@:tuningfile> ; + patch:readable <@LV2PLUGIN_URI@:activevoices> ; lv2:port [ a lv2:InputPort, atom:AtomPort ; From c2716c3beab0440395bdd9fa6dcb4d9d8bd231aa Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Sat, 18 Jul 2020 18:20:56 +0200 Subject: [PATCH 3/5] getNumActiveVoices takes a boolean flag to recompute the number of active voices All tests updated as this was the previous default behavior. --- src/sfizz/Synth.cpp | 13 ++++++++-- src/sfizz/Synth.h | 2 +- tests/FilesT.cpp | 16 ++++++------- tests/PolyphonyT.cpp | 26 ++++++++++---------- tests/SynthT.cpp | 56 ++++++++++++++++++++++---------------------- 5 files changed, 61 insertions(+), 52 deletions(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index caf410b1..9a62067a 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -625,9 +625,18 @@ sfz::Voice* sfz::Synth::findFreeVoice() noexcept return {}; } -int sfz::Synth::getNumActiveVoices() const noexcept +int sfz::Synth::getNumActiveVoices(bool recompute) const noexcept { - return activeVoices; + if (!recompute) + return activeVoices; + + int active { 0 }; + for (auto& voice: voices) { + if (!voice->isFree()) + active++; + } + + return active; } void sfz::Synth::garbageCollect() noexcept diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index a0f6f09a..d7f481af 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -396,7 +396,7 @@ public: * * @return int */ - int getNumActiveVoices() const noexcept; + int getNumActiveVoices(bool recompute = false) const noexcept; /** * @brief Get the total number of voices in the synth (the polyphony) * diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 8d85b23a..73ea4ab0 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -474,7 +474,7 @@ TEST_CASE("[Files] Off by with different delays") synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_by.sfz"); REQUIRE( synth.getNumRegions() == 4 ); synth.noteOn(0, 63, 63); - REQUIRE( synth.getNumActiveVoices() == 1 ); + REQUIRE( synth.getNumActiveVoices(true) == 1 ); auto group1Voice = synth.getVoiceView(0); REQUIRE( group1Voice->getRegion()->group == 1ul ); REQUIRE( group1Voice->getRegion()->offBy == 2ul ); @@ -490,7 +490,7 @@ TEST_CASE("[Files] Off by with the same delays") synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_by.sfz"); REQUIRE( synth.getNumRegions() == 4 ); synth.noteOn(0, 63, 63); - REQUIRE( synth.getNumActiveVoices() == 1 ); + REQUIRE( synth.getNumActiveVoices(true) == 1 ); auto group1Voice = synth.getVoiceView(0); REQUIRE( group1Voice->getRegion()->group == 1ul ); REQUIRE( group1Voice->getRegion()->offBy == 2ul ); @@ -505,14 +505,14 @@ TEST_CASE("[Files] Off by with the same notes at the same time") synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_by.sfz"); REQUIRE( synth.getNumRegions() == 4 ); synth.noteOn(0, 65, 63); - REQUIRE( synth.getNumActiveVoices() == 2 ); + REQUIRE( synth.getNumActiveVoices(true) == 2 ); synth.noteOn(0, 65, 63); - REQUIRE( synth.getNumActiveVoices() == 4 ); + REQUIRE( synth.getNumActiveVoices(true) == 4 ); AudioBuffer buffer { 2, 256 }; synth.renderBlock(buffer); synth.noteOn(0, 65, 63); synth.renderBlock(buffer); - REQUIRE( synth.getNumActiveVoices() == 2 ); + REQUIRE( synth.getNumActiveVoices(true) == 2 ); } TEST_CASE("[Files] Off modes") @@ -522,7 +522,7 @@ TEST_CASE("[Files] Off modes") synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_mode.sfz"); REQUIRE( synth.getNumRegions() == 3 ); synth.noteOn(0, 64, 63); - REQUIRE( synth.getNumActiveVoices() == 2 ); + REQUIRE( synth.getNumActiveVoices(true) == 2 ); const auto* fastVoice = synth.getVoiceView(0)->getRegion()->offMode == SfzOffMode::fast ? synth.getVoiceView(0) : @@ -532,10 +532,10 @@ TEST_CASE("[Files] Off modes") synth.getVoiceView(1) : synth.getVoiceView(0) ; synth.noteOn(100, 63, 63); - REQUIRE( synth.getNumActiveVoices() == 3 ); + REQUIRE( synth.getNumActiveVoices(true) == 3 ); AudioBuffer buffer { 2, 256 }; synth.renderBlock(buffer); - REQUIRE( synth.getNumActiveVoices() == 2 ); + REQUIRE( synth.getNumActiveVoices(true) == 2 ); REQUIRE( fastVoice->isFree() ); REQUIRE( !normalVoice->isFree() ); } diff --git a/tests/PolyphonyT.cpp b/tests/PolyphonyT.cpp index 23716cab..aeac0ebe 100644 --- a/tests/PolyphonyT.cpp +++ b/tests/PolyphonyT.cpp @@ -78,7 +78,7 @@ TEST_CASE("[Polyphony] group polyphony limits") synth.noteOn(0, 65, 64); synth.noteOn(0, 65, 64); synth.noteOn(0, 65, 64); - REQUIRE(synth.getNumActiveVoices() == 2); // group polyphony should block the last note + REQUIRE(synth.getNumActiveVoices(true) == 2); // group polyphony should block the last note } TEST_CASE("[Polyphony] Hierarchy polyphony limits") @@ -91,7 +91,7 @@ TEST_CASE("[Polyphony] Hierarchy polyphony limits") synth.noteOn(0, 65, 64); synth.noteOn(0, 65, 64); synth.noteOn(0, 65, 64); - REQUIRE(synth.getNumActiveVoices() == 2); + REQUIRE(synth.getNumActiveVoices(true) == 2); } TEST_CASE("[Polyphony] Hierarchy polyphony limits (group)") @@ -104,7 +104,7 @@ TEST_CASE("[Polyphony] Hierarchy polyphony limits (group)") synth.noteOn(0, 65, 64); synth.noteOn(0, 65, 64); synth.noteOn(0, 65, 64); - REQUIRE(synth.getNumActiveVoices() == 2); + REQUIRE(synth.getNumActiveVoices(true) == 2); } TEST_CASE("[Polyphony] Hierarchy polyphony limits (master)") @@ -118,7 +118,7 @@ TEST_CASE("[Polyphony] Hierarchy polyphony limits (master)") synth.noteOn(0, 65, 64); synth.noteOn(0, 65, 64); synth.noteOn(0, 65, 64); - REQUIRE(synth.getNumActiveVoices() == 2); + REQUIRE(synth.getNumActiveVoices(true) == 2); } TEST_CASE("[Polyphony] Hierarchy polyphony limits (limit in another master)") @@ -137,7 +137,7 @@ TEST_CASE("[Polyphony] Hierarchy polyphony limits (limit in another master)") synth.noteOn(0, 66, 64); synth.noteOn(0, 66, 64); synth.noteOn(0, 66, 64); - REQUIRE(synth.getNumActiveVoices() == 5); + REQUIRE(synth.getNumActiveVoices(true) == 5); } TEST_CASE("[Polyphony] Hierarchy polyphony limits (global)") @@ -151,7 +151,7 @@ TEST_CASE("[Polyphony] Hierarchy polyphony limits (global)") synth.noteOn(0, 65, 64); synth.noteOn(0, 65, 64); synth.noteOn(0, 65, 64); - REQUIRE(synth.getNumActiveVoices() == 2); + REQUIRE(synth.getNumActiveVoices(true) == 2); } TEST_CASE("[Polyphony] Polyphony in master") @@ -171,21 +171,21 @@ TEST_CASE("[Polyphony] Polyphony in master") synth.noteOn(0, 65, 64); synth.noteOn(0, 65, 64); synth.noteOn(0, 65, 64); - REQUIRE(synth.getNumActiveVoices() == 2); // group polyphony should block the last note + REQUIRE(synth.getNumActiveVoices(true) == 2); // group polyphony should block the last note synth.allSoundOff(); synth.renderBlock(buffer); - REQUIRE(synth.getNumActiveVoices() == 0); + REQUIRE(synth.getNumActiveVoices(true) == 0); synth.noteOn(0, 63, 64); synth.noteOn(0, 63, 64); synth.noteOn(0, 63, 64); - REQUIRE(synth.getNumActiveVoices() == 2); // group polyphony should block the last note + REQUIRE(synth.getNumActiveVoices(true) == 2); // group polyphony should block the last note synth.allSoundOff(); synth.renderBlock(buffer); - REQUIRE(synth.getNumActiveVoices() == 0); + REQUIRE(synth.getNumActiveVoices(true) == 0); synth.noteOn(0, 61, 64); synth.noteOn(0, 61, 64); synth.noteOn(0, 61, 64); - REQUIRE(synth.getNumActiveVoices() == 3); + REQUIRE(synth.getNumActiveVoices(true) == 3); } @@ -198,7 +198,7 @@ TEST_CASE("[Polyphony] Self-masking") synth.noteOn(0, 64, 63); synth.noteOn(0, 64, 62); synth.noteOn(0, 64, 64); - REQUIRE(synth.getNumActiveVoices() == 3); // One of these is releasing + REQUIRE(synth.getNumActiveVoices(true) == 3); // One of these is releasing REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 63_norm); REQUIRE(!synth.getVoiceView(0)->releasedOrFree()); REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 62_norm); @@ -216,7 +216,7 @@ TEST_CASE("[Polyphony] Not self-masking") synth.noteOn(0, 66, 63); synth.noteOn(0, 66, 62); synth.noteOn(0, 66, 64); - REQUIRE(synth.getNumActiveVoices() == 3); // One of these is releasing + REQUIRE(synth.getNumActiveVoices(true) == 3); // One of these is releasing REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 63_norm); REQUIRE(synth.getVoiceView(0)->releasedOrFree()); // The first encountered voice is the masking candidate REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 62_norm); diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index bd6b847b..44a3faaa 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -23,11 +23,11 @@ TEST_CASE("[Synth] Play and check active voices") synth.noteOn(0, 36, 24); synth.noteOn(0, 36, 89); - REQUIRE(synth.getNumActiveVoices() == 2); + REQUIRE(synth.getNumActiveVoices(true) == 2); // Render for a while for (int i = 0; i < 200; ++i) synth.renderBlock(buffer); - REQUIRE(synth.getNumActiveVoices() == 0); + REQUIRE(synth.getNumActiveVoices(true) == 0); } TEST_CASE("[Synth] All sound off") @@ -36,9 +36,9 @@ TEST_CASE("[Synth] All sound off") synth.loadSfzFile(fs::current_path() / "tests/TestFiles/groups_avl.sfz"); synth.noteOn(0, 36, 24); synth.noteOn(0, 36, 89); - REQUIRE(synth.getNumActiveVoices() == 2); + REQUIRE(synth.getNumActiveVoices(true) == 2); synth.allSoundOff(); - REQUIRE(synth.getNumActiveVoices() == 0); + REQUIRE(synth.getNumActiveVoices(true) == 0); } TEST_CASE("[Synth] Change the number of voice while playing") @@ -51,9 +51,9 @@ TEST_CASE("[Synth] Change the number of voice while playing") synth.noteOn(0, 36, 24); synth.noteOn(0, 36, 89); synth.renderBlock(buffer); - REQUIRE(synth.getNumActiveVoices() == 2); + REQUIRE(synth.getNumActiveVoices(true) == 2); synth.setNumVoices(8); - REQUIRE(synth.getNumActiveVoices() == 0); + REQUIRE(synth.getNumActiveVoices(true) == 0); REQUIRE(synth.getNumVoices() == 8); } @@ -131,15 +131,15 @@ TEST_CASE("[Synth] All notes offs/all sounds off") )"); synth.noteOn(0, 60, 63); synth.noteOn(0, 62, 63); - REQUIRE( synth.getNumActiveVoices() == 2 ); + REQUIRE( synth.getNumActiveVoices(true) == 2 ); synth.cc(0, 120, 63); - REQUIRE( synth.getNumActiveVoices() == 0 ); + REQUIRE( synth.getNumActiveVoices(true) == 0 ); synth.noteOn(0, 62, 63); synth.noteOn(0, 60, 63); - REQUIRE( synth.getNumActiveVoices() == 2 ); + REQUIRE( synth.getNumActiveVoices(true) == 2 ); synth.cc(0, 123, 63); - REQUIRE( synth.getNumActiveVoices() == 0 ); + REQUIRE( synth.getNumActiveVoices(true) == 0 ); } TEST_CASE("[Synth] Reset all controllers") @@ -491,14 +491,14 @@ TEST_CASE("[Synth] sample quality") // default sample quality synth.noteOn(0, 60, 100); - REQUIRE(synth.getNumActiveVoices() == 1); + REQUIRE(synth.getNumActiveVoices(true) == 1); REQUIRE(synth.getVoiceView(0)->getCurrentSampleQuality() == sfz::Default::sampleQuality); synth.allSoundOff(); // default sample quality, freewheeling synth.enableFreeWheeling(); synth.noteOn(0, 60, 100); - REQUIRE(synth.getNumActiveVoices() == 1); + REQUIRE(synth.getNumActiveVoices(true) == 1); REQUIRE(synth.getVoiceView(0)->getCurrentSampleQuality() == sfz::Default::sampleQualityInFreewheelingMode); synth.allSoundOff(); synth.disableFreeWheeling(); @@ -506,7 +506,7 @@ TEST_CASE("[Synth] sample quality") // user-defined sample quality synth.setSampleQuality(sfz::Synth::ProcessLive, 3); synth.noteOn(0, 60, 100); - REQUIRE(synth.getNumActiveVoices() == 1); + REQUIRE(synth.getNumActiveVoices(true) == 1); REQUIRE(synth.getVoiceView(0)->getCurrentSampleQuality() == 3); synth.allSoundOff(); @@ -514,21 +514,21 @@ TEST_CASE("[Synth] sample quality") synth.enableFreeWheeling(); synth.setSampleQuality(sfz::Synth::ProcessFreewheeling, 8); synth.noteOn(0, 60, 100); - REQUIRE(synth.getNumActiveVoices() == 1); + REQUIRE(synth.getNumActiveVoices(true) == 1); REQUIRE(synth.getVoiceView(0)->getCurrentSampleQuality() == 8); synth.allSoundOff(); synth.disableFreeWheeling(); // region sample quality synth.noteOn(0, 61, 100); - REQUIRE(synth.getNumActiveVoices() == 1); + REQUIRE(synth.getNumActiveVoices(true) == 1); REQUIRE(synth.getVoiceView(0)->getCurrentSampleQuality() == 5); synth.allSoundOff(); // region sample quality, freewheeling synth.enableFreeWheeling(); synth.noteOn(0, 61, 100); - REQUIRE(synth.getNumActiveVoices() == 1); + REQUIRE(synth.getNumActiveVoices(true) == 1); REQUIRE(synth.getVoiceView(0)->getCurrentSampleQuality() == 5); synth.allSoundOff(); synth.disableFreeWheeling(); @@ -551,7 +551,7 @@ TEST_CASE("[Synth] Sister voices") REQUIRE( synth.getVoiceView(0)->getNextSisterVoice() == synth.getVoiceView(0) ); REQUIRE( synth.getVoiceView(0)->getPreviousSisterVoice() == synth.getVoiceView(0) ); synth.noteOn(0, 62, 85); - REQUIRE( synth.getNumActiveVoices() == 3 ); + REQUIRE( synth.getNumActiveVoices(true) == 3 ); REQUIRE( sfz::SisterVoiceRing::countSisterVoices(synth.getVoiceView(1)) == 2 ); REQUIRE( synth.getVoiceView(1)->getNextSisterVoice() == synth.getVoiceView(2) ); REQUIRE( synth.getVoiceView(1)->getPreviousSisterVoice() == synth.getVoiceView(2) ); @@ -559,7 +559,7 @@ TEST_CASE("[Synth] Sister voices") REQUIRE( synth.getVoiceView(2)->getNextSisterVoice() == synth.getVoiceView(1) ); REQUIRE( synth.getVoiceView(2)->getPreviousSisterVoice() == synth.getVoiceView(1) ); synth.noteOn(0, 63, 85); - REQUIRE( synth.getNumActiveVoices() == 6 ); + REQUIRE( synth.getNumActiveVoices(true) == 6 ); REQUIRE( sfz::SisterVoiceRing::countSisterVoices(synth.getVoiceView(3)) == 3 ); REQUIRE( synth.getVoiceView(3)->getNextSisterVoice() == synth.getVoiceView(4) ); REQUIRE( synth.getVoiceView(3)->getPreviousSisterVoice() == synth.getVoiceView(5) ); @@ -599,14 +599,14 @@ TEST_CASE("[Synth] Sisters and off-by") group=2 key=63 sample=*saw )"); synth.noteOn(0, 62, 85); - REQUIRE( synth.getNumActiveVoices() == 2 ); + REQUIRE( synth.getNumActiveVoices(true) == 2 ); REQUIRE( sfz::SisterVoiceRing::countSisterVoices(synth.getVoiceView(0)) == 2 ); synth.renderBlock(buffer); - REQUIRE( synth.getNumActiveVoices() == 2 ); + REQUIRE( synth.getNumActiveVoices(true) == 2 ); synth.noteOn(0, 63, 85); - REQUIRE( synth.getNumActiveVoices() == 3 ); + REQUIRE( synth.getNumActiveVoices(true) == 3 ); synth.renderBlock(buffer); - REQUIRE( synth.getNumActiveVoices() == 2 ); + REQUIRE( synth.getNumActiveVoices(true) == 2 ); REQUIRE( sfz::SisterVoiceRing::countSisterVoices(synth.getVoiceView(0)) == 1 ); } @@ -619,7 +619,7 @@ TEST_CASE("[Synth] Release key") synth.noteOn(0, 62, 85); synth.cc(0, 64, 127); synth.noteOff(0, 62, 85); - REQUIRE( synth.getNumActiveVoices() == 1 ); + REQUIRE( synth.getNumActiveVoices(true) == 1 ); } TEST_CASE("[Synth] Release") @@ -631,9 +631,9 @@ TEST_CASE("[Synth] Release") synth.noteOn(0, 62, 85); synth.cc(0, 64, 127); synth.noteOff(0, 62, 85); - REQUIRE( synth.getNumActiveVoices() == 0 ); + REQUIRE( synth.getNumActiveVoices(true) == 0 ); synth.cc(0, 64, 0); - REQUIRE( synth.getNumActiveVoices() == 1 ); + REQUIRE( synth.getNumActiveVoices(true) == 1 ); } TEST_CASE("[Synth] Release key (Different sustain CC)") @@ -646,7 +646,7 @@ TEST_CASE("[Synth] Release key (Different sustain CC)") synth.noteOn(0, 62, 85); synth.cc(0, 54, 127); synth.noteOff(0, 62, 85); - REQUIRE( synth.getNumActiveVoices() == 1 ); + REQUIRE( synth.getNumActiveVoices(true) == 1 ); } TEST_CASE("[Synth] Release (Different sustain CC)") @@ -659,9 +659,9 @@ TEST_CASE("[Synth] Release (Different sustain CC)") synth.noteOn(0, 62, 85); synth.cc(0, 54, 127); synth.noteOff(0, 62, 85); - REQUIRE( synth.getNumActiveVoices() == 0 ); + REQUIRE( synth.getNumActiveVoices(true) == 0 ); synth.cc(0, 54, 0); - REQUIRE( synth.getNumActiveVoices() == 1 ); + REQUIRE( synth.getNumActiveVoices(true) == 1 ); } TEST_CASE("[Synth] Sustain threshold default") From aff0fedb387086a71d6bf587165fa5638b7773bf Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 26 Jul 2020 13:50:35 +0200 Subject: [PATCH 4/5] Readable LV2 active voices with control port --- lv2/sfizz.c | 31 ++++++------------------------- lv2/sfizz.ttl.in | 22 +++++++++++----------- 2 files changed, 17 insertions(+), 36 deletions(-) diff --git a/lv2/sfizz.c b/lv2/sfizz.c index 8fad9716..5bafd04b 100644 --- a/lv2/sfizz.c +++ b/lv2/sfizz.c @@ -63,7 +63,6 @@ #define SFIZZ__sfzFile SFIZZ_URI ":" "sfzfile" #define SFIZZ__tuningfile SFIZZ_URI ":" "tuningfile" #define SFIZZ__numVoices SFIZZ_URI ":" "numvoices" -#define SFIZZ__activeVoices SFIZZ_URI ":" "activevoices" #define SFIZZ__preloadSize SFIZZ_URI ":" "preload_size" #define SFIZZ__oversampling SFIZZ_URI ":" "oversampling" // These ones are just for the worker @@ -115,6 +114,7 @@ typedef struct const float *scala_root_key_port; const float *tuning_frequency_port; const float *stretch_tuning_port; + float *active_voices_port; // Atom forge LV2_Atom_Forge forge; ///< Forge for writing atoms in run thread @@ -184,6 +184,7 @@ enum SFIZZ_SCALA_ROOT_KEY = 9, SFIZZ_TUNING_FREQUENCY = 10, SFIZZ_STRETCH_TUNING = 11, + SFIZZ_ACTIVE_VOICES = 12, }; static void @@ -228,7 +229,6 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self) self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus); self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus); self->sfizz_check_modification_uri = map->map(map->handle, SFIZZ__checkModification); - self->sfizz_active_voices_uri = map->map(map->handle, SFIZZ__activeVoices); self->time_position_uri = map->map(map->handle, LV2_TIME__Position); } @@ -276,6 +276,9 @@ connect_port(LV2_Handle instance, case SFIZZ_STRETCH_TUNING: self->stretch_tuning_port = (const float *)data; break; + case SFIZZ_ACTIVE_VOICES: + self->active_voices_port = (float *)data; + break; default: break; } @@ -513,24 +516,6 @@ sfizz_lv2_send_file_path(sfizz_plugin_t *self, LV2_URID urid, const char *path) lv2_atom_forge_pop(&self->forge, &frame); } -static void -sfizz_lv2_send_active_voices(sfizz_plugin_t *self) -{ - LV2_Atom_Forge_Frame frame; - const int active_voices = sfizz_get_num_active_voices(self->synth); - - bool write_ok = - lv2_atom_forge_frame_time(&self->forge, 0) && - lv2_atom_forge_object(&self->forge, &frame, 0, self->patch_set_uri) && - lv2_atom_forge_key(&self->forge, self->patch_property_uri) && - lv2_atom_forge_urid(&self->forge, self->sfizz_active_voices_uri) && - lv2_atom_forge_key(&self->forge, self->patch_value_uri) && - lv2_atom_forge_int(&self->forge, active_voices); - - if (write_ok) - lv2_atom_forge_pop(&self->forge, &frame); -} - static void sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, const LV2_Atom_Object *obj) { @@ -791,10 +776,6 @@ run(LV2_Handle instance, uint32_t sample_count) { sfizz_lv2_send_file_path(self, self->sfizz_scala_file_uri, self->scala_file_path); } - else if (property->body == self->sfizz_active_voices_uri) - { - // We're sending it anyway, nothing to do - } } else if (obj->body.otype == self->time_position_uri) { @@ -827,7 +808,7 @@ run(LV2_Handle instance, uint32_t sample_count) sfizz_lv2_check_preload_size(self); sfizz_lv2_check_oversampling(self); sfizz_lv2_check_num_voices(self); - sfizz_lv2_send_active_voices(self); + *(self->active_voices_port) = sfizz_get_num_active_voices(self->synth); // Log the buffer usage self->sample_counter += (int)sample_count; diff --git a/lv2/sfizz.ttl.in b/lv2/sfizz.ttl.in index e2e835fa..582c4a53 100644 --- a/lv2/sfizz.ttl.in +++ b/lv2/sfizz.ttl.in @@ -56,16 +56,6 @@ midnam:update a lv2:Feature . "File Scala"@it ; rdfs:range atom:Path . -<@LV2PLUGIN_URI@:activevoices> - a lv2:Parameter ; - pg:group <@LV2PLUGIN_URI@#status> ; - rdfs:label "Active voices", - "Voix utilisées"@fr ; - rdfs:range atom:Int ; - lv2:minimum 0 ; - lv2:maximum 256 - . - <@LV2PLUGIN_URI@> a doap:Project, lv2:Plugin, lv2:InstrumentPlugin ; @@ -95,7 +85,6 @@ midnam:update a lv2:Feature . patch:writable <@LV2PLUGIN_URI@:sfzfile> , <@LV2PLUGIN_URI@:tuningfile> ; - patch:readable <@LV2PLUGIN_URI@:activevoices> ; lv2:port [ a lv2:InputPort, atom:AtomPort ; @@ -308,4 +297,15 @@ midnam:update a lv2:Feature . lv2:minimum 0.0 ; lv2:maximum 1.0 ; units:unit units:coef + ] , [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:index 12 ; + lv2:symbol "active_voices" ; + lv2:name "Active voices", + "Voix utilisées"@fr ; + pg:group <@LV2PLUGIN_URI@#status> ; + lv2:portProperty lv2:integer ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 256 ; ] . From ab6086b6c53e4fc2e084be0470b9ca3f7002978a Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 26 Jul 2020 14:08:10 +0200 Subject: [PATCH 5/5] Rebase on develop and fix sustain tests --- tests/SynthT.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 44a3faaa..ccd92b94 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -673,7 +673,7 @@ TEST_CASE("[Synth] Sustain threshold default") synth.noteOn(0, 62, 85); synth.cc(0, 64, 1); synth.noteOff(0, 62, 85); - REQUIRE( synth.getNumActiveVoices() == 0 ); + REQUIRE( synth.getNumActiveVoices(true) == 0 ); } TEST_CASE("[Synth] Sustain threshold") @@ -686,12 +686,12 @@ TEST_CASE("[Synth] Sustain threshold") synth.noteOn(0, 62, 85); synth.cc(0, 64, 1); synth.noteOff(0, 62, 85); - REQUIRE( synth.getNumActiveVoices() == 1 ); + REQUIRE( synth.getNumActiveVoices(true) == 1 ); synth.noteOn(0, 62, 85); synth.noteOff(0, 62, 85); - REQUIRE( synth.getNumActiveVoices() == 2 ); + REQUIRE( synth.getNumActiveVoices(true) == 2 ); synth.noteOn(0, 62, 85); synth.cc(0, 64, 64); synth.noteOff(0, 62, 85); - REQUIRE( synth.getNumActiveVoices() == 2 ); + REQUIRE( synth.getNumActiveVoices(true) == 2 ); }