diff options
Diffstat (limited to 'plugins/eg-sampler.lv2')
-rw-r--r-- | plugins/eg-sampler.lv2/atom_sink.h | 17 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/manifest.ttl.in | 2 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/meson.build | 78 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/peaks.h | 26 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/sampler.c | 125 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/sampler_ui.c | 18 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/uris.h | 18 | ||||
l--------- | plugins/eg-sampler.lv2/waf | 1 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/wscript | 64 |
9 files changed, 176 insertions, 173 deletions
diff --git a/plugins/eg-sampler.lv2/atom_sink.h b/plugins/eg-sampler.lv2/atom_sink.h index 57035e1..8966eb2 100644 --- a/plugins/eg-sampler.lv2/atom_sink.h +++ b/plugins/eg-sampler.lv2/atom_sink.h @@ -1,18 +1,5 @@ -/* - Copyright 2016 David Robillard <d@drobilla.net> - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ +// Copyright 2016 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC #include "lv2/atom/atom.h" #include "lv2/atom/forge.h" diff --git a/plugins/eg-sampler.lv2/manifest.ttl.in b/plugins/eg-sampler.lv2/manifest.ttl.in index 8a01428..e688256 100644 --- a/plugins/eg-sampler.lv2/manifest.ttl.in +++ b/plugins/eg-sampler.lv2/manifest.ttl.in @@ -15,5 +15,5 @@ <http://lv2plug.in/plugins/eg-sampler#ui> a ui:GtkUI ; - ui:binary <sampler_ui@LIB_EXT@> ; + lv2:binary <sampler_ui@LIB_EXT@> ; rdfs:seeAlso <sampler.ttl> . diff --git a/plugins/eg-sampler.lv2/meson.build b/plugins/eg-sampler.lv2/meson.build new file mode 100644 index 0000000..9700df2 --- /dev/null +++ b/plugins/eg-sampler.lv2/meson.build @@ -0,0 +1,78 @@ +# Copyright 2022 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +plugin_sources = files('sampler.c') +ui_sources = files('sampler_ui.c') +bundle_name = 'eg-sampler.lv2' +data_filenames = ['manifest.ttl.in', 'sampler.ttl', 'click.wav'] + +samplerate_dep = dependency( + 'samplerate', + required: get_option('plugins'), + version: '>= 0.1.0', +) + +sndfile_dep = dependency( + 'sndfile', + required: get_option('plugins'), + version: '>= 1.0.0', +) + +gtk2_dep = dependency( + 'gtk+-2.0', + include_type: 'system', + required: get_option('plugins'), + version: '>= 2.18.0', +) + +if samplerate_dep.found() and sndfile_dep.found() + module = shared_library( + 'sampler', + plugin_sources, + c_args: c_suppressions, + dependencies: [lv2_dep, m_dep, samplerate_dep, sndfile_dep], + gnu_symbol_visibility: 'hidden', + install: true, + install_dir: lv2dir / bundle_name, + name_prefix: '', + ) + + extension = '.' + module.full_path().split('.')[-1] + config = configuration_data({'LIB_EXT': extension}) + + foreach filename : data_filenames + if filename.endswith('.in') + configure_file( + configuration: config, + input: files(filename), + install_dir: lv2dir / bundle_name, + output: filename.substring(0, -3), + ) + else + configure_file( + copy: true, + input: files(filename), + install_dir: lv2dir / bundle_name, + output: filename, + ) + endif + endforeach + + if gtk2_dep.found() + ui_suppressions = c_suppressions + if cc.get_id() == 'gcc' + ui_suppressions += ['-Wno-strict-overflow'] + endif + + shared_library( + 'sampler_ui', + ui_sources, + c_args: ui_suppressions, + dependencies: [lv2_dep, gtk2_dep], + gnu_symbol_visibility: 'hidden', + install: true, + install_dir: lv2dir / bundle_name, + name_prefix: '', + ) + endif +endif diff --git a/plugins/eg-sampler.lv2/peaks.h b/plugins/eg-sampler.lv2/peaks.h index ca5328d..47d6616 100644 --- a/plugins/eg-sampler.lv2/peaks.h +++ b/plugins/eg-sampler.lv2/peaks.h @@ -1,19 +1,5 @@ -/* - LV2 audio peaks utilities - Copyright 2016 David Robillard <d@drobilla.net> - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ +// Copyright 2016 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC #ifndef PEAKS_H_INCLUDED #define PEAKS_H_INCLUDED @@ -153,11 +139,11 @@ peaks_sender_send(PeaksSender* sender, // eg:offset = OFFSET lv2_atom_forge_key(forge, uris->peaks_offset); - lv2_atom_forge_int(forge, sender->current_offset); + lv2_atom_forge_int(forge, (int32_t)sender->current_offset); // eg:total = TOTAL lv2_atom_forge_key(forge, uris->peaks_total); - lv2_atom_forge_int(forge, sender->n_peaks); + lv2_atom_forge_int(forge, (int32_t)sender->n_peaks); // eg:magnitudes = Vector<Float>(PEAK, PEAK, ...) lv2_atom_forge_key(forge, uris->peaks_magnitudes); @@ -166,11 +152,11 @@ peaks_sender_send(PeaksSender* sender, forge, &vec_frame, sizeof(float), uris->atom_Float); // Calculate how many peaks to send this update - const uint32_t chunk_size = MAX(1u, sender->n_samples / sender->n_peaks); + const uint32_t chunk_size = MAX(1U, sender->n_samples / sender->n_peaks); const uint32_t space = forge->size - forge->offset; const uint32_t remaining = sender->n_peaks - sender->current_offset; const uint32_t n_update = - MIN(remaining, MIN(n_frames / 4u, space / sizeof(float))); + MIN(remaining, MIN(n_frames / 4U, space / sizeof(float))); // Calculate peak (maximum magnitude) for each chunk for (uint32_t i = 0; i < n_update; ++i) { diff --git a/plugins/eg-sampler.lv2/sampler.c b/plugins/eg-sampler.lv2/sampler.c index c141038..d870a07 100644 --- a/plugins/eg-sampler.lv2/sampler.c +++ b/plugins/eg-sampler.lv2/sampler.c @@ -1,21 +1,7 @@ -/* - LV2 Sampler Example Plugin - Copyright 2011-2016 David Robillard <d@drobilla.net> - Copyright 2011 Gabriel M. Beddingfield <gabriel@teuton.org> - Copyright 2011 James Morris <jwm.art.net@gmail.com> - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ +// Copyright 2011-2016 David Robillard <d@drobilla.net> +// Copyright 2011 Gabriel M. Beddingfield <gabriel@teuton.org> +// Copyright 2011 James Morris <jwm.art.net@gmail.com> +// SPDX-License-Identifier: ISC #include "atom_sink.h" #include "peaks.h" @@ -33,6 +19,7 @@ #include "lv2/urid/urid.h" #include "lv2/worker/worker.h" +#include <samplerate.h> #include <sndfile.h> #include <math.h> @@ -80,6 +67,7 @@ typedef struct { bool activated; bool gain_changed; bool sample_changed; + int sample_rate; } Sampler; /** @@ -96,6 +84,23 @@ typedef struct { } SampleMessage; /** + Convert an interleaved audio buffer to mono. + + This simply ignores the data on all channels but the first. +*/ +static sf_count_t +convert_to_mono(float* data, sf_count_t num_input_frames, uint32_t channels) +{ + sf_count_t num_output_frames = 0; + + for (sf_count_t i = 0; i < num_input_frames * channels; i += channels) { + data[num_output_frames++] = data[i]; + } + + return num_output_frames; +} + +/** Load a new sample and return it. Since this is of course not a real-time safe action, this is called in the @@ -103,7 +108,7 @@ typedef struct { 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); @@ -115,9 +120,8 @@ load_sample(LV2_Log_Logger* logger, const char* path) bool error = true; if (!sndfile || !info->frames) { lv2_log_error(logger, "Failed to open %s\n", path); - } else if (info->channels != 1) { - lv2_log_error(logger, "%s has %d channels\n", path, info->channels); - } else if (!(data = (float*)malloc(sizeof(float) * info->frames))) { + } else if (!(data = (float*)malloc(sizeof(float) * info->frames * + info->channels))) { lv2_log_error(logger, "Failed to allocate memory for sample\n"); } else { error = false; @@ -130,10 +134,51 @@ load_sample(LV2_Log_Logger* logger, const char* path) return NULL; } - sf_seek(sndfile, 0ul, SEEK_SET); - sf_read_float(sndfile, data, info->frames); + sf_seek(sndfile, 0UL, SEEK_SET); + sf_read_float(sndfile, data, info->frames * info->channels); sf_close(sndfile); + if (info->channels != 1) { + info->frames = convert_to_mono(data, info->frames, info->channels); + 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); @@ -184,7 +229,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); @@ -280,8 +325,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; } @@ -440,20 +486,19 @@ run(LV2_Handle instance, uint32_t sample_count) // Start a sequence in the notify output port. lv2_atom_forge_sequence_head(&self->forge, &self->notify_frame, 0); - // Send update to UI if gain or sample has changed due to state restore - if (self->gain_changed || self->sample_changed) { + // Send update to UI if gain has changed due to state restore + if (self->gain_changed) { lv2_atom_forge_frame_time(&self->forge, 0); + write_set_gain(&self->forge, &self->uris, self->gain_dB); + self->gain_changed = false; + } - if (self->gain_changed) { - write_set_gain(&self->forge, &self->uris, self->gain_dB); - self->gain_changed = false; - } - - if (self->sample_changed) { - write_set_file( - &self->forge, &self->uris, self->sample->path, self->sample->path_len); - self->sample_changed = false; - } + // Send update to UI if sample has changed due to state restore + if (self->sample_changed) { + lv2_atom_forge_frame_time(&self->forge, 0); + write_set_file( + &self->forge, &self->uris, self->sample->path, self->sample->path_len); + self->sample_changed = false; } // Iterate over incoming events, emitting audio along the way @@ -574,7 +619,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; diff --git a/plugins/eg-sampler.lv2/sampler_ui.c b/plugins/eg-sampler.lv2/sampler_ui.c index 630fc22..16a8c71 100644 --- a/plugins/eg-sampler.lv2/sampler_ui.c +++ b/plugins/eg-sampler.lv2/sampler_ui.c @@ -1,19 +1,5 @@ -/* - LV2 Sampler Example Plugin UI - Copyright 2011-2016 David Robillard <d@drobilla.net> - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ +// Copyright 2011-2016 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC #include "peaks.h" #include "uris.h" diff --git a/plugins/eg-sampler.lv2/uris.h b/plugins/eg-sampler.lv2/uris.h index d7201fa..f934390 100644 --- a/plugins/eg-sampler.lv2/uris.h +++ b/plugins/eg-sampler.lv2/uris.h @@ -1,19 +1,5 @@ -/* - LV2 Sampler Example Plugin - Copyright 2011-2016 David Robillard <d@drobilla.net> - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ +// Copyright 2011-2016 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC #ifndef SAMPLER_URIS_H #define SAMPLER_URIS_H diff --git a/plugins/eg-sampler.lv2/waf b/plugins/eg-sampler.lv2/waf deleted file mode 120000 index 59a1ac9..0000000 --- a/plugins/eg-sampler.lv2/waf +++ /dev/null @@ -1 +0,0 @@ -../../waf
\ No newline at end of file diff --git a/plugins/eg-sampler.lv2/wscript b/plugins/eg-sampler.lv2/wscript deleted file mode 100644 index 8c640c1..0000000 --- a/plugins/eg-sampler.lv2/wscript +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python -from waflib.extras import autowaf as autowaf -import re - -# Variables for 'waf dist' -APPNAME = 'eg-sampler.lv2' -VERSION = '1.0.0' - -# Mandatory variables -top = '.' -out = 'build' - -def options(opt): - opt.load('compiler_c') - opt.load('lv2') - autowaf.set_options(opt) - -def configure(conf): - conf.load('compiler_c', cache=True) - conf.load('lv2', cache=True) - conf.load('autowaf', cache=True) - - conf.check_pkg('lv2 >= 1.2.1', uselib_store='LV2') - conf.check_pkg('sndfile >= 1.0.0', uselib_store='SNDFILE') - conf.check_pkg('gtk+-2.0 >= 2.18.0', - uselib_store='GTK2', - system=True, - mandatory=False) - conf.check(features='c cshlib', lib='m', uselib_store='M', mandatory=False) - -def build(bld): - bundle = 'eg-sampler.lv2' - - # Build manifest.ttl by substitution (for portable lib extension) - bld(features = 'subst', - source = 'manifest.ttl.in', - target = 'lv2/%s/%s' % (bundle, 'manifest.ttl'), - install_path = '${LV2DIR}/%s' % bundle, - LIB_EXT = bld.env.LV2_LIB_EXT) - - # Copy other data files to build bundle (build/eg-sampler.lv2) - for i in ['sampler.ttl', 'click.wav']: - bld(features = 'subst', - is_copy = True, - source = i, - target = 'lv2/%s/%s' % (bundle, i), - install_path = '${LV2DIR}/%s' % bundle) - - # Build plugin library - obj = bld(features = 'c cshlib lv2lib', - source = 'sampler.c', - name = 'sampler', - target = 'lv2/%s/sampler' % bundle, - install_path = '${LV2DIR}/%s' % bundle, - use = ['M', 'SNDFILE', 'LV2']) - - # Build UI library - if bld.env.HAVE_GTK2: - obj = bld(features = 'c cshlib lv2lib', - source = 'sampler_ui.c', - name = 'sampler_ui', - target = 'lv2/%s/sampler_ui' % bundle, - install_path = '${LV2DIR}/%s' % bundle, - use = ['GTK2', 'LV2']) |