From 6713640b227997e454f08a935d3617dba82e5bbc Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Mon, 2 Dec 2019 00:10:21 +0100 Subject: [PATCH] Added the LV2 parameters related to the preloaded size and the oversampling --- lv2/sfizz.c | 137 +++++++++++++++++++++++++++++++++--- lv2/sfizz.ttl.in | 47 ++++++++++++- src/sfizz.h | 5 +- src/sfizz/sfizz_wrapper.cpp | 22 +++++- tests/SynthT.cpp | 31 ++++++++ 5 files changed, 228 insertions(+), 14 deletions(-) diff --git a/lv2/sfizz.c b/lv2/sfizz.c index 5856be0f..f78794ae 100644 --- a/lv2/sfizz.c +++ b/lv2/sfizz.c @@ -51,6 +51,8 @@ #define SFIZZ_PREFIX SFIZZ_URI "#" #define SFIZZ__sfzFile "http://sfztools.github.io/sfizz:sfzfile" #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 CHANNEL_MASK 0x0F #define MIDI_CHANNEL(byte) (byte & CHANNEL_MASK) @@ -59,6 +61,8 @@ #define MAX_PATH_SIZE 1024 #define MAX_VOICES 256 #define DEFAULT_VOICES 64 +#define DEFAULT_OVERSAMPLING SFIZZ_OVERSAMPLING_X1 +#define DEFAULT_PRELOAD 8192 #define UNUSED(x) (void)(x) typedef struct @@ -75,6 +79,8 @@ typedef struct float *output_buffers[2]; const float *volume_port; const float *polyphony_port; + const float *oversampling_port; + const float *preload_port; // Atom forge LV2_Atom_Forge forge; ///< Forge for writing atoms in run thread @@ -103,13 +109,17 @@ typedef struct LV2_URID state_changed_uri; LV2_URID sfizz_sfz_file_uri; LV2_URID sfizz_num_voices_uri; + LV2_URID sfizz_preload_size_uri; + LV2_URID sfizz_oversampling_uri; // Sfizz related data sfizz_synth_t *synth; bool expect_nominal_block_length; char sfz_file_path[MAX_PATH_SIZE]; int num_voices; - bool changing_voices; + unsigned int preload_size; + sfizz_oversampling_factor_t oversampling; + bool changing_state; int max_block_size; float sample_rate; } sfizz_plugin_t; @@ -121,7 +131,9 @@ enum SFIZZ_LEFT = 2, SFIZZ_RIGHT = 3, SFIZZ_VOLUME = 4, - SFIZZ_POLYPHONY = 5 + SFIZZ_POLYPHONY = 5, + SFIZZ_OVERSAMPLING = 6, + SFIZZ_PRELOAD = 7 }; static void @@ -146,6 +158,8 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self) self->state_changed_uri = map->map(map->handle, LV2_STATE__StateChanged); self->sfizz_sfz_file_uri = map->map(map->handle, SFIZZ__sfzFile); self->sfizz_num_voices_uri = map->map(map->handle, SFIZZ__numVoices); + self->sfizz_preload_size_uri = map->map(map->handle, SFIZZ__preloadSize); + self->sfizz_oversampling_uri = map->map(map->handle, SFIZZ__oversampling); } static void @@ -174,6 +188,12 @@ connect_port(LV2_Handle instance, case SFIZZ_POLYPHONY: self->polyphony_port = (const float *)data; break; + case SFIZZ_OVERSAMPLING: + self->oversampling_port = (const float *)data; + break; + case SFIZZ_PRELOAD: + self->preload_port = (const float *)data; + break; default: break; } @@ -203,7 +223,9 @@ instantiate(const LV2_Descriptor *descriptor, self->expect_nominal_block_length = false; self->sfz_file_path[0] = '\0'; self->num_voices = DEFAULT_VOICES; - self->changing_voices = false; + self->oversampling = DEFAULT_OVERSAMPLING; + self->preload_size = DEFAULT_PRELOAD; + self->changing_state = false; // Get the features from the host and populate the structure for (const LV2_Feature *const *f = features; *f; f++) @@ -509,8 +531,7 @@ run(LV2_Handle instance, uint32_t sample_count) sfizz_set_volume(self->synth, volume); int num_voices = (int)*self->polyphony_port; - if (num_voices != self->num_voices && !self->changing_voices) - { + if (num_voices != self->num_voices && !self->changing_state) { lv2_log_note(&self->logger, "[run] Number of voices changed to %d\n", num_voices); LV2_Atom_Int num_voices_atom; num_voices_atom.atom.type = self->sfizz_num_voices_uri; @@ -520,7 +541,37 @@ run(LV2_Handle instance, uint32_t sample_count) lv2_atom_total_size((LV2_Atom*)&num_voices_atom), &num_voices_atom) == LV2_WORKER_SUCCESS) { - self->changing_voices = true; + self->changing_state = true; + } + } + + unsigned int preload_size = (int)*self->preload_port; + if (preload_size != self->preload_size && !self->changing_state) { + lv2_log_note(&self->logger, "[run] Preload size changed to %d\n", preload_size); + LV2_Atom_Int atom; + atom.atom.type = self->sfizz_preload_size_uri; + atom.atom.size = sizeof(int); + atom.body = preload_size; + if (self->worker->schedule_work(self->worker->handle, + lv2_atom_total_size((LV2_Atom*)&atom), + &atom) == LV2_WORKER_SUCCESS) + { + self->changing_state = true; + } + } + + sfizz_oversampling_factor_t oversampling = (sfizz_oversampling_factor_t)*self->oversampling_port; + if (oversampling != self->oversampling && !self->changing_state) { + lv2_log_note(&self->logger, "[run] Oversampling size changed to %d\n", oversampling); + LV2_Atom_Int atom; + atom.atom.type = self->sfizz_oversampling_uri; + atom.atom.size = sizeof(int); + atom.body = oversampling; + if (self->worker->schedule_work(self->worker->handle, + lv2_atom_total_size((LV2_Atom*)&atom), + &atom) == LV2_WORKER_SUCCESS) + { + self->changing_state = true; } } @@ -614,6 +665,30 @@ restore(LV2_Handle instance, self->num_voices = num_voices; } } + + value = retrieve(handle, self->sfizz_preload_size_uri, &size, &type, &val_flags); + if (value) + { + unsigned int preload_size = *(const unsigned int *)value; + if (preload_size != self->preload_size) + { + lv2_log_note(&self->logger, "Restoring the preload size to %d\n", preload_size); + sfizz_set_preload_size(self->synth, preload_size); + self->preload_size = preload_size; + } + } + + value = retrieve(handle, self->sfizz_oversampling_uri, &size, &type, &val_flags); + if (value) + { + sfizz_oversampling_factor_t oversampling = *(const sfizz_oversampling_factor_t *)value; + if (oversampling != self->oversampling) + { + lv2_log_note(&self->logger, "Restoring the oversampling to %d\n", oversampling); + sfizz_set_oversampling_factor(self->synth, oversampling); + self->oversampling = oversampling; + } + } return LV2_STATE_SUCCESS; } @@ -643,6 +718,22 @@ save(LV2_Handle instance, self->atom_int_uri, LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + // Save the preload size + store(handle, + self->sfizz_preload_size_uri, + &self->preload_size, + sizeof(unsigned int), + self->atom_int_uri, + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + + // Save the preload size + store(handle, + self->sfizz_oversampling_uri, + &self->oversampling, + sizeof(int), + self->atom_int_uri, + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + return LV2_STATE_SUCCESS; } @@ -674,6 +765,19 @@ work(LV2_Handle instance, lv2_log_note(&self->logger, "[work] Changing number of voices to: %d\n", num_voices); sfizz_set_num_voices(self->synth, num_voices); } + else if (atom->type == self->sfizz_preload_size_uri) + { + const unsigned int preload_size = *(const unsigned int *)LV2_ATOM_BODY_CONST(atom); + lv2_log_note(&self->logger, "[work] Changing preload size to: %d\n", preload_size); + sfizz_set_preload_size(self->synth, preload_size); + } + else if (atom->type == self->sfizz_oversampling_uri) + { + const sfizz_oversampling_factor_t oversampling = + *(const sfizz_oversampling_factor_t *)LV2_ATOM_BODY_CONST(atom); + lv2_log_note(&self->logger, "[work] Changing oversampling to: %d\n", oversampling); + sfizz_set_oversampling_factor(self->synth, oversampling); + } else { lv2_log_error(&self->logger, "[worker] Got an unknown atom.\n"); @@ -702,18 +806,33 @@ work_response(LV2_Handle instance, const LV2_Atom *atom = (const LV2_Atom *)data; if (atom->type == self->sfizz_sfz_file_uri) - { + { // TODO: could check that everything is indeed changed here const char *sfz_file_path = LV2_ATOM_BODY_CONST(atom); strcpy(self->sfz_file_path, sfz_file_path); lv2_log_note(&self->logger, "[work_response] File changed to: %s\n", self->sfz_file_path); } else if (atom->type == self->sfizz_num_voices_uri) - { + { // TODO: could check that everything is indeed changed here const int num_voices = *(const int *)LV2_ATOM_BODY_CONST(atom); self->num_voices = num_voices; - self->changing_voices = false; + self->changing_state = false; lv2_log_note(&self->logger, "[work_response] Number of voices changed to: %d\n", self->num_voices); } + else if (atom->type == self->sfizz_preload_size_uri) + { // TODO: could check that everything is indeed changed here + const unsigned int preload_size = *(const unsigned int *)LV2_ATOM_BODY_CONST(atom); + self->preload_size = preload_size; + self->changing_state = false; + lv2_log_note(&self->logger, "[work_response] Preload size changed to: %d\n", self->preload_size); + } + else if (atom->type == self->sfizz_oversampling_uri) + { // TODO: could check that everything is indeed changed here + const sfizz_oversampling_factor_t oversampling = + *(const sfizz_oversampling_factor_t *)LV2_ATOM_BODY_CONST(atom); + self->oversampling = oversampling; + self->changing_state = false; + lv2_log_note(&self->logger, "[work_response] Oversampling changed to: %d\n", self->oversampling); + } else { lv2_log_error(&self->logger, "[work_response] Got an unknown atom.\n"); diff --git a/lv2/sfizz.ttl.in b/lv2/sfizz.ttl.in index 032d51ff..fb3a07bc 100644 --- a/lv2/sfizz.ttl.in +++ b/lv2/sfizz.ttl.in @@ -30,6 +30,16 @@ rdfs:label "Polyphony" ; rdfs:range atom:Int . +<@LV2PLUGIN_URI@:preload_size> + a lv2:Parameter ; + rdfs:label "Preload size" ; + rdfs:range atom:Int . + +<@LV2PLUGIN_URI@:oversampling> + a lv2:Parameter ; + rdfs:label "Oversampling" ; + rdfs:range atom:Int . + <@LV2PLUGIN_URI@> a doap:Project, lv2:Plugin, lv2:InstrumentPlugin ; @@ -105,4 +115,39 @@ lv2:scalePoint [ rdfs:label "64 voices"; rdf:value 64 ] ; lv2:scalePoint [ rdfs:label "128 voices"; rdf:value 128 ] ; lv2:scalePoint [ rdfs:label "256 voices"; rdf:value 256 ] ; - ] . + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 6 ; + lv2:symbol "oversampling" ; + lv2:name "Internal oversampling factor" ; + pg:group <#config> ; + lv2:portProperty pprop:notAutomatic ; + lv2:portProperty pprop:expensive ; + lv2:portProperty lv2:integer ; + lv2:portProperty lv2:enumeration ; + lv2:default 1 ; + lv2:minimum 1 ; + lv2:maximum 8 ; + lv2:scalePoint [ rdfs:label "x1 oversampling"; rdf:value 1 ] ; + lv2:scalePoint [ rdfs:label "x2 oversampling"; rdf:value 2 ] ; + lv2:scalePoint [ rdfs:label "x4 oversampling"; rdf:value 4 ] ; + lv2:scalePoint [ rdfs:label "x8 oversampling"; rdf:value 8 ] ; + ], [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 7 ; + lv2:symbol "preload_size" ; + lv2:name "Preload size" ; + pg:group <#config> ; + lv2:portProperty pprop:notAutomatic ; + lv2:portProperty pprop:expensive ; + lv2:portProperty lv2:integer ; + lv2:portProperty lv2:enumeration ; + lv2:default 8192 ; + lv2:minimum 1024 ; + lv2:maximum 16384 ; + lv2:scalePoint [ rdfs:label "4 Ko"; rdf:value 1024 ] ; + lv2:scalePoint [ rdfs:label "8 Ko"; rdf:value 2048 ] ; + lv2:scalePoint [ rdfs:label "16 Ko"; rdf:value 4096 ] ; + lv2:scalePoint [ rdfs:label "32 Ko"; rdf:value 8192 ] ; + lv2:scalePoint [ rdfs:label "64 Ko"; rdf:value 16384 ] ; + ]. diff --git a/src/sfizz.h b/src/sfizz.h index 98a647ad..6df7d7b8 100644 --- a/src/sfizz.h +++ b/src/sfizz.h @@ -39,6 +39,7 @@ typedef enum { SFIZZ_OVERSAMPLING_X4 = 4, SFIZZ_OVERSAMPLING_X8 = 8 } sfizz_oversampling_factor_t; + /** * @brief Creates a sfizz synth. This object has to be freed by the caller * using sfizz_free(). @@ -268,9 +269,9 @@ sfizz_oversampling_factor_t sfizz_get_oversampling_factor(sfizz_synth_t* synth); * @param synth The synth * @param[in] preload_size The preload size * - * @return The internal sample rate of the engine + * @return True if the oversampling factor was correct */ -void sfizz_set_oversampling_factor(sfizz_synth_t* synth, sfizz_oversampling_factor_t preload_size); +bool sfizz_set_oversampling_factor(sfizz_synth_t* synth, sfizz_oversampling_factor_t oversampling); /** * @brief Set the global instrument volume. diff --git a/src/sfizz/sfizz_wrapper.cpp b/src/sfizz/sfizz_wrapper.cpp index a123ae28..736241d8 100644 --- a/src/sfizz/sfizz_wrapper.cpp +++ b/src/sfizz/sfizz_wrapper.cpp @@ -144,10 +144,28 @@ sfizz_oversampling_factor_t sfizz_get_oversampling_factor(sfizz_synth_t* synth) auto self = reinterpret_cast(synth); return static_cast(self->getOversamplingFactor()); } -void sfizz_set_oversampling_factor(sfizz_synth_t* synth, sfizz_oversampling_factor_t preload_size) + +bool sfizz_set_oversampling_factor(sfizz_synth_t* synth, sfizz_oversampling_factor_t oversampling) { auto self = reinterpret_cast(synth); - self->setOversamplingFactor(static_cast(preload_size)); + using sfz::Oversampling; + switch(oversampling) + { + case SFIZZ_OVERSAMPLING_X1: + self->setOversamplingFactor(sfz::Oversampling::x1); + return true; + case SFIZZ_OVERSAMPLING_X2: + self->setOversamplingFactor(sfz::Oversampling::x2); + return true; + case SFIZZ_OVERSAMPLING_X4: + self->setOversamplingFactor(sfz::Oversampling::x4); + return true; + case SFIZZ_OVERSAMPLING_X8: + self->setOversamplingFactor(sfz::Oversampling::x8); + return true; + default: + return false; + } } void sfizz_set_volume(sfizz_synth_t* synth, float volume) diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 7a6d2082..f5dadec0 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -21,6 +21,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include "Config.h" #include "sfizz.hpp" #include "catch2/catch.hpp" using namespace Catch::literals; @@ -89,3 +90,33 @@ TEST_CASE("[Synth] Check that the sample per block and sample rate are actually REQUIRE( synth.getVoiceView(i)->getSampleRate() == 48000.0f ); } } + +TEST_CASE("[Synth] Check that we can change the size of the preload before and after loading") +{ + sfz::Synth synth; + synth.setPreloadSize(512); + sfz::AudioBuffer buffer { 2, blockSize }; + synth.loadSfzFile(fs::current_path() / "tests/TestFiles/groups_avl.sfz"); + synth.setPreloadSize(1024); + + synth.noteOn(0, 1, 36, 24); + synth.noteOn(0, 1, 36, 89); + synth.renderBlock(buffer); + synth.setPreloadSize(2048); + synth.renderBlock(buffer); +} + +TEST_CASE("[Synth] Check that we can change the oversampling factor before and after loading") +{ + sfz::Synth synth; + synth.setOversamplingFactor(sfz::x2); + sfz::AudioBuffer buffer { 2, blockSize }; + synth.loadSfzFile(fs::current_path() / "tests/TestFiles/groups_avl.sfz"); + synth.setOversamplingFactor(sfz::x4); + + synth.noteOn(0, 1, 36, 24); + synth.noteOn(0, 1, 36, 89); + synth.renderBlock(buffer); + synth.setOversamplingFactor(sfz::x2); + synth.renderBlock(buffer); +}