From 471a12185abb24458d4606efa96c61f02a728533 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 14 Jun 2012 21:42:03 +0000 Subject: Add synth example from Harry van Haaren. --- plugins/eg-synth.lv2/manifest.ttl.in | 9 ++ plugins/eg-synth.lv2/synth.c | 173 +++++++++++++++++++++++++++++++++++ plugins/eg-synth.lv2/synth.ttl | 48 ++++++++++ plugins/eg-synth.lv2/waf | 1 + plugins/eg-synth.lv2/wscript | 76 +++++++++++++++ 5 files changed, 307 insertions(+) create mode 100644 plugins/eg-synth.lv2/manifest.ttl.in create mode 100644 plugins/eg-synth.lv2/synth.c create mode 100644 plugins/eg-synth.lv2/synth.ttl create mode 120000 plugins/eg-synth.lv2/waf create mode 100644 plugins/eg-synth.lv2/wscript (limited to 'plugins') diff --git a/plugins/eg-synth.lv2/manifest.ttl.in b/plugins/eg-synth.lv2/manifest.ttl.in new file mode 100644 index 0000000..24acd71 --- /dev/null +++ b/plugins/eg-synth.lv2/manifest.ttl.in @@ -0,0 +1,9 @@ +# See manifest.ttl.in in the eg-amp.lv2 example for an explanation of this file + +@prefix lv2: . +@prefix rdfs: . + + + a lv2:Plugin ; + lv2:binary ; + rdfs:seeAlso . diff --git a/plugins/eg-synth.lv2/synth.c b/plugins/eg-synth.lv2/synth.c new file mode 100644 index 0000000..c607daa --- /dev/null +++ b/plugins/eg-synth.lv2/synth.c @@ -0,0 +1,173 @@ +/* + Copyright 2012 Harry van Haaren + Copyright 2012 David Robillard + + 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. +*/ + +/** + @file synth.c Implementation of the LV2 Sin Synth example plugin. + + This is a simple LV2 synthesis plugin that demonstrates how to receive MIDI + events and render audio in response to them. +*/ + +#include +#include +#include + +#include "lv2/lv2plug.in/ns/lv2core/lv2.h" + +#define SYNTH_URI "http://lv2plug.in/plugins/eg-synth" + +/** Port indices. */ +typedef enum { + SYNTH_FREQ = 0, + SYNTH_OUTPUT, +} PortIndex; + +/** Plugin instance. */ +typedef struct { + // Sample rate, necessary to generate sin wave in run() + double sample_rate; + + // Current wave phase + float phase; + + // Port buffers + float* freq; + float* input; + float* output; +} Synth; + +/** Create a new plugin instance. */ +static LV2_Handle +instantiate(const LV2_Descriptor* descriptor, + double rate, + const char* bundle_path, + const LV2_Feature* const* features) +{ + Synth* self = (Synth*)malloc(sizeof(Synth)); + + // Store the sample rate so it is available in run() + self->sample_rate = rate; + + return (LV2_Handle)self; +} + +/** Connect a port to a buffer (audio thread, must be RT safe). */ +static void +connect_port(LV2_Handle instance, + uint32_t port, + void* data) +{ + Synth* self = (Synth*)instance; + + switch ((PortIndex)port) { + case SYNTH_FREQ: + self->freq = (float*)data; + break; + case SYNTH_OUTPUT: + self->output = (float*)data; + break; + } +} + +/** Initialise and prepare the plugin instance for running. */ +static void +activate(LV2_Handle instance) +{ + Synth* self = (Synth*)instance; + + // Initialize phase so we start at the beginning of the wave + self->phase = 0.0f; +} + +/** Process a block of audio (audio thread, must be RT safe). */ +static void +run(LV2_Handle instance, uint32_t n_samples) +{ + Synth* self = (Synth*)instance; + + const float PI = 3.1415; + const float volume = 0.3; + const float freq = *(self->freq); + float* const output = self->output; + + float samples_per_cycle = self->sample_rate / freq; + + /* Calculate the phase offset per sample. Phase ranges from 0..1, so + phase_increment is a floating point number such that we get "freq" + number of cycles in "sample_rate" amount of samples. */ + float phase_increment = (1.f / samples_per_cycle); + + for (uint32_t pos = 0; pos < n_samples; pos++) { + + /* Calculate the next sample. Phase ranges from 0..1, but sin() + expects its input in radians, so we multiply by 2 PI to convert it. + We also multiply by volume so it's not extremely loud. */ + output[pos] = sin(self->phase * 2 * PI) * volume; + + /* Increment the phase so we generate the next sample */ + self->phase += phase_increment; + if (self->phase > 1.0f) { + self->phase = 0.0f; + } + } +} + +/** Finish running (counterpart to activate()). */ +static void +deactivate(LV2_Handle instance) +{ + /* Nothing to do here in this trivial mostly stateless plugin. */ +} + +/** Destroy a plugin instance (counterpart to instantiate()). */ +static void +cleanup(LV2_Handle instance) +{ + free(instance); +} + +/** Return extension data provided by the plugin. */ +const void* +extension_data(const char* uri) +{ + return NULL; /* This plugin has no extension data. */ +} + +/** The LV2_Descriptor for this plugin. */ +static const LV2_Descriptor descriptor = { + SYNTH_URI, + instantiate, + connect_port, + activate, + run, + deactivate, + cleanup, + extension_data +}; + +/** Entry point, the host will call this function to access descriptors. */ +LV2_SYMBOL_EXPORT +const LV2_Descriptor* +lv2_descriptor(uint32_t index) +{ + switch (index) { + case 0: + return &descriptor; + default: + return NULL; + } +} diff --git a/plugins/eg-synth.lv2/synth.ttl b/plugins/eg-synth.lv2/synth.ttl new file mode 100644 index 0000000..dd6afa4 --- /dev/null +++ b/plugins/eg-synth.lv2/synth.ttl @@ -0,0 +1,48 @@ +# LV2 Sinewave synth plugin +# Copyright 2012 Harry van Haaren +# +# 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. + +@prefix doap: . +@prefix foaf: . +@prefix lv2: . +@prefix rdf: . +@prefix rdfs: . + + + a lv2:Plugin , + lv2:InstrumentPlugin ; + doap:maintainer [ + a foaf:Person ; + foaf:name "Harry van Haaren" ; + foaf:mbox + ] ; + doap:name "Example Synthesizer" ; + doap:license ; + lv2:optionalFeature lv2:hardRTCapable ; + lv2:port [ + a lv2:InputPort , + lv2:ControlPort ; + lv2:index 0 ; + lv2:symbol "frequency" ; + lv2:name "Frequency" ; + lv2:default 440.0 ; + lv2:minimum 40.0 ; + lv2:maximum 880.0 + ] , [ + a lv2:AudioPort , + lv2:OutputPort ; + lv2:index 1 ; + lv2:symbol "out" ; + lv2:name "Out" + ] . diff --git a/plugins/eg-synth.lv2/waf b/plugins/eg-synth.lv2/waf new file mode 120000 index 0000000..59a1ac9 --- /dev/null +++ b/plugins/eg-synth.lv2/waf @@ -0,0 +1 @@ +../../waf \ No newline at end of file diff --git a/plugins/eg-synth.lv2/wscript b/plugins/eg-synth.lv2/wscript new file mode 100644 index 0000000..f1b8e2d --- /dev/null +++ b/plugins/eg-synth.lv2/wscript @@ -0,0 +1,76 @@ +#!/usr/bin/env python +from waflib.extras import autowaf as autowaf + +# Variables for 'waf dist' +APPNAME = 'eg-synth.lv2' +VERSION = '1.0.0' + +# Mandatory variables +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + autowaf.set_options(opt) + +def configure(conf): + conf.load('compiler_c') + autowaf.configure(conf) + autowaf.display_header('Synth Configuration') + + if conf.env['MSVC_COMPILER']: + conf.env.append_unique('CFLAGS', ['-TP', '-MD']) + else: + conf.env.append_unique('CFLAGS', '-std=c99') + + if not autowaf.is_child(): + autowaf.check_pkg(conf, 'lv2core', uselib_store='LV2CORE') + + # Set env['pluginlib_PATTERN'] + pat = conf.env['cshlib_PATTERN'] + if pat.startswith('lib'): + pat = pat[3:] + conf.env['pluginlib_PATTERN'] = pat + conf.env['pluginlib_EXT'] = pat[pat.rfind('.'):] + + autowaf.display_msg(conf, "LV2 bundle directory", conf.env['LV2DIR']) + print('') + +def build(bld): + bundle = APPNAME + + # Build manifest.ttl by substitution (for portable lib extension) + bld(features = 'subst', + source = 'manifest.ttl.in', + target = '%s/%s' % (bundle, 'manifest.ttl'), + install_path = '${LV2DIR}/%s' % bundle, + LIB_EXT = bld.env['pluginlib_EXT']) + + # Copy other data files to build bundle (build/eg-amp.lv2) + for i in [ 'synth.ttl' ]: + bld(features = 'subst', + source = i, + target = '%s/%s' % (bundle, i), + install_path = '${LV2DIR}/%s' % bundle, + LIB_EXT = bld.env['pluginlib_EXT']) + + # Create a build environment that builds module-style library names + # e.g. eg-amp.so instead of libeg-amp.so + # Note for C++ you must set cxxshlib_PATTERN instead + penv = bld.env.derive() + penv['cshlib_PATTERN'] = bld.env['pluginlib_PATTERN'] + + includes = None + if autowaf.is_child: + includes = '../..' + + # Build plugin library + obj = bld(features = 'c cshlib', + env = penv, + source = 'synth.c', + name = 'synth', + target = '%s/synth' % bundle, + install_path = '${LV2DIR}/%s' % bundle, + uselib = 'LV2CORE', + includes = includes) + -- cgit v1.2.1