diff options
-rw-r--r-- | lv2/core.lv2/meta.ttl | 2 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/meson.build | 8 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/sampler.c | 49 |
3 files changed, 52 insertions, 7 deletions
diff --git a/lv2/core.lv2/meta.ttl b/lv2/core.lv2/meta.ttl index 8e4e471..f51da18 100644 --- a/lv2/core.lv2/meta.ttl +++ b/lv2/core.lv2/meta.ttl @@ -48,6 +48,8 @@ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH R ] , [ rdfs:label "eg-midigate: Fix output timing." ] , [ + rdfs:label "eg-sampler: Add resampling via libsamplerate." + ] , [ rdfs:label "eg-sampler: Fix potentially corrupt notification events." ] ] diff --git a/plugins/eg-sampler.lv2/meson.build b/plugins/eg-sampler.lv2/meson.build index fe9c2fd..1283a8f 100644 --- a/plugins/eg-sampler.lv2/meson.build +++ b/plugins/eg-sampler.lv2/meson.build @@ -5,16 +5,20 @@ plugin_sources = files('sampler.c') bundle_name = 'eg-sampler.lv2' data_filenames = ['manifest.ttl.in', 'sampler.ttl', 'click.wav'] +samplerate_dep = dependency('samplerate', + version: '>= 0.1.0', + required: get_option('plugins')) + sndfile_dep = dependency('sndfile', version: '>= 1.0.0', required: get_option('plugins')) -if sndfile_dep.found() +if samplerate_dep.found() and sndfile_dep.found() module = shared_library( 'sampler', plugin_sources, c_args: c_suppressions, - dependencies: [lv2_dep, m_dep, sndfile_dep], + dependencies: [lv2_dep, m_dep, samplerate_dep, sndfile_dep], gnu_symbol_visibility: 'hidden', install: true, install_dir: lv2dir / bundle_name, diff --git a/plugins/eg-sampler.lv2/sampler.c b/plugins/eg-sampler.lv2/sampler.c index a3ef5c3..c978d2b 100644 --- a/plugins/eg-sampler.lv2/sampler.c +++ b/plugins/eg-sampler.lv2/sampler.c @@ -33,6 +33,7 @@ #include "lv2/urid/urid.h" #include "lv2/worker/worker.h" +#include <samplerate.h> #include <sndfile.h> #include <math.h> @@ -80,6 +81,7 @@ typedef struct { bool activated; bool gain_changed; bool sample_changed; + int sample_rate; } Sampler; /** @@ -120,7 +122,7 @@ convert_to_mono(float* data, sf_count_t num_input_frames, uint32_t channels) not modified. */ static Sample* -load_sample(LV2_Log_Logger* logger, const char* path) +load_sample(LV2_Log_Logger* logger, const char* path, const int sample_rate) { lv2_log_trace(logger, "Loading %s\n", path); @@ -155,6 +157,42 @@ load_sample(LV2_Log_Logger* logger, const char* path) info->channels = 1; } + if (info->samplerate != sample_rate) { + lv2_log_trace(logger, + "Converting from %d Hz to %d Hz\n", + info->samplerate, + sample_rate); + + const double src_ratio = (double)sample_rate / (double)info->samplerate; + const double output_length = ceil((double)info->frames * src_ratio); + const long output_frames = (long)output_length; + float* const output_buffer = (float*)malloc(sizeof(float) * output_frames); + + SRC_DATA src_data = { + data, + output_buffer, + info->frames, + output_frames, + 0, + 0, + 0, + src_ratio, + }; + + if (src_simple(&src_data, SRC_SINC_BEST_QUALITY, 1) != 0) { + lv2_log_error(logger, "Sample rate conversion failed\n"); + free(output_buffer); + } else { + // Replace original data with converted buffer + free(data); + data = output_buffer; + info->frames = src_data.output_frames_gen; + } + } else { + lv2_log_trace( + logger, "Sample matches the current rate of %d Hz\n", sample_rate); + } + // Fill sample struct and return it sample->data = data; sample->path = (char*)malloc(path_len + 1); @@ -205,7 +243,7 @@ work(LV2_Handle instance, } // Load sample. - Sample* sample = load_sample(&self->logger, path); + Sample* sample = load_sample(&self->logger, path, self->sample_rate); if (sample) { // Send new sample to run() to be applied respond(handle, sizeof(Sample*), &sample); @@ -301,8 +339,9 @@ instantiate(const LV2_Descriptor* descriptor, lv2_atom_forge_init(&self->forge, self->map); peaks_sender_init(&self->psend, self->map); - self->gain = 1.0f; - self->gain_dB = 0.0f; + self->gain = 1.0f; + self->gain_dB = 0.0f; + self->sample_rate = (int)rate; return (LV2_Handle)self; } @@ -594,7 +633,7 @@ restore(LV2_Handle instance, if (!self->activated || !schedule) { // No scheduling available, load sample immediately lv2_log_trace(&self->logger, "Synchronous restore\n"); - Sample* sample = load_sample(&self->logger, path); + Sample* sample = load_sample(&self->logger, path, self->sample_rate); if (sample) { free_sample(self, self->sample); self->sample = sample; |