Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Readable active voices #321

Merged
merged 5 commits into from
Jul 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions lv2/sfizz.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,14 @@

#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__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)
Expand Down Expand Up @@ -114,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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -182,6 +184,7 @@ enum
SFIZZ_SCALA_ROOT_KEY = 9,
SFIZZ_TUNING_FREQUENCY = 10,
SFIZZ_STRETCH_TUNING = 11,
SFIZZ_ACTIVE_VOICES = 12,
};

static void
Expand Down Expand Up @@ -224,6 +227,7 @@ 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->time_position_uri = map->map(map->handle, LV2_TIME__Position);
}
Expand Down Expand Up @@ -272,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;
}
Expand Down Expand Up @@ -509,7 +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_handle_atom_object(sfizz_plugin_t *self, const LV2_Atom_Object *obj)
{
Expand Down Expand Up @@ -802,6 +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);
*(self->active_voices_port) = sfizz_get_num_active_voices(self->synth);

// Log the buffer usage
self->sample_counter += (int)sample_count;
Expand Down
21 changes: 19 additions & 2 deletions lv2/sfizz.ttl.in
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -77,8 +83,8 @@ 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> ;

lv2:port [
a lv2:InputPort, atom:AtomPort ;
Expand Down Expand Up @@ -291,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 ;
] .
20 changes: 12 additions & 8 deletions src/sfizz/Synth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -625,14 +625,18 @@ sfz::Voice* sfz::Synth::findFreeVoice() noexcept
return {};
}

int sfz::Synth::getNumActiveVoices() const noexcept
int sfz::Synth::getNumActiveVoices(bool recompute) const noexcept
{
auto activeVoices = 0;
for (const auto& voice : voices) {
if (!recompute)
return activeVoices;

int active { 0 };
for (auto& voice: voices) {
if (!voice->isFree())
activeVoices++;
active++;
}
return activeVoices;

return active;
}

void sfz::Synth::garbageCollect() noexcept
Expand Down Expand Up @@ -711,7 +715,7 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
return;
}

int numActiveVoices { 0 };
activeVoices = 0;
{ // Main render block
ScopedTiming logger { callbackBreakdown.renderMethod, ScopedTiming::Operation::addToDuration };
tempSpan->fill(0.0f);
Expand All @@ -730,7 +734,7 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
if (voice->isFree())
continue;

numActiveVoices++;
activeVoices++;
renderVoiceToOutputs(*voice, *tempSpan);
callbackBreakdown.data += voice->getLastDataDuration();
callbackBreakdown.amplitude += voice->getLastAmplitudeDuration();
Expand Down Expand Up @@ -770,7 +774,7 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
}

callbackBreakdown.dispatch = dispatchDuration;
resources.logger.logCallbackTime(callbackBreakdown, numActiveVoices, numFrames);
resources.logger.logCallbackTime(callbackBreakdown, activeVoices, numFrames);

// Reset the dispatch counter
dispatchDuration = Duration(0);
Expand Down
3 changes: 2 additions & 1 deletion src/sfizz/Synth.h
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ class Synth final : public Voice::StateListener, public Parser::Listener {
*
* @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)
*
Expand Down Expand Up @@ -731,6 +731,7 @@ class Synth final : public Voice::StateListener, public Parser::Listener {
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
Expand Down
16 changes: 8 additions & 8 deletions tests/FilesT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand All @@ -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 );
Expand All @@ -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<float> 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")
Expand All @@ -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) :
Expand All @@ -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<float> buffer { 2, 256 };
synth.renderBlock(buffer);
REQUIRE( synth.getNumActiveVoices() == 2 );
REQUIRE( synth.getNumActiveVoices(true) == 2 );
REQUIRE( fastVoice->isFree() );
REQUIRE( !normalVoice->isFree() );
}
Expand Down
26 changes: 13 additions & 13 deletions tests/PolyphonyT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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)")
Expand All @@ -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)")
Expand All @@ -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)")
Expand All @@ -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)")
Expand All @@ -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")
Expand All @@ -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);
}


Expand All @@ -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);
Expand All @@ -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);
Expand Down
Loading