aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lv2/core.lv2/meta.ttl2
-rw-r--r--plugins/eg-sampler.lv2/meson.build8
-rw-r--r--plugins/eg-sampler.lv2/sampler.c49
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;