aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/adsr.c259
-rw-r--r--src/adsr_gt.c266
-rw-r--r--src/amp.c166
-rw-r--r--src/branch.c223
-rw-r--r--src/dahdsr.c416
-rw-r--r--src/difference.c188
-rw-r--r--src/fmod.c199
-rw-r--r--src/include/common.h54
-rw-r--r--src/include/interpolate.h74
-rw-r--r--src/include/lp4pole_filter.h137
-rw-r--r--src/include/math_func.h48
-rw-r--r--src/include/uris.h59
-rw-r--r--src/include/vector_op.h44
-rw-r--r--src/include/wavedata.h193
-rw-r--r--src/include/wdatutil.h141
-rw-r--r--src/interpolator.c144
-rw-r--r--src/lp4pole.c206
-rw-r--r--src/lp4pole_filter.c55
-rw-r--r--src/product.c187
-rw-r--r--src/pulse.c225
-rw-r--r--src/quantiser.c461
-rw-r--r--src/random.c237
-rw-r--r--src/ratio.c202
-rw-r--r--src/sawtooth.c194
-rw-r--r--src/sequencer.c216
-rw-r--r--src/square.c195
-rw-r--r--src/sum.c185
-rw-r--r--src/sync_pulse.c213
-rw-r--r--src/sync_square.c201
-rw-r--r--src/tracker.c245
-rw-r--r--src/triangle.c232
-rw-r--r--src/wavedata.c79
-rw-r--r--src/wavegen.c310
-rw-r--r--src/wdatutil.c679
34 files changed, 6933 insertions, 0 deletions
diff --git a/src/adsr.c b/src/adsr.c
new file mode 100644
index 0000000..a84469f
--- /dev/null
+++ b/src/adsr.c
@@ -0,0 +1,259 @@
+/*
+ An LV2 plugin to generate ADSR envelopes.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "common.h"
+
+#define ADSR_SIGNAL 0
+#define ADSR_TRIGGER 1
+#define ADSR_ATTACK 2
+#define ADSR_DECAY 3
+#define ADSR_SUSTAIN 4
+#define ADSR_RELEASE 5
+#define ADSR_OUTPUT 6
+
+typedef enum {
+ IDLE,
+ ATTACK,
+ DECAY,
+ SUSTAIN,
+ RELEASE
+} ADSRState;
+
+typedef struct {
+ const float* signal;
+ const float* trigger;
+ const float* attack;
+ const float* decay;
+ const float* sustain;
+ const float* release;
+ float* output;
+ float srate;
+ float inv_srate;
+ float from_level;
+ float level;
+ ADSRState state;
+ uint32_t samples;
+} Adsr;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Adsr* plugin = (Adsr*)instance;
+
+ switch (port) {
+ case ADSR_SIGNAL:
+ plugin->signal = (const float*)data;
+ break;
+ case ADSR_TRIGGER:
+ plugin->trigger = (const float*)data;
+ break;
+ case ADSR_ATTACK:
+ plugin->attack = (const float*)data;
+ break;
+ case ADSR_DECAY:
+ plugin->decay = (const float*)data;
+ break;
+ case ADSR_SUSTAIN:
+ plugin->sustain = (const float*)data;
+ break;
+ case ADSR_RELEASE:
+ plugin->release = (const float*)data;
+ break;
+ case ADSR_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Adsr* plugin = (Adsr*)malloc(sizeof(Adsr));
+ if (!plugin) {
+ return NULL;
+ }
+
+ plugin->srate = (float)sample_rate;
+ plugin->inv_srate = 1.0f / plugin->srate;
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Adsr* plugin = (Adsr*)instance;
+
+ plugin->from_level = 0.0f;
+ plugin->level = 0.0f;
+ plugin->state = IDLE;
+ plugin->samples = 0;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Adsr* plugin = (Adsr*)instance;
+
+ /* Driving signal */
+ const float* signal = plugin->signal;
+
+ /* Trigger Threshold */
+ const float trigger = *(plugin->trigger);
+
+ /* Attack Time (s) */
+ float attack = *(plugin->attack);
+
+ /* Decay Time (s) */
+ float decay = *(plugin->decay);
+
+ /* Sustain Level */
+ const float sustain = f_clip(*(plugin->sustain), 0.0f, 1.0f);
+
+ /* Release Time (s) */
+ float release = *(plugin->release);
+
+ /* Envelope Out */
+ float* output = plugin->output;
+
+ float srate = plugin->srate;
+ float inv_srate = plugin->inv_srate;
+ float from_level = plugin->from_level;
+ float level = plugin->level;
+ ADSRState state = plugin->state;
+ uint32_t samples = plugin->samples;
+
+ float elapsed;
+
+ /* Convert times into rates */
+ attack = attack > 0.0f ? inv_srate / attack : srate;
+ decay = decay > 0.0f ? inv_srate / decay : srate;
+ release = release > 0.0f ? inv_srate / release : srate;
+
+ for (uint32_t s = 0; s < sample_count; s++) {
+ /* Determine if attack or release happened */
+ if ((state == IDLE) || (state == RELEASE)) {
+ if (signal[s] > trigger) {
+ if (attack < srate) {
+ state = ATTACK;
+ } else {
+ state = decay < srate ? DECAY : SUSTAIN;
+ level = 1.0f;
+ }
+ samples = 0;
+ }
+ } else {
+ if (signal[s] <= trigger) {
+ state = release < srate ? RELEASE : IDLE;
+ samples = 0;
+ }
+ }
+
+ if (samples == 0) {
+ from_level = level;
+ }
+
+ /* Calculate level of envelope from current state */
+ switch (state) {
+ case IDLE:
+ level = 0;
+ break;
+ case ATTACK:
+ samples++;
+ elapsed = (float)samples * attack;
+ if (elapsed > 1.0f) {
+ state = decay < srate ? DECAY : SUSTAIN;
+ level = 1.0f;
+ samples = 0;
+ } else {
+ level = from_level + elapsed * (1.0f - from_level);
+ }
+ break;
+ case DECAY:
+ samples++;
+ elapsed = (float)samples * decay;
+ if (elapsed > 1.0f) {
+ state = SUSTAIN;
+ level = sustain;
+ samples = 0;
+ } else {
+ level = from_level + elapsed * (sustain - from_level);
+ }
+ break;
+ case SUSTAIN:
+ level = sustain;
+ break;
+ case RELEASE:
+ samples++;
+ elapsed = (float)samples * release;
+ if (elapsed > 1.0f) {
+ state = IDLE;
+ level = 0.0f;
+ samples = 0;
+ } else {
+ level = from_level - elapsed * from_level;
+ }
+ break;
+ default:
+ /* Should never happen */
+ level = 0.0f;
+ }
+
+ output[s] = level;
+ }
+
+ plugin->from_level = from_level;
+ plugin->level = level;
+ plugin->state = state;
+ plugin->samples = samples;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/adsr",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ NULL,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/adsr_gt.c b/src/adsr_gt.c
new file mode 100644
index 0000000..4f23649
--- /dev/null
+++ b/src/adsr_gt.c
@@ -0,0 +1,266 @@
+/*
+ An LV2 plugin to generate ADSR envelopes Gate and Trigger variant.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "common.h"
+
+#define ADSR_GATE 0
+#define ADSR_TRIGGER 1
+#define ADSR_ATTACK 2
+#define ADSR_DECAY 3
+#define ADSR_SUSTAIN 4
+#define ADSR_RELEASE 5
+#define ADSR_OUTPUT 6
+
+typedef enum {
+ IDLE,
+ ATTACK,
+ DECAY,
+ SUSTAIN,
+ RELEASE
+} ADSRState;
+
+typedef struct {
+ const float* gate;
+ const float* trigger;
+ const float* attack;
+ const float* decay;
+ const float* sustain;
+ const float* release;
+ float* output;
+ float srate;
+ float inv_srate;
+ float last_trigger;
+ float from_level;
+ float level;
+ ADSRState state;
+ uint32_t samples;
+} Adsr;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Adsr* plugin = (Adsr*)instance;
+
+ switch (port) {
+ case ADSR_GATE:
+ plugin->gate = (const float*)data;
+ break;
+ case ADSR_TRIGGER:
+ plugin->trigger = (const float*)data;
+ break;
+ case ADSR_ATTACK:
+ plugin->attack = (const float*)data;
+ break;
+ case ADSR_DECAY:
+ plugin->decay = (const float*)data;
+ break;
+ case ADSR_SUSTAIN:
+ plugin->sustain = (const float*)data;
+ break;
+ case ADSR_RELEASE:
+ plugin->release = (const float*)data;
+ break;
+ case ADSR_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Adsr* plugin = (Adsr*)malloc(sizeof(Adsr));
+
+ if (plugin) {
+ plugin->srate = (float)sample_rate;
+ plugin->inv_srate = 1.0f / plugin->srate;
+ }
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Adsr* plugin = (Adsr*)instance;
+
+ plugin->last_trigger = 0.0f;
+ plugin->from_level = 0.0f;
+ plugin->level = 0.0f;
+ plugin->state = IDLE;
+ plugin->samples = 0;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Adsr* plugin = (Adsr*)instance;
+
+ /* Gate */
+ const float* gate = plugin->gate;
+
+ /* Trigger */
+ const float* trigger = plugin->trigger;
+
+ /* Attack Time (s) */
+ float attack = *(plugin->attack);
+
+ /* Decay Time (s) */
+ float decay = *(plugin->decay);
+
+ /* Sustain Level */
+ const float sustain = f_clip(*(plugin->sustain), 0.0f, 1.0f);
+
+ /* Release Time (s) */
+ float release = *(plugin->release);
+
+ /* Envelope Out */
+ float* output = plugin->output;
+
+ float srate = plugin->srate;
+ float inv_srate = plugin->inv_srate;
+ float last_trigger = plugin->last_trigger;
+ float from_level = plugin->from_level;
+ float level = plugin->level;
+ ADSRState state = plugin->state;
+ uint32_t samples = plugin->samples;
+
+ float elapsed;
+
+ /* Convert times into rates */
+ attack = attack > 0.0f ? inv_srate / attack : srate;
+ decay = decay > 0.0f ? inv_srate / decay : srate;
+ release = release > 0.0f ? inv_srate / release : srate;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ /* Attack on trigger, if gate is open */
+ if (trigger[s] > 0.0f
+ && !(last_trigger > 0.0f)
+ && gate[s] > 0.0f) {
+ if (attack < srate) {
+ state = ATTACK;
+ } else {
+ state = decay < srate ? DECAY : SUSTAIN;
+ level = 1.0f;
+ }
+ samples = 0;
+ }
+
+ /* Release if gate closed */
+ if (state != IDLE
+ && state != RELEASE
+ && !(gate[s] > 0.0f)) {
+ state = release < srate ? RELEASE : IDLE;
+ samples = 0;
+ }
+
+ if (samples == 0) {
+ from_level = level;
+ }
+
+ /* Calculate level of envelope from current state */
+ switch (state) {
+ case IDLE:
+ level = 0;
+ break;
+ case ATTACK:
+ samples++;
+ elapsed = (float)samples * attack;
+ if (elapsed > 1.0f) {
+ state = decay < srate ? DECAY : SUSTAIN;
+ level = 1.0f;
+ samples = 0;
+ } else {
+ level = from_level + elapsed * (1.0f - from_level);
+ }
+ break;
+ case DECAY:
+ samples++;
+ elapsed = (float)samples * decay;
+ if (elapsed > 1.0f) {
+ state = SUSTAIN;
+ level = sustain;
+ samples = 0;
+ } else {
+ level = from_level + elapsed * (sustain - from_level);
+ }
+ break;
+ case SUSTAIN:
+ level = sustain;
+ break;
+ case RELEASE:
+ samples++;
+ elapsed = (float)samples * release;
+ if (elapsed > 1.0f) {
+ state = IDLE;
+ level = 0.0f;
+ samples = 0;
+ } else {
+ level = from_level - elapsed * from_level;
+ }
+ break;
+ default:
+ /* Should never happen */
+ level = 0.0f;
+ }
+
+ output[s] = level;
+ last_trigger = trigger[s];
+ }
+
+ plugin->last_trigger = last_trigger;
+ plugin->from_level = from_level;
+ plugin->level = level;
+ plugin->state = state;
+ plugin->samples = samples;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/adsr_gt",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ NULL,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/amp.c b/src/amp.c
new file mode 100644
index 0000000..4f6de84
--- /dev/null
+++ b/src/amp.c
@@ -0,0 +1,166 @@
+/*
+ An LV2 plugin representing a simple mono amplifier.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "math_func.h"
+#include "uris.h"
+
+#define AMP_GAIN 0
+#define AMP_INPUT 1
+#define AMP_OUTPUT 2
+
+typedef struct {
+ const float* gain;
+ const float* input;
+ float* output;
+ URIs uris;
+ uint32_t gain_is_cv;
+} Amp;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Amp* plugin = (Amp*)instance;
+
+ switch (port) {
+ case AMP_GAIN:
+ plugin->gain = (const float*)data;
+ break;
+ case AMP_INPUT:
+ plugin->input = (const float*)data;
+ break;
+ case AMP_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Amp* plugin = (Amp*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case AMP_GAIN:
+ plugin->gain_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Amp* plugin = (Amp*)malloc(sizeof(Amp));
+ if (!plugin) {
+ return NULL;
+ }
+
+ plugin->gain_is_cv = 0;
+ map_uris(&plugin->uris, features);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Amp* plugin = (Amp*)instance;
+
+ /* Gain (dB) */
+ const float* gain = plugin->gain;
+
+ /* Input */
+ const float* input = plugin->input;
+
+ /* Output */
+ float* output = plugin->output;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float gn = gain[s * plugin->gain_is_cv];
+ const float scale = (float)EXPF(M_LN10 * gn * 0.05f);
+
+ output[s] = scale * input[s];
+ }
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/amp",
+ instantiate,
+ connect_port,
+ NULL,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/branch.c b/src/branch.c
new file mode 100644
index 0000000..a4de320
--- /dev/null
+++ b/src/branch.c
@@ -0,0 +1,223 @@
+/*
+ An LV2 plugin to split a signal into two.
+ Copyright 2011-2014 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+
+#define BRANCH_INPUT 0
+#define BRANCH_OUTPUT1 1
+#define BRANCH_OUTPUT2 2
+
+typedef struct {
+ const float* input;
+ float* output1;
+ float* output2;
+ LV2_URID input_type;
+ URIs uris;
+} Branch;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Branch* plugin = (Branch*)instance;
+
+ switch (port) {
+ case BRANCH_INPUT:
+ plugin->input = (const float*)data;
+ break;
+ case BRANCH_OUTPUT1:
+ plugin->output1 = (float*)data;
+ break;
+ case BRANCH_OUTPUT2:
+ plugin->output2 = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Branch* plugin = (Branch*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_AudioPort &&
+ port_type != plugin->uris.lv2_CVPort &&
+ port_type != plugin->uris.lv2_ControlPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+ switch (o->subject) {
+ case BRANCH_INPUT:
+ plugin->input_type = port_type;
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static uint32_t
+options_get(LV2_Handle instance,
+ LV2_Options_Option* options)
+{
+ const Branch* plugin = (const Branch*)instance;
+ uint32_t ret = 0;
+ for (LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT &&
+ o->subject != BRANCH_OUTPUT1 &&
+ o->subject != BRANCH_OUTPUT2) {
+ fprintf(stderr, "Bad subject %d\n", o->subject);
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ fprintf(stderr, "Bad key %d\n", o->subject);
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else {
+ o->size = sizeof(LV2_URID);
+ o->type = plugin->uris.atom_URID;
+ o->value = &plugin->input_type;
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Branch* plugin = (Branch*)malloc(sizeof(Branch));
+
+ map_uris(&plugin->uris, features);
+ plugin->input_type = plugin->uris.lv2_ControlPort;
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+runBranch_ia_oaoa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Branch* plugin = (Branch*)instance;
+
+ /* Input (array of floats of length sample_count) */
+ const float* input = plugin->input;
+
+ /* First Output (array of floats of length sample_count) */
+ float* output1 = plugin->output1;
+
+ /* Second Output (array of floats of length sample_count) */
+ float* output2 = plugin->output2;
+
+ float in;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in = input[s];
+
+ output1[s] = in;
+ output2[s] = in;
+ }
+}
+
+static void
+runBranch_ic_ococ(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Branch* plugin = (Branch*)instance;
+
+ /* Input (float value) */
+ const float input = *(plugin->input);
+
+ /* First Output (pointer to float value) */
+ float* output1 = plugin->output1;
+
+ /* Second Output (pointer to float value) */
+ float* output2 = plugin->output2;
+
+ output1[0] = input;
+ output2[0] = input;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Branch* plugin = (Branch*)instance;
+
+ if (plugin->input_type == plugin->uris.lv2_AudioPort ||
+ plugin->input_type == plugin->uris.lv2_CVPort) {
+ runBranch_ia_oaoa(instance, sample_count);
+ } else {
+ runBranch_ic_ococ(instance, sample_count);
+ }
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { options_get, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/branch",
+ instantiate,
+ connect_port,
+ NULL,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/dahdsr.c b/src/dahdsr.c
new file mode 100644
index 0000000..a4b9c65
--- /dev/null
+++ b/src/dahdsr.c
@@ -0,0 +1,416 @@
+/*
+ An LV2 plugin to generate DAHDSR envelopes Gate and (re)trigger
+ Copyright 2011 David Robillard
+ Copyright 2004 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "common.h"
+#include "uris.h"
+
+#define DAHDSR_GATE 0
+#define DAHDSR_TRIGGER 1
+#define DAHDSR_DELAY 2
+#define DAHDSR_ATTACK 3
+#define DAHDSR_HOLD 4
+#define DAHDSR_DECAY 5
+#define DAHDSR_SUSTAIN 6
+#define DAHDSR_RELEASE 7
+#define DAHDSR_OUTPUT 8
+
+typedef enum {
+ IDLE,
+ DELAY,
+ ATTACK,
+ HOLD,
+ DECAY,
+ SUSTAIN,
+ RELEASE
+} DAHDSRState;
+
+typedef struct {
+ const float* gate;
+ const float* trigger;
+ const float* delay;
+ const float* attack;
+ const float* hold;
+ const float* decay;
+ const float* sustain;
+ const float* release;
+ float* output;
+ float srate;
+ float inv_srate;
+ float last_gate;
+ float last_trigger;
+ float from_level;
+ float level;
+ uint32_t delay_is_cv;
+ uint32_t attack_is_cv;
+ uint32_t hold_is_cv;
+ uint32_t decay_is_cv;
+ uint32_t sustain_is_cv;
+ uint32_t release_is_cv;
+ DAHDSRState state;
+ uint32_t samples;
+ URIs uris;
+} Dahdsr;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Dahdsr* plugin = (Dahdsr*)instance;
+
+ switch (port) {
+ case DAHDSR_GATE:
+ plugin->gate = (const float*)data;
+ break;
+ case DAHDSR_TRIGGER:
+ plugin->trigger = (const float*)data;
+ break;
+ case DAHDSR_DELAY:
+ plugin->delay = (const float*)data;
+ break;
+ case DAHDSR_ATTACK:
+ plugin->attack = (const float*)data;
+ break;
+ case DAHDSR_HOLD:
+ plugin->hold = (const float*)data;
+ break;
+ case DAHDSR_DECAY:
+ plugin->decay = (const float*)data;
+ break;
+ case DAHDSR_SUSTAIN:
+ plugin->sustain = (const float*)data;
+ break;
+ case DAHDSR_RELEASE:
+ plugin->release = (const float*)data;
+ break;
+ case DAHDSR_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Dahdsr* plugin = (Dahdsr*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+ switch (o->subject) {
+ case DAHDSR_DELAY:
+ plugin->delay_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case DAHDSR_ATTACK:
+ plugin->attack_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case DAHDSR_HOLD:
+ plugin->hold_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case DAHDSR_DECAY:
+ plugin->decay_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case DAHDSR_SUSTAIN:
+ plugin->sustain_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case DAHDSR_RELEASE:
+ plugin->release_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Dahdsr* plugin = (Dahdsr*)malloc(sizeof(Dahdsr));
+ if (!plugin) {
+ return NULL;
+ }
+
+ plugin->srate = (float)sample_rate;
+ plugin->inv_srate = 1.0f / plugin->srate;
+
+ plugin->delay_is_cv = 0;
+ plugin->attack_is_cv = 0;
+ plugin->hold_is_cv = 0;
+ plugin->decay_is_cv = 0;
+ plugin->sustain_is_cv = 0;
+ plugin->release_is_cv = 0;
+
+ map_uris(&plugin->uris, features);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Dahdsr* plugin = (Dahdsr*)instance;
+
+ plugin->last_gate = 0.0f;
+ plugin->last_trigger = 0.0f;
+ plugin->from_level = 0.0f;
+ plugin->level = 0.0f;
+ plugin->state = IDLE;
+ plugin->samples = 0;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Dahdsr* plugin = (Dahdsr*)instance;
+
+ /* Gate */
+ const float* gate = plugin->gate;
+
+ /* Trigger */
+ const float* trigger = plugin->trigger;
+
+ /* Delay Time (s) */
+ const float* delay = plugin->delay;
+
+ /* Attack Time (s) */
+ const float* attack = plugin->attack;
+
+ /* Hold Time (s) */
+ const float* hold = plugin->hold;
+
+ /* Decay Time (s) */
+ const float* decay = plugin->decay;
+
+ /* Sustain Level */
+ const float* sustain = plugin->sustain;
+
+ /* Release Time (s) */
+ const float* release = plugin->release;
+
+ /* Envelope Out */
+ float* output = plugin->output;
+
+ /* Instance Data */
+ float srate = plugin->srate;
+ float inv_srate = plugin->inv_srate;
+ float last_gate = plugin->last_gate;
+ float last_trigger = plugin->last_trigger;
+ float from_level = plugin->from_level;
+ float level = plugin->level;
+ DAHDSRState state = plugin->state;
+ uint32_t samples = plugin->samples;
+
+ float elapsed;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float dl = delay[s * plugin->delay_is_cv];
+ const float at = attack[s * plugin->attack_is_cv];
+ const float hl = hold[s * plugin->hold_is_cv];
+ const float dc = decay[s * plugin->decay_is_cv];
+ const float st = sustain[s * plugin->sustain_is_cv];
+ const float rl = release[s * plugin->release_is_cv];
+
+ /* Convert times into rates */
+ const float del = dl > 0.0f ? inv_srate / dl : srate;
+ const float att = at > 0.0f ? inv_srate / at : srate;
+ const float hld = hl > 0.0f ? inv_srate / hl : srate;
+ const float dec = dc > 0.0f ? inv_srate / dc : srate;
+ const float rel = rl > 0.0f ? inv_srate / rl : srate;
+
+ const float gat = gate[s];
+ const float trg = trigger[s];
+ const float sus = f_clip(st, 0.0f, 1.0f);
+
+ /* Initialise delay phase if gate is opened and was closed, or
+ we received a trigger */
+ if ((trg > 0.0f && !(last_trigger > 0.0f))
+ || (gat > 0.0f && !(last_gate > 0.0f))) {
+ if (del < srate) {
+ state = DELAY;
+ } else if (att < srate) {
+ state = ATTACK;
+ } else {
+ state = hld < srate ? HOLD
+ : (dec < srate ? DECAY
+ : (gat > 0.0f ? SUSTAIN
+ : (rel < srate ? RELEASE
+ : IDLE)));
+ level = 1.0f;
+ }
+ samples = 0;
+ }
+
+ /* Release if gate was open and now closed */
+ if (state != IDLE && state != RELEASE
+ && last_gate > 0.0f && !(gat > 0.0f)) {
+ state = rel < srate ? RELEASE : IDLE;
+ samples = 0;
+ }
+
+ if (samples == 0) {
+ from_level = level;
+ }
+
+ /* Calculate level of envelope from current state */
+ switch (state) {
+ case IDLE:
+ level = 0;
+ break;
+ case DELAY:
+ samples++;
+ elapsed = (float)samples * del;
+ if (elapsed > 1.0f) {
+ state = att < srate ? ATTACK
+ : (hld < srate ? HOLD
+ : (dec < srate ? DECAY
+ : (gat > 0.0f ? SUSTAIN
+ : (rel < srate ? RELEASE
+ : IDLE))));
+ samples = 0;
+ }
+ break;
+ case ATTACK:
+ samples++;
+ elapsed = (float)samples * att;
+ if (elapsed > 1.0f) {
+ state = hld < srate ? HOLD
+ : (dec < srate ? DECAY
+ : (gat > 0.0f ? SUSTAIN
+ : (rel < srate ? RELEASE
+ : IDLE)));
+ level = 1.0f;
+ samples = 0;
+ } else {
+ level = from_level + elapsed * (1.0f - from_level);
+ }
+ break;
+ case HOLD:
+ samples++;
+ elapsed = (float)samples * hld;
+ if (elapsed > 1.0f) {
+ state = dec < srate ? DECAY
+ : (gat > 0.0f ? SUSTAIN
+ : (rel < srate ? RELEASE
+ : IDLE));
+ samples = 0;
+ }
+ break;
+ case DECAY:
+ samples++;
+ elapsed = (float)samples * dec;
+ if (elapsed > 1.0f) {
+ state = gat > 0.0f ? SUSTAIN
+ : (rel < srate ? RELEASE
+ : IDLE);
+ level = sus;
+ samples = 0;
+ } else {
+ level = from_level + elapsed * (sus - from_level);
+ }
+ break;
+ case SUSTAIN:
+ level = sus;
+ break;
+ case RELEASE:
+ samples++;
+ elapsed = (float)samples * rel;
+ if (elapsed > 1.0f) {
+ state = IDLE;
+ level = 0.0f;
+ samples = 0;
+ } else {
+ level = from_level - elapsed * from_level;
+ }
+ break;
+ default:
+ /* Should never happen */
+ level = 0.0f;
+ }
+
+ output[s] = level;
+
+ last_gate = gate[s];
+ last_trigger = trigger[s];
+ }
+
+ plugin->last_gate = last_gate;
+ plugin->last_trigger = last_trigger;
+ plugin->from_level = from_level;
+ plugin->level = level;
+ plugin->state = state;
+ plugin->samples = samples;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/dahdsr",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/difference.c b/src/difference.c
new file mode 100644
index 0000000..9e76e0a
--- /dev/null
+++ b/src/difference.c
@@ -0,0 +1,188 @@
+/*
+ An LV2 plugin to calculate the difference of two signals.
+ Copyright 2011 David Robillard
+ Copyright 2004 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+#include "vector_op.h"
+
+#define DIFFERENCE_MINUEND 0
+#define DIFFERENCE_SUBTRAHEND 1
+#define DIFFERENCE_DIFFERENCE 2
+
+typedef struct {
+ const float* minuend;
+ const float* subtrahend;
+ float* difference;
+ uint32_t minuend_is_cv;
+ uint32_t subtrahend_is_cv;
+ uint32_t difference_is_cv;
+ URIs uris;
+} Difference;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Difference* plugin = (Difference*)instance;
+
+ switch (port) {
+ case DIFFERENCE_MINUEND:
+ plugin->minuend = (const float*)data;
+ break;
+ case DIFFERENCE_SUBTRAHEND:
+ plugin->subtrahend = (const float*)data;
+ break;
+ case DIFFERENCE_DIFFERENCE:
+ plugin->difference = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Difference* plugin = (Difference*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+ switch (o->subject) {
+ case DIFFERENCE_MINUEND:
+ plugin->minuend_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case DIFFERENCE_SUBTRAHEND:
+ plugin->subtrahend_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ plugin->difference_is_cv = plugin->minuend_is_cv || plugin->subtrahend_is_cv;
+ return ret;
+}
+
+static uint32_t
+options_get(LV2_Handle instance,
+ LV2_Options_Option* options)
+{
+ const Difference* plugin = (const Difference*)instance;
+ uint32_t ret = 0;
+ for (LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT ||
+ o->subject != DIFFERENCE_DIFFERENCE) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else {
+ o->size = sizeof(LV2_URID);
+ o->type = plugin->uris.atom_URID;
+ o->value = (plugin->difference_is_cv
+ ? &plugin->uris.lv2_CVPort
+ : &plugin->uris.lv2_ControlPort);
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Difference* plugin = (Difference*)malloc(sizeof(Difference));
+ if (!plugin) {
+ return NULL;
+ }
+
+ plugin->minuend_is_cv = 0;
+ plugin->subtrahend_is_cv = 0;
+ plugin->difference_is_cv = 0;
+
+ map_uris(&plugin->uris, features);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ const Difference* const plugin = (Difference*)instance;
+ const float* const minuend = plugin->minuend;
+ const float* const subtrahend = plugin->subtrahend;
+ float* const difference = plugin->difference;
+
+ VECTOR_OP(-, difference,
+ minuend, plugin->minuend_is_cv,
+ subtrahend, plugin->subtrahend_is_cv);
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { options_get, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/difference",
+ instantiate,
+ connect_port,
+ NULL,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/fmod.c b/src/fmod.c
new file mode 100644
index 0000000..9284d9d
--- /dev/null
+++ b/src/fmod.c
@@ -0,0 +1,199 @@
+/*
+ An LV2 plugin to modulate a frequency by a signal.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "math_func.h"
+#include "uris.h"
+
+#define FMOD_FREQUENCY 0
+#define FMOD_MODULATOR 1
+#define FMOD_OUTPUT 2
+
+typedef struct {
+ const float* frequency;
+ const float* modulator;
+ float* output;
+ uint32_t frequency_is_cv;
+ uint32_t modulator_is_cv;
+ uint32_t output_is_cv;
+ URIs uris;
+} Fmod;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Fmod* plugin = (Fmod*)instance;
+
+ switch (port) {
+ case FMOD_FREQUENCY:
+ plugin->frequency = (const float*)data;
+ break;
+ case FMOD_MODULATOR:
+ plugin->modulator = (const float*)data;
+ break;
+ case FMOD_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Fmod* plugin = (Fmod*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case FMOD_FREQUENCY:
+ plugin->frequency_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case FMOD_MODULATOR:
+ plugin->modulator_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static uint32_t
+options_get(LV2_Handle instance,
+ LV2_Options_Option* options)
+{
+ const Fmod* plugin = (const Fmod*)instance;
+ uint32_t ret = 0;
+ for (LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT || o->subject != FMOD_OUTPUT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else {
+ o->size = sizeof(LV2_URID);
+ o->type = plugin->uris.atom_URID;
+ o->value = (plugin->output_is_cv
+ ? &plugin->uris.lv2_CVPort
+ : &plugin->uris.lv2_ControlPort);
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Fmod* plugin = (Fmod*)malloc(sizeof(Fmod));
+
+ if (plugin) {
+ plugin->frequency_is_cv = 0;
+ plugin->modulator_is_cv = 0;
+ plugin->output_is_cv = 0;
+ map_uris(&plugin->uris, features);
+ }
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Fmod* plugin = (Fmod*)instance;
+
+ /* Frequency to Modulate (array of floats of length 1 or sample_count) */
+ const float* frequency = plugin->frequency;
+
+ /* LFO Input (array of floats of length 1 or sample_count) */
+ const float* modulator = plugin->modulator;
+
+ /* Output Frequency (array of floats of length 1 or sample_count) */
+ float* output = plugin->output;
+
+ if (!plugin->output_is_cv) { /* TODO: Avoid this branch */
+ sample_count = 1;
+ }
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float freq = frequency[s * plugin->frequency_is_cv];
+ const float mod = modulator[s * plugin->modulator_is_cv];
+ const float scale = (float)EXPF(M_LN2 * mod);
+
+ output[s] = scale * freq;
+ }
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { options_get, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/fmod",
+ instantiate,
+ connect_port,
+ NULL,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/include/common.h b/src/include/common.h
new file mode 100644
index 0000000..26a405a
--- /dev/null
+++ b/src/include/common.h
@@ -0,0 +1,54 @@
+/*
+ Common definitions.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef blop_common_h
+#define blop_common_h
+
+#include "math_func.h"
+
+/* Handy constants and macros */
+
+#ifndef SMALLEST_FLOAT
+/** Smallest generated non-zero float, used for pre-empting denormals */
+#define SMALLEST_FLOAT (1.0 / (float)0xFFFFFFFF)
+#endif
+
+/*
+ * Clip without branch (from http://musicdsp.org)
+ */
+
+static inline float
+f_min (float x, float a)
+{
+ return a - (a - x + FABSF (a - x)) * 0.5f;
+}
+
+static inline float
+f_max (float x, float b)
+{
+ return (x - b + FABSF (x - b)) * 0.5f + b;
+}
+
+static inline float
+f_clip (float x, float a, float b)
+{
+ return 0.5f * (FABSF (x - a) + a + b - FABSF (x - b));
+}
+
+#endif /* blop_common_h */
diff --git a/src/include/interpolate.h b/src/include/interpolate.h
new file mode 100644
index 0000000..237008c
--- /dev/null
+++ b/src/include/interpolate.h
@@ -0,0 +1,74 @@
+#ifndef blop_interpolate_h
+#define blop_interpolate_h
+
+#include "blop_config.h"
+#include "math_func.h"
+
+/**
+ Interpolate between p0 and n0 taking the previous (p1) and next (n1) points
+ into account, using a 3rd order polynomial (a.k.a. cubic spline).
+ @param interval Normalised time interval between intepolated sample and p0
+ @param p1 Sample two previous to interpolated one
+ @param p0 Previous sample to interpolated one
+ @param n0 Sample following interpolated one
+ @param n1 Sample following n0
+ @return Interpolated sample.
+
+ Adapted from Steve Harris' plugin code
+ swh-plugins-0.2.7/ladspa-util.h::cube_interp
+ http://plugin.org.uk/releases/0.2.7/
+*/
+static inline float
+interpolate_cubic(float interval,
+ float p1,
+ float p0,
+ float n0,
+ float n1)
+{
+ return p0 + 0.5f * interval * (n0 - p1 +
+ interval * (4.0f * n0 + 2.0f * p1 - 5.0f * p0 - n1 +
+ interval * (3.0f * (p0 - n0) - p1 + n1)));
+}
+
+/**
+ Interpolate between p0 and n0 taking the previous (p1) and next (n1) points
+ into account, using a 5th order polynomial.
+ @param interval Normalised time interval between intepolated sample and p0
+ @param p1 Sample two previous to interpolated one
+ @param p0 Previous sample to interpolated one
+ @param n0 Sample following interpolated one
+ @param n1 Sample following n0
+ @return Interpolated sample.
+
+ Adapted from http://www.musicdsp.org/archive.php?classid=5#62
+*/
+static inline float
+interpolate_quintic(float interval,
+ float p1,
+ float p0,
+ float n0,
+ float n1)
+{
+ return p0 + 0.5f * interval * (n0 - p1 +
+ interval * (n0 - 2.0f * p0 + p1 +
+ interval * ( 9.0f * (n0 - p0) + 3.0f * (p1 - n1) +
+ interval * (15.0f * (p0 - n0) + 5.0f * (n1 - p1) +
+ interval * ( 6.0f * (n0 - p0) + 2.0f * (p1 - n1))))));
+}
+
+/**
+ Linear interpolation
+*/
+static inline float
+f_lerp (float value,
+ float v1,
+ float v2)
+{
+ value -= LRINTF (value - 0.5f);
+ value *= (v2 - v1);
+ value += v1;
+
+ return value;
+}
+
+#endif /* blop_interpolate_h */
diff --git a/src/include/lp4pole_filter.h b/src/include/lp4pole_filter.h
new file mode 100644
index 0000000..310fbbe
--- /dev/null
+++ b/src/include/lp4pole_filter.h
@@ -0,0 +1,137 @@
+/*
+ Header for lp4pole_filter struct, and functions to run instance.
+ Copyright 2011 David Robillard
+ Copyright 2003 Mike Rawes
+
+ Originally originally appeared in CSound as Timo Tossavainen's (sp?)
+ implementation from the Stilson/Smith CCRMA paper.
+
+ See http://musicdsp.org/archive.php?classid=3#26
+
+ Originally appeared in the arts softsynth by Stefan Westerfeld:
+ http://www.arts-project.org/
+
+ First ported to LADSPA by Reiner Klenk (pdq808[at]t-online.de)
+
+ Tuning and stability issues (output NaN) and additional audio-rate
+ variant added by Mike Rawes (mike_rawes[at]yahoo.co.uk)
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef blop_lp4pole_filter_h
+#define blop_lp4pole_filter_h
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "common.h"
+
+typedef struct {
+ float f;
+ float coeff;
+ float fb;
+ float in1;
+ float in2;
+ float in3;
+ float in4;
+ float inv_nyquist;
+ float out1;
+ float out2;
+ float out3;
+ float out4;
+ float max_abs_in;
+} LP4PoleFilter;
+
+/**
+ Allocate a new LP4PoleFilter instance.
+ @param sample_rate Intended playback (DAC) rate
+ @return Allocated LP4PoleFilter instance
+*/
+LP4PoleFilter* lp4pole_new(double sample_rate);
+
+/**
+ Cleanup an existing LP4PoleFilter instance.
+ @param lpf Pointer to LP4PoleFilter instance allocated with initFilter
+*/
+void lp4pole_cleanup(LP4PoleFilter* lpf);
+
+/**
+ Initialise filter.
+ @param lpf Pointer to LP4PoleFilter instance allocated with initFilter
+*/
+void lp4pole_init(LP4PoleFilter* lpf);
+
+/**
+ Set up filter coefficients for given LP4Pole instance.
+ @param lpf Pointer to LP4PoleFilter instance
+ @param cutoff Cutoff frequency in Hz
+ @param resonance Resonance [Min=0.0, Max=4.0]
+*/
+static inline void
+lp4pole_set_params(LP4PoleFilter* lpf,
+ float cutoff,
+ float resonance)
+{
+ float fsqd;
+ float tuning;
+
+ /* Normalise cutoff and find tuning - Magic numbers found empirically :) */
+ lpf->f = cutoff * lpf->inv_nyquist;
+ tuning = f_clip(3.13f - (lpf->f * 4.24703592f), 1.56503274f, 3.13f);
+
+ /* Clip to bounds */
+ lpf->f = f_clip(lpf->f * tuning, lpf->inv_nyquist, 1.16f);
+
+ fsqd = lpf->f * lpf->f;
+ lpf->coeff = fsqd * fsqd * 0.35013f;
+
+ lpf->fb = f_clip(resonance, -1.3f, 4.0f) * (1.0f - 0.15f * fsqd);
+
+ lpf->f = 1.0f - lpf->f;
+}
+
+/**
+ Run given LP4PoleFilter instance for a single sample.
+ @param lpf Pointer to LP4PoleFilter instance
+ @param in Input sample
+ @return Filtered sample
+*/
+static inline float
+lp4pole_run(LP4PoleFilter* lpf,
+ float in)
+{
+ const float abs_in = fabsf(16.0f * in); /* ~24dB unclipped headroom */
+
+ lpf->max_abs_in = f_max(lpf->max_abs_in, abs_in);
+
+ in -= lpf->out4 * lpf->fb;
+ in *= lpf->coeff;
+
+ lpf->out1 = in + 0.3f * lpf->in1 + lpf->f * lpf->out1; /* Pole 1 */
+ lpf->in1 = in;
+ lpf->out2 = lpf->out1 + 0.3f * lpf->in2 + lpf->f * lpf->out2; /* Pole 2 */
+ lpf->in2 = lpf->out1;
+ lpf->out3 = lpf->out2 + 0.3f * lpf->in3 + lpf->f * lpf->out3; /* Pole 3 */
+ lpf->in3 = lpf->out2;
+ lpf->out4 = lpf->out3 + 0.3f * lpf->in4 + lpf->f * lpf->out4; /* Pole 4 */
+ lpf->in4 = lpf->out3;
+
+ /* Simple hard clip to prevent NaN */
+ lpf->out4 = f_clip(lpf->out4, -lpf->max_abs_in, lpf->max_abs_in);
+
+ lpf->max_abs_in *= 0.999f;
+
+ return lpf->out4;
+}
+
+#endif /* blop_lp4pole_filter_h */
diff --git a/src/include/math_func.h b/src/include/math_func.h
new file mode 100644
index 0000000..60f7ce0
--- /dev/null
+++ b/src/include/math_func.h
@@ -0,0 +1,48 @@
+/*
+ * Provide double fallbacks for environments lacking sinf and
+ * friends (e.g. Solaris)
+ */
+
+#ifndef math_func_h
+#define math_func_h
+
+#include <math.h>
+#include "blop_config.h"
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846 /* pi */
+#endif
+
+#ifndef M_LN10
+# define M_LN10 2.30258509299404568402 /* log_e(10) */
+#endif
+
+#ifndef M_LN2
+# define M_LN2 0.69314718055994530942 /* log_e(2) */
+#endif
+
+#ifdef HAVE_SINF
+/* Use float functions */
+#define SINF(x) sinf(x)
+#define COSF(x) cosf(x)
+#define FABSF(x) fabsf(x)
+#define FLOORF(x) floorf(x)
+#define EXPF(x) expf(x)
+#define POWF(x,p) powf(x,p)
+#define COPYSIGNF(s,d) copysignf(s,d)
+#define LRINTF(x) lrintf(x)
+
+#else
+/* Use double functions */
+#define SINF(x) sin(x)
+#define COSF(x) cos(x)
+#define FABSF(x) fabs(x)
+#define FLOORF(x) floor(x)
+#define EXPF(x) exp(x)
+#define POWF(x,p) pow(x)
+#define COPYSIGNF(s,d) copysign(s,d)
+#define LRINTF(x) lrint(x)
+
+#endif
+
+#endif
diff --git a/src/include/uris.h b/src/include/uris.h
new file mode 100644
index 0000000..0c35628
--- /dev/null
+++ b/src/include/uris.h
@@ -0,0 +1,59 @@
+/*
+ Common URIs used by plugins.
+ Copyright 2012 David Robillard
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef blop_uris_h
+#define blop_uris_h
+
+#include <string.h>
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+typedef struct {
+ LV2_URID atom_URID;
+ LV2_URID lv2_AudioPort;
+ LV2_URID lv2_CVPort;
+ LV2_URID lv2_ControlPort;
+ LV2_URID morph_currentType;
+} URIs;
+
+static inline void
+map_uris(URIs* uris,
+ const LV2_Feature* const* features)
+{
+ LV2_URID_Map* map = NULL;
+ for (int i = 0; features[i]; ++i) {
+ if (!strcmp(features[i]->URI, LV2_URID__map)) {
+ map = (LV2_URID_Map*)features[i]->data;
+ break;
+ }
+ }
+
+ if (map) {
+ uris->atom_URID = map->map(map->handle, LV2_ATOM__URID);
+ uris->lv2_AudioPort = map->map(map->handle, LV2_CORE__AudioPort);
+ uris->lv2_CVPort = map->map(map->handle, LV2_CORE__CVPort);
+ uris->lv2_ControlPort = map->map(map->handle, LV2_CORE__ControlPort);
+ uris->morph_currentType = map->map(map->handle, LV2_MORPH__currentType);
+ } else {
+ memset(uris, 0, sizeof(*uris));
+ }
+}
+
+#endif /* blop_uris_h */
diff --git a/src/include/vector_op.h b/src/include/vector_op.h
new file mode 100644
index 0000000..f261ff8
--- /dev/null
+++ b/src/include/vector_op.h
@@ -0,0 +1,44 @@
+/*
+ Apply a C arithmetical operator to two sample buffers.
+ Copyright 2012-2014 David Robillard
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef BLOP_VECTOR_OP_H
+#define BLOP_VECTOR_OP_H
+
+#define VECTOR_OP(op, output, input1, input1_is_cv, input2, input2_is_cv) \
+ switch ((input1_is_cv << 1) + input2_is_cv) { \
+ case 0: /* 00 (control * control) */ \
+ output[0] = input1[0] op input2[0]; \
+ break; \
+ case 1: /* 01 (control * cv) */ \
+ for (uint32_t s = 0; s < sample_count; ++s) { \
+ output[s] = input1[0] op input2[s]; \
+ } \
+ break; \
+ case 2: /* 10 (cv * control) */ \
+ for (uint32_t s = 0; s < sample_count; ++s) { \
+ output[s] = input1[s] op input2[0]; \
+ } \
+ break; \
+ case 3: /* 11 (cv * cv) */ \
+ for (uint32_t s = 0; s < sample_count; ++s) { \
+ output[s] = input1[s] op input2[s]; \
+ } \
+ break; \
+ }
+
+#endif /* BLOP_VECTOR_OP_H */
diff --git a/src/include/wavedata.h b/src/include/wavedata.h
new file mode 100644
index 0000000..97b2986
--- /dev/null
+++ b/src/include/wavedata.h
@@ -0,0 +1,193 @@
+/*
+ Structures to represent a set of wavetables.
+ Copyright 2011 David Robillard
+ Copyright 2003 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef blop_wavedata_h
+#define blop_wavedata_h
+
+#include "blop_config.h"
+#include "math_func.h"
+#include "interpolate.h"
+#include "common.h"
+
+/* Functions identifying wavedata dlls */
+#define BLOP_DLSYM_SAWTOOTH "blop_get_sawtooth"
+#define BLOP_DLSYM_SQUARE "blop_get_square"
+#define BLOP_DLSYM_PARABOLA "blop_get_parabola"
+
+/*
+ * Structure holding a single segment of sample data
+ * along with information relating to playback.
+ */
+typedef struct {
+ unsigned long sample_count; /* Sample count */
+ float* samples_lf; /* Sample data played back at amplitude
+ inversely proportional to frequency */
+ float* samples_hf; /* Sample data played back at amplitude
+ proportional to frequency */
+ unsigned long harmonics; /* Max harmonic content of sample data */
+
+ float phase_scale_factor; /* Phase scale factor for playback */
+ float min_frequency; /* Minimum playback frequency */
+ float max_frequency; /* Maximum playback frequency */
+ float range_scale_factor; /* Blend scale factor for cross fading */
+} Wavetable;
+
+/*
+ * Structure holding the wavetable data and playback state
+ */
+typedef struct {
+ void* data_handle; /* Data DLL handle */
+ unsigned long table_count; /* Number of wavetables in wavedata */
+ Wavetable** tables; /* One or more wavetables, plus pair of
+ extra tables for frequency extremes */
+ unsigned long* lookup; /* Wavetable lookup vector */
+ unsigned long lookup_max; /* For clipping lookup indices */
+
+ float sample_rate; /* Sample rate */
+ float nyquist; /* Nyquist rate (sample_rate / 2) */
+
+ /* Playback state */
+ float frequency; /* Current playback frequency */
+ float abs_freq; /* Absolute playback frequency */
+ float xfade; /* Crossfade factor for fading */
+ Wavetable* table; /* Wavetable to playback */
+} Wavedata;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int wavedata_load(Wavedata* w,
+ const char* bundle_path,
+ const char* lib_name,
+ const char* wdat_descriptor_name,
+ double sample_rate);
+
+void wavedata_unload(Wavedata* w);
+
+/*****************************************************************************
+ * Description: Get interpolated sample from current wavetable in wavedata
+ * at given phase offset
+ *
+ * Arguments: w Wavedata containing playback state and data
+ * phase Phase offset [0.0, sample_rate]
+ *
+ * Returns: Interpolated sample
+ *
+ * Notes: Cubic (or quintic) interpolation requires four consecutive
+ * samples for operation:
+ *
+ * phase
+ * :
+ * p1 p0 : n0 n1
+ * | | x | |
+ * : :
+ * <-o->
+ * :
+ * interval
+ *
+ * Phase values less than one make p0 the first sample in
+ * the table - p1 will be the last sample, as a previous
+ * sample does not exist. To avoid checking for this case,
+ * a copy of the last sample is stored before the first
+ * sample in each table.
+ * Likewise, if the phase means p0 is the last sample, n0
+ * and n1 will be the first and second samples respectively.
+ * Copies of these samples are stored after the last sample
+ * in each table.
+ *
+ *****************************************************************************/
+static inline float
+wavedata_get_sample(Wavedata* w,
+ float phase)
+{
+ float* samples_hf = w->table->samples_hf;
+ float* samples_lf = w->table->samples_lf;
+ float p1, p0, n0, n1;
+ float phase_f;
+ long int index;
+
+ /* Scale phase to map to position in wavetable */
+ phase *= w->table->phase_scale_factor;
+
+ /* Get position of first contributing sample (p1) */
+ index = LRINTF((float)phase - 0.5f);
+ phase_f = (float)index;
+
+ index %= w->table->sample_count;
+
+ /* Cross-fade table pairs */
+ /* Previous two samples */
+ p1 = w->xfade * (samples_lf[index] - samples_hf[index]) + samples_hf[index];
+ index++;
+ p0 = w->xfade * (samples_lf[index] - samples_hf[index]) + samples_hf[index];
+ index++;
+ /* Next two samples */
+ n0 = w->xfade * (samples_lf[index] - samples_hf[index]) + samples_hf[index];
+ index++;
+ n1 = w->xfade * (samples_lf[index] - samples_hf[index]) + samples_hf[index];
+
+ /* Return interpolated sample */
+ return interpolate_cubic(phase - phase_f, p1, p0, n0, n1);
+}
+
+/*****************************************************************************
+ * Description: Get wavetable to use for playback frequency.
+ *
+ * Arguments: w Wavedata object (contains all table info)
+ * frequency Playback frequency
+ *
+ * Notes: The lookup vector used to determine the wavetable
+ * is indexed by harmonic number.
+ *
+ * The maximum playback frequency for a wavetable is
+ * determined by its harmonic content and the sample rate,
+ * and equals sample_rate / 2 / max_harmonic_in_table.
+ *
+ *****************************************************************************/
+static inline void
+wavedata_get_table(Wavedata* w,
+ float frequency)
+{
+ unsigned long harmonic;
+
+ w->frequency = frequency;
+ w->abs_freq = (float)FABSF((float)frequency);
+
+ /* Get highest harmonic possible in frequency */
+ harmonic = LRINTF(w->nyquist / w->abs_freq - 0.5f);
+
+ /* Clip so lookup is within bounds */
+ if (harmonic > w->lookup_max) {
+ harmonic = w->lookup_max;
+ }
+
+ /* Set playback table */
+ w->table = w->tables[w->lookup[harmonic]];
+
+ /* Get cross fade factor */
+ w->xfade = f_max(w->table->max_frequency - w->abs_freq, 0.0f) * w->table->range_scale_factor;
+ w->xfade = f_min(w->xfade, 1.0f);
+}
+
+#ifdef __cplusplus
+} /* extern "C" { */
+#endif
+
+#endif /* blop_wavedata_h */
diff --git a/src/include/wdatutil.h b/src/include/wdatutil.h
new file mode 100644
index 0000000..9a0810a
--- /dev/null
+++ b/src/include/wdatutil.h
@@ -0,0 +1,141 @@
+/*
+ Code to generate wavedata dl containing pre-calculated wavetables.
+ Copyright 2011 David Robillard
+ Copyright 2003 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef blop_wdatutil_h
+#define blop_wdatutil_h
+
+#include <stdio.h>
+#include <stdint.h>
+#include "math_func.h"
+#include "wavedata.h"
+
+#define WAVE_TYPE_COUNT 3
+
+extern const char* wave_names[];
+extern const char* wave_descriptions[];
+extern unsigned long wave_first_harmonics[];
+extern unsigned long wave_harmonic_intervals[];
+
+/** Get actual maximum harmonic from given harmonic, h, and wavetype, w */
+#define ACTUAL_HARM(h, w) h - (h - wave_first_harmonics[w]) % wave_harmonic_intervals[w]
+
+/** Get minimum harmonic content in given wavetype, w */
+#define MIN_HARM(w) wave_first_harmonics[w]
+
+/** Get minimum extra harmonic content possible in given wavetype, w */
+#define MIN_EXTRA_HARM(w) wave_harmonic_intervals[w]
+
+/** Get frequency from MIDI note, n */
+#define FREQ_FROM_NOTE(n) 6.875f * POWF(2.0f, (float)(n + 3) / 12.0f)
+
+/** Get max harmonic from given frequency, f, at sample rate, r */
+#define HARM_FROM_FREQ(f, r) (unsigned long)((float)r / f / 2.0f)
+
+/*
+ * A single wavetable will have a range of pitches at which their samples
+ * may be played back.
+ *
+ * The upper bound is determined by the maximum harmonic present in the
+ * waveform - above this frequency, the higher harmonics will alias.
+ *
+ * The lower bound is chosen to be the higher bound of the previous wavetable
+ * (or a pre-defined limit if there is no such table).
+ */
+
+typedef enum {
+ SAW,
+ SQUARE,
+ PARABOLA
+} Wavetype;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*******************************************************************************
+ * Description: Allocate new wavedata struct
+ *
+ * Arguments: sample_rate Sample rate to use when generating data
+ *
+ * Returns: Pointer to wavedata on success
+ * NULL (0) on failure
+ *
+ * Notes: No wavetables are allocated. Use wavedata_add_table
+ ******************************************************************************/
+Wavedata* wavedata_new(double sample_rate);
+
+/*******************************************************************************
+ * Description: Destroy allocated wavedata and any tables
+ *
+ * Arguments: w Wavedata struct to cleanup
+ ******************************************************************************/
+void wavedata_cleanup(Wavedata* w);
+
+/*******************************************************************************
+ * Description: Add new wavetable information to wavedata file object
+ *
+ * Arguments: w Wavedata to add table to
+ * sample_count Number of samples in wavetable
+ * harmonics Maximum harmonics present in table
+ *
+ * Returns: 0 on success
+ * -1 otherwise
+ ******************************************************************************/
+int wavedata_add_table(Wavedata* w,
+ uint32_t sample_count,
+ unsigned long harmonics);
+
+/*******************************************************************************
+ * Description: Initialise all wavetables in wavedata with a waveform
+ * generated from Fourier series.
+ *
+ * Arguments: w Wavedata to generate data for
+ * wavetype Wavetype to generate
+ * gibbs_comp Compensation for Gibbs' effect:
+ * 0.0: none (wave will overshoot)
+ * 1.0: full (wave will not overshoot)
+ *
+ * Notes: Compensation for Gibbs' Effect will reduce the degree
+ * of overshoot and ripple at the transitions. A value of 1.0
+ * will pretty much eliminate it.
+ ******************************************************************************/
+void wavedata_generate_tables(Wavedata* w,
+ Wavetype wavetype,
+ float gibbs_comp);
+
+/*******************************************************************************
+ * Description: Write wavedata as a c header file
+ *
+ * Arguments: w Wavedata to write
+ * wdat_fp Pointer to output file
+ * prefix Prefix to use in declarations. If this is null
+ * declarations are prefixed with 'wdat'.
+ *
+ * Returns: 0 on success
+ * -1 otherwise
+ ******************************************************************************/
+int wavedata_write(Wavedata* w,
+ FILE* wdat_fp,
+ const char* prefix);
+
+#ifdef __cplusplus
+} /* extern "C" { */
+#endif
+
+#endif /* blop_wdatutil_h */
diff --git a/src/interpolator.c b/src/interpolator.c
new file mode 100644
index 0000000..e47fbe3
--- /dev/null
+++ b/src/interpolator.c
@@ -0,0 +1,144 @@
+/*
+ An LV2 plugin to generate a smooth audio signal from a control source.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define INTERPOLATOR_INPUT 0
+#define INTERPOLATOR_OUTPUT 1
+
+/**
+ Mutated spline interpolator using only two previous samples and one next.
+ @param interval Normalised time interval between inteprolated sample and p0
+ @param p1 Sample two previous to interpolated one
+ @param p0 Previous sample to interpolated one
+ @param n0 Sample following interpolated one
+ @return Interpolated sample.
+*/
+static inline float
+interpolate(float interval,
+ float p1,
+ float p0,
+ float n0)
+{
+ return p0 + 0.5f * interval * (n0 - p1 +
+ interval * (4.0f * n0 + 2.0f * p1 - 5.0f * p0 - n0 +
+ interval * (3.0f * (p0 - n0) - p1 + n0)));
+}
+
+typedef struct {
+ const float* input;
+ float* output;
+ float p1;
+ float p0;
+} Interpolator;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Interpolator* plugin = (Interpolator*)instance;
+
+ switch (port) {
+ case INTERPOLATOR_INPUT:
+ plugin->input = (const float*)data;
+ break;
+ case INTERPOLATOR_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Interpolator* plugin = (Interpolator*)malloc(sizeof(Interpolator));
+ if (!plugin) {
+ return NULL;
+ }
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Interpolator* plugin = (Interpolator*)instance;
+
+ plugin->p1 = 0.0f;
+ plugin->p0 = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Interpolator* plugin = (Interpolator*)instance;
+
+ /* Control Input (float value) */
+ const float input = *(plugin->input);
+
+ /* Interpolated Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* We use two previous values and the input as the 'next' one */
+ float p1 = plugin->p1;
+ float p0 = plugin->p0;
+
+ const float inv_scount = 1.0f / (float)sample_count;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float interval = (float)s * inv_scount;
+
+ output[s] = interpolate(interval, p1, p0, input);
+ }
+
+ plugin->p1 = p0;
+ plugin->p0 = input;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/interpolator",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ NULL,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/lp4pole.c b/src/lp4pole.c
new file mode 100644
index 0000000..7d2eb6b
--- /dev/null
+++ b/src/lp4pole.c
@@ -0,0 +1,206 @@
+/*
+ An LV2 plugin simulating a 4 pole low pass resonant filter.
+ Copyright 2011 David Robillard
+ Copyright 2003 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "lp4pole_filter.h"
+#include "common.h"
+#include "uris.h"
+
+#define LP4POLE_CUTOFF 0
+#define LP4POLE_RESONANCE 1
+#define LP4POLE_INPUT 2
+#define LP4POLE_OUTPUT 3
+
+typedef struct {
+ const float* cutoff;
+ const float* resonance;
+ const float* input;
+ float* output;
+ LP4PoleFilter* lpf;
+ uint32_t cutoff_is_cv;
+ uint32_t resonance_is_cv;
+ URIs uris;
+} Lp4pole;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ Lp4pole* plugin = (Lp4pole*)instance;
+
+ lp4pole_cleanup(plugin->lpf);
+
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Lp4pole* plugin = (Lp4pole*)instance;
+
+ switch (port) {
+ case LP4POLE_CUTOFF:
+ plugin->cutoff = (const float*)data;
+ break;
+ case LP4POLE_RESONANCE:
+ plugin->resonance = (const float*)data;
+ break;
+ case LP4POLE_INPUT:
+ plugin->input = (const float*)data;
+ break;
+ case LP4POLE_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Lp4pole* plugin = (Lp4pole*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case LP4POLE_CUTOFF:
+ plugin->cutoff_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case LP4POLE_RESONANCE:
+ plugin->resonance_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Lp4pole* plugin = (Lp4pole*)malloc(sizeof(Lp4pole));
+
+ if (plugin) {
+ plugin->lpf = lp4pole_new(sample_rate);
+ if (!plugin->lpf) {
+ free(plugin);
+ return NULL;
+ }
+ plugin->cutoff_is_cv = 0;
+ plugin->resonance_is_cv = 0;
+ map_uris(&plugin->uris, features);
+ }
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Lp4pole* plugin = (Lp4pole*)instance;
+
+ lp4pole_init(plugin->lpf);
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Lp4pole* plugin = (Lp4pole*)instance;
+
+ /* Cutoff Frequency (array of floats of length 1 or sample_count) */
+ const float* cutoff = plugin->cutoff;
+
+ /* Resonance (array of floats of length 1 or sample_count) */
+ const float* resonance = plugin->resonance;
+
+ /* Input (array of floats of length sample_count) */
+ const float* input = plugin->input;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ LP4PoleFilter* lpf = plugin->lpf;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float co = cutoff[s * plugin->cutoff_is_cv];
+ const float res = resonance[s * plugin->resonance_is_cv];
+ const float in = input[s];
+
+ /* TODO: There is no branching in this function.
+ Would it actually be faster to check if co or res has changed?
+ */
+ lp4pole_set_params(lpf, co, res);
+
+ output[s] = lp4pole_run(lpf, in);
+ }
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/lp4pole",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/lp4pole_filter.c b/src/lp4pole_filter.c
new file mode 100644
index 0000000..7d54691
--- /dev/null
+++ b/src/lp4pole_filter.c
@@ -0,0 +1,55 @@
+/*
+ lp4pole filter admin.
+ Copyright 2011 David Robillard
+ Copyright 2003 Mike Rawes
+
+ See lp4pole_filter.h for history
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "lp4pole_filter.h"
+
+LP4PoleFilter*
+lp4pole_new(double sample_rate)
+{
+ LP4PoleFilter* lpf;
+
+ lpf = (LP4PoleFilter*)malloc(sizeof(LP4PoleFilter));
+
+ if (lpf) {
+ lpf->inv_nyquist = 2.0f / sample_rate;
+ lp4pole_init(lpf);
+ }
+
+ return lpf;
+}
+
+void
+lp4pole_cleanup(LP4PoleFilter* lpf)
+{
+ if (lpf) {
+ free(lpf);
+ }
+}
+
+void
+lp4pole_init(LP4PoleFilter* lpf)
+{
+ lpf->in1 = lpf->in2 = lpf->in3 = lpf->in4 = 0.0f;
+ lpf->out1 = lpf->out2 = lpf->out3 = lpf->out4 = 0.0f;
+ lpf->max_abs_in = 0.0f;
+}
diff --git a/src/product.c b/src/product.c
new file mode 100644
index 0000000..007d853
--- /dev/null
+++ b/src/product.c
@@ -0,0 +1,187 @@
+/*
+ An LV2 plugin to calculate the product of two signals.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+#include "vector_op.h"
+
+#define PRODUCT_MULTIPLICAND 0
+#define PRODUCT_MULTIPLIER 1
+#define PRODUCT_PRODUCT 2
+
+typedef struct {
+ const float* input1;
+ const float* input2;
+ float* output;
+ uint32_t input1_is_cv;
+ uint32_t input2_is_cv;
+ uint32_t output_is_cv;
+ URIs uris;
+} Product;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Product* plugin = (Product*)instance;
+
+ switch (port) {
+ case PRODUCT_MULTIPLICAND:
+ plugin->input1 = (const float*)data;
+ break;
+ case PRODUCT_MULTIPLIER:
+ plugin->input2 = (const float*)data;
+ break;
+ case PRODUCT_PRODUCT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Product* plugin = (Product*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+ switch (o->subject) {
+ case PRODUCT_MULTIPLICAND:
+ plugin->input1_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case PRODUCT_MULTIPLIER:
+ plugin->input2_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ plugin->output_is_cv = plugin->input1_is_cv || plugin->input2_is_cv;
+ return ret;
+}
+
+static uint32_t
+options_get(LV2_Handle instance,
+ LV2_Options_Option* options)
+{
+ const Product* plugin = (const Product*)instance;
+ uint32_t ret = 0;
+ for (LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT || o->subject != PRODUCT_PRODUCT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else {
+ o->size = sizeof(LV2_URID);
+ o->type = plugin->uris.atom_URID;
+ o->value = (plugin->output_is_cv
+ ? &plugin->uris.lv2_CVPort
+ : &plugin->uris.lv2_ControlPort);
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Product* plugin = (Product*)malloc(sizeof(Product));
+ if (!plugin) {
+ return NULL;
+ }
+
+ plugin->input1_is_cv = 0;
+ plugin->input2_is_cv = 0;
+ plugin->output_is_cv = 0;
+
+ map_uris(&plugin->uris, features);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ const Product* const plugin = (Product*)instance;
+ const float* const input1 = plugin->input1;
+ const float* const input2 = plugin->input2;
+ float* const output = plugin->output;
+
+ VECTOR_OP(*, output,
+ input1, plugin->input1_is_cv,
+ input2, plugin->input2_is_cv);
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { options_get, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/product",
+ instantiate,
+ connect_port,
+ NULL,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/pulse.c b/src/pulse.c
new file mode 100644
index 0000000..29e1001
--- /dev/null
+++ b/src/pulse.c
@@ -0,0 +1,225 @@
+/*
+ An LV2 plugin to generate a bandlimited variable pulse waveform.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+#include "wavedata.h"
+
+#define PULSE_FREQUENCY 0
+#define PULSE_PULSEWIDTH 1
+#define PULSE_OUTPUT 2
+
+typedef struct {
+ const float* frequency;
+ const float* pulsewidth;
+ float* output;
+ float phase;
+ uint32_t frequency_is_cv;
+ uint32_t pulsewidth_is_cv;
+ Wavedata wdat;
+ URIs uris;
+} Pulse;
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Pulse* plugin = (Pulse*)instance;
+
+ switch (port) {
+ case PULSE_FREQUENCY:
+ plugin->frequency = (const float*)data;
+ break;
+ case PULSE_PULSEWIDTH:
+ plugin->pulsewidth = (const float*)data;
+ break;
+ case PULSE_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Pulse* plugin = (Pulse*)malloc(sizeof(Pulse));
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case PULSE_FREQUENCY:
+ plugin->frequency_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case PULSE_PULSEWIDTH:
+ plugin->pulsewidth_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Pulse* plugin = (Pulse*)malloc(sizeof(Pulse));
+ if (!plugin) {
+ return NULL;
+ }
+
+ if (wavedata_load(&plugin->wdat, bundle_path, "sawtooth_data",
+ BLOP_DLSYM_SAWTOOTH, sample_rate)) {
+ free(plugin);
+ return 0;
+ }
+
+ plugin->frequency_is_cv = 0;
+ plugin->pulsewidth_is_cv = 0;
+ map_uris(&plugin->uris, features);
+ wavedata_get_table(&plugin->wdat, 440.0);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ Pulse* plugin = (Pulse*)instance;
+
+ wavedata_unload(&plugin->wdat);
+ free(instance);
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Pulse* plugin = (Pulse*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Pulse* plugin = (Pulse*)instance;
+
+ /* Frequency (array of float of length sample_count) */
+ const float* frequency = plugin->frequency;
+
+ /* Pulse Width (array of float of length sample_count) */
+ const float* pulsewidth = plugin->pulsewidth;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+
+ float last_pwidth = pulsewidth[0];
+ float pwidth = f_clip(last_pwidth, 0.0f, 1.0f);
+ float dc_shift = 1.0 - (2.0 * pwidth);
+ float phase_shift = pwidth * wdat->sample_rate;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float freq = frequency[s * plugin->frequency_is_cv];
+ if (freq != wdat->frequency) {
+ /* Frequency changed, look up table to play */
+ wavedata_get_table(wdat, freq);
+ }
+
+ const float this_pwidth = pulsewidth[s * plugin->pulsewidth_is_cv];
+ if (this_pwidth != last_pwidth) {
+ /* Pulsewidth changed, recalculate */
+ last_pwidth = this_pwidth;
+ pwidth = f_clip(this_pwidth, 0.0f, 1.0f);
+ dc_shift = 1.0f - (2.0f * pwidth);
+ phase_shift = pwidth * wdat->sample_rate;
+ }
+
+ /* Get samples from sawtooth and phase shifted inverted sawtooth,
+ with approriate DC offset */
+ output[s] = wavedata_get_sample(wdat, phase)
+ - wavedata_get_sample(wdat, phase + phase_shift)
+ + dc_shift;
+
+ /* Update phase, wrapping if necessary */
+ phase += wdat->frequency;
+ if (phase < 0.0f) {
+ phase += wdat->sample_rate;
+ } else if (phase > wdat->sample_rate) {
+ phase -= wdat->sample_rate;
+ }
+ }
+ plugin->phase = phase;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/pulse",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/quantiser.c b/src/quantiser.c
new file mode 100644
index 0000000..ed3ae1d
--- /dev/null
+++ b/src/quantiser.c
@@ -0,0 +1,461 @@
+/*
+ An LV2 plugin to quantise an input to a set of fixed values.
+ Copyright 2011 David Robillard
+ Copyright 2003 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "math_func.h"
+#include "common.h"
+
+#define QUANTISER_RANGE_MIN 0
+#define QUANTISER_RANGE_MAX 1
+#define QUANTISER_MATCH_RANGE 2
+#define QUANTISER_MODE 3
+#define QUANTISER_COUNT 4
+#define QUANTISER_VALUE_START 5
+#define QUANTISER_INPUT (QUANTISER_MAX_INPUTS + 5)
+#define QUANTISER_OUTPUT (QUANTISER_MAX_INPUTS + 6)
+#define QUANTISER_OUTPUT_CHANGED (QUANTISER_MAX_INPUTS + 7)
+
+typedef struct {
+ const float* min;
+ const float* max;
+ const float* match_range;
+ const float* mode;
+ const float* count;
+ const float* values[QUANTISER_MAX_INPUTS];
+ const float* input;
+ float* output_changed;
+ float* output;
+ float svalues[QUANTISER_MAX_INPUTS + 2];
+ float temp[QUANTISER_MAX_INPUTS + 2];
+ float last_found;
+} Quantiser;
+
+/*
+ * f <= m <= l
+*/
+static inline void
+merge(float* v,
+ int f,
+ int m,
+ int l,
+ float* temp)
+{
+ int f1 = f;
+ int l1 = m;
+ int f2 = m + 1;
+ int l2 = l;
+ int i = f1;
+
+ while ((f1 <= l1) && (f2 <= l2)) {
+ if (v[f1] < v[f2]) {
+ temp[i] = v[f1];
+ f1++;
+ } else {
+ temp[i] = v[f2];
+ f2++;
+ }
+ i++;
+ }
+ while (f1 <= l1) {
+ temp[i] = v[f1];
+ f1++;
+ i++;
+ }
+ while (f2 <= l2) {
+ temp[i] = v[f2];
+ f2++;
+ i++;
+ }
+ for (i = f; i <= l; i++) {
+ v[i] = temp[i];
+ }
+}
+
+/*
+ * Recursive Merge Sort
+ * Sort elements in unsorted vector v
+*/
+static inline void
+msort(float* v,
+ int f,
+ int l,
+ float* temp)
+{
+ int m;
+
+ if (f < l) {
+ m = (f + l) / 2;
+ msort(v, f, m, temp);
+ msort(v, m + 1, l, temp);
+ merge(v, f, m, l, temp);
+ }
+}
+
+/*
+ * Binary search for nearest match to sought value in
+ * ordered vector v of given size
+*/
+static inline int
+fuzzy_bsearch(float* v,
+ int size,
+ float sought)
+{
+ int f = 0;
+ int l = size - 1;
+ int m = ((l - f) / 2);
+
+ while ((l - f) > 1) {
+ if (v[m] < sought) {
+ f = (l - f) / 2 + f;
+ } else {
+ l = (l - f) / 2 + f;
+ }
+
+ m = ((l - f) / 2 + f);
+ }
+
+ if (sought < v[m]) {
+ if (m > 0) {
+ if (FABSF(v[m] - sought) > FABSF(v[m - 1] - sought)) {
+ m--;
+ }
+ }
+ } else if (m < size - 1) {
+ if (FABSF(v[m] - sought) > FABSF(v[m + 1] - sought)) {
+ m++;
+ }
+ }
+
+ return m;
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Quantiser* plugin = (Quantiser*)instance;
+
+ plugin->last_found = 0.0f;
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Quantiser* plugin = (Quantiser*)instance;
+
+ switch (port) {
+ case QUANTISER_RANGE_MIN:
+ plugin->min = (const float*)data;
+ break;
+ case QUANTISER_RANGE_MAX:
+ plugin->max = (const float*)data;
+ break;
+ case QUANTISER_MATCH_RANGE:
+ plugin->match_range = (const float*)data;
+ break;
+ case QUANTISER_MODE:
+ plugin->mode = (const float*)data;
+ break;
+ case QUANTISER_COUNT:
+ plugin->count = (const float*)data;
+ break;
+ case QUANTISER_INPUT:
+ plugin->input = (const float*)data;
+ break;
+ case QUANTISER_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ case QUANTISER_OUTPUT_CHANGED:
+ plugin->output_changed = (float*)data;
+ break;
+ default:
+ if (port >= QUANTISER_VALUE_START && port < QUANTISER_OUTPUT) {
+ plugin->values[port - QUANTISER_VALUE_START] = (float*)data;
+ }
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Quantiser* plugin = (Quantiser*)malloc(sizeof(Quantiser));
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Quantiser* plugin = (Quantiser*)instance;
+
+ /* Range Min (float value) */
+ float min = *(plugin->min);
+
+ /* Range Max (float value) */
+ float max = *(plugin->max);
+
+ /* Match Range (float value) */
+ const float match_range = FABSF(*(plugin->match_range));
+
+ /* Mode (float value) */
+ const float mode = *(plugin->mode);
+
+ /* Count (float value) */
+ const float count = *(plugin->count);
+
+ /* Input (array of float of length sample_count) */
+ const float* input = plugin->input;
+
+ /* Output (array of float of length sample_count) */
+ float* output = plugin->output;
+
+ /* Output Changed (array of float of length sample_count) */
+ float* output_changed = plugin->output_changed;
+
+ /* Instance Data */
+ float* temp = plugin->temp;
+ float* values = plugin->svalues;
+ float last_found = plugin->last_found;
+
+ int md = LRINTF(mode);
+ int n = LRINTF(count);
+ int i;
+ float in;
+ float out_changed;
+ float range;
+ float offset;
+ float found = last_found;
+ int found_index = 0;
+
+ /* Clip count if out of range */
+ n = n < 1 ? 1 : (n > QUANTISER_MAX_INPUTS ? QUANTISER_MAX_INPUTS : n);
+
+ /* Swap min and max if wrong way around */
+ if (min > max) {
+ range = min;
+ min = max;
+ max = range;
+ }
+ range = max - min;
+
+ /* Copy and sort required values */
+ for (i = 0; i < n; i++) {
+ values[i + 1] = *(plugin->values[i]);
+ }
+
+ msort(values, 1, n, temp);
+
+ /* Add wrapped extremes */
+ values[0] = values[n] - range;
+ values[n + 1] = values[1] + range;
+
+ if (md < 1) {
+ /* Extend mode */
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in = input[s];
+
+ if (range > 0.0f) {
+ if ((in < min) || (in > max)) {
+ offset = FLOORF((in - max) / range) + 1.0f;
+ offset *= range;
+ in -= offset;
+
+ /* Quantise */
+ found_index = fuzzy_bsearch(values, n + 2, in);
+
+ /* Wrapped */
+ if (found_index == 0) {
+ found_index = n;
+ offset -= range;
+ } else if (found_index == n + 1) {
+ found_index = 1;
+ offset += range;
+ }
+
+ found = values[found_index];
+
+ /* Allow near misses */
+ if (match_range > 0.0f) {
+ if (in < (found - match_range)) {
+ found -= match_range;
+ } else if (in > (found + match_range)) {
+ found += match_range;
+ }
+ }
+ found += offset;
+ } else {
+ /* Quantise */
+ found_index = fuzzy_bsearch(values, n + 2, in);
+ if (found_index == 0) {
+ found_index = n;
+ found = values[n] - range;
+ } else if (found_index == n + 1) {
+ found_index = 1;
+ found = values[1] + range;
+ } else {
+ found = values[found_index];
+ }
+
+ if (match_range > 0.0f) {
+ if (in < (found - match_range)) {
+ found -= match_range;
+ } else if (in > (found + match_range)) {
+ found += match_range;
+ }
+ }
+ }
+ } else {
+ /* Min and max the same, so only one value to quantise to */
+ found = min;
+ }
+
+ /* Has quantised output changed? */
+ if (FABSF(found - last_found) > 2.0001f * match_range) {
+ out_changed = 1.0f;
+ last_found = found;
+ } else {
+ out_changed = 0.0f;
+ }
+
+ output[s] = found;
+ output_changed[s] = out_changed;
+ }
+ } else if (md == 1) {
+ /* Wrap mode */
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in = input[s];
+
+ if (range > 0.0f) {
+ if ((in < min) || (in > max)) {
+ in -= (FLOORF((in - max) / range) + 1.0f) * range;
+ }
+
+ /* Quantise */
+ found_index = fuzzy_bsearch(values, n + 2, in);
+ if (found_index == 0) {
+ found_index = n;
+ } else if (found_index == n + 1) {
+ found_index = 1;
+ }
+
+ found = values[found_index];
+
+ /* Allow near misses */
+ if (match_range > 0.0f) {
+ if (in < (found - match_range)) {
+ found -= match_range;
+ } else if (in > (found + match_range)) {
+ found += match_range;
+ }
+ }
+ } else {
+ /* Min and max the same, so only one value to quantise to */
+ found = min;
+ }
+
+ /* Has quantised output changed? */
+ if (FABSF(found - last_found) > match_range) {
+ out_changed = 1.0f;
+ last_found = found;
+ } else {
+ out_changed = 0.0f;
+ }
+
+ output[s] = found;
+ output_changed[s] = out_changed;
+ }
+ } else {
+ /* Clip mode */
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in = input[s];
+
+ if (range > 0.0f) {
+ /* Clip to range */
+ if (in < min) {
+ found_index = 1;
+ } else if (in > max) {
+ found_index = n;
+ } else {
+ /* Quantise */
+ found_index = fuzzy_bsearch(values + 1, n, in) + 1;
+ }
+
+ found = values[found_index];
+
+ /* Allow near misses */
+ if (match_range > 0.0f) {
+ if (in < (found - match_range)) {
+ found -= match_range;
+ } else if (in > (found + match_range)) {
+ found += match_range;
+ }
+ }
+ } else {
+ /* Min and max the same, so only one value to quantise to */
+ found = min;
+ }
+
+ /* Has quantised output changed? */
+ if (FABSF(found - last_found) > match_range) {
+ out_changed = 1.0f;
+ last_found = found;
+ } else {
+ out_changed = 0.0f;
+ }
+
+ output[s] = found;
+ output_changed[s] = out_changed;
+ }
+ }
+ plugin->last_found = last_found;
+}
+
+static const LV2_Descriptor descriptor = {
+ QUANTISER_URI,
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ NULL
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/random.c b/src/random.c
new file mode 100644
index 0000000..45d8de8
--- /dev/null
+++ b/src/random.c
@@ -0,0 +1,237 @@
+/*
+ An LV2 plugin to generate a random wave of varying frequency and smoothness.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include <time.h>
+#include "math_func.h"
+#include "common.h"
+#include "uris.h"
+
+#define RANDOM_FREQUENCY 0
+#define RANDOM_SMOOTH 1
+#define RANDOM_OUTPUT 2
+
+typedef struct {
+ const float* frequency;
+ const float* smooth;
+ float* output;
+ float nyquist;
+ float inv_nyquist;
+ float phase;
+ float value1;
+ float value2;
+ uint32_t frequency_is_cv;
+ uint32_t smooth_is_cv;
+ URIs uris;
+} Random;
+
+float inv_rand_max;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Random* plugin = (Random*)instance;
+
+ switch (port) {
+ case RANDOM_FREQUENCY:
+ plugin->frequency = (const float*)data;
+ break;
+ case RANDOM_SMOOTH:
+ plugin->smooth = (const float*)data;
+ break;
+ case RANDOM_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Random* plugin = (Random*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case RANDOM_FREQUENCY:
+ plugin->frequency_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case RANDOM_SMOOTH:
+ plugin->smooth_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Random* plugin = (Random*)malloc(sizeof(Random));
+ if (!plugin) {
+ return NULL;
+ }
+
+ srand((int)time((time_t*)0));
+
+ inv_rand_max = 2.0f / (float)RAND_MAX;
+
+ plugin->nyquist = (float)sample_rate / 2.0f;
+ plugin->inv_nyquist = 1.0f / plugin->nyquist;
+
+ plugin->value1 = rand() * inv_rand_max - 1.0f;
+ plugin->value2 = rand() * inv_rand_max - 1.0f;
+
+ plugin->frequency_is_cv = 0;
+ plugin->smooth_is_cv = 0;
+
+ map_uris(&plugin->uris, features);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Random* plugin = (Random*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Random* plugin = (Random*)instance;
+
+ /* Frequency (Hz) (array of floats of length 1 or sample_count) */
+ const float* frequency = plugin->frequency;
+
+ /* Wave smoothness (array of floats of length 1 or sample_count) */
+ const float* smooth = plugin->smooth;
+
+ /* Output (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ float nyquist = plugin->nyquist;
+ float inv_nyquist = plugin->inv_nyquist;
+ float phase = plugin->phase;
+ float value1 = plugin->value1;
+ float value2 = plugin->value2;
+
+ float result;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float freq = f_clip(frequency[s * plugin->frequency_is_cv],
+ 0.0f, nyquist);
+
+ const float smth = f_clip(smooth[s * plugin->smooth_is_cv],
+ 0.0f, 1.0f);
+
+ const float interval = (1.0f - smth) * 0.5f;
+
+ if (phase < interval) {
+ result = 1.0f;
+ } else if (phase > (1.0f - interval)) {
+ result = -1.0f;
+ } else if (interval > 0.0f) {
+ result = COSF((phase - interval) / smth * M_PI);
+ } else {
+ result = COSF(phase * M_PI);
+ }
+
+ result *= (value2 - value1) * 0.5f;
+ result -= (value2 + value1) * 0.5f;
+
+ output[s] = result;
+
+ phase += freq * inv_nyquist;
+ if (phase > 1.0f) {
+ phase -= 1.0f;
+ value1 = value2;
+ value2 = (float)rand() * inv_rand_max - 1.0f;
+ }
+ }
+
+ plugin->phase = phase;
+ plugin->value1 = value1;
+ plugin->value2 = value2;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/random",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/ratio.c b/src/ratio.c
new file mode 100644
index 0000000..30873c2
--- /dev/null
+++ b/src/ratio.c
@@ -0,0 +1,202 @@
+/*
+ An LV2 plugin to calculate the ratio of two signals.
+ Copyright 2011 David Robillard
+ Copyright 2004 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "math_func.h"
+#include "common.h"
+#include "uris.h"
+
+#define RATIO_NUMERATOR 0
+#define RATIO_DENOMINATOR 1
+#define RATIO_OUTPUT 2
+
+typedef struct {
+ const float* numerator;
+ const float* denominator;
+ float* output;
+ uint32_t numerator_is_cv;
+ uint32_t denominator_is_cv;
+ uint32_t output_is_cv;
+ URIs uris;
+} Ratio;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Ratio* plugin = (Ratio*)instance;
+
+ switch (port) {
+ case RATIO_NUMERATOR:
+ plugin->numerator = (const float*)data;
+ break;
+ case RATIO_DENOMINATOR:
+ plugin->denominator = (const float*)data;
+ break;
+ case RATIO_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Ratio* plugin = (Ratio*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+ switch (o->subject) {
+ case RATIO_NUMERATOR:
+ plugin->numerator_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case RATIO_DENOMINATOR:
+ plugin->denominator_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ plugin->output_is_cv = plugin->numerator_is_cv || plugin->denominator_is_cv;
+ return ret;
+}
+
+static uint32_t
+options_get(LV2_Handle instance,
+ LV2_Options_Option* options)
+{
+ const Ratio* plugin = (const Ratio*)instance;
+ uint32_t ret = 0;
+ for (LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT || o->subject != RATIO_OUTPUT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else {
+ o->size = sizeof(LV2_URID);
+ o->type = plugin->uris.atom_URID;
+ o->value = (plugin->output_is_cv
+ ? &plugin->uris.lv2_CVPort
+ : &plugin->uris.lv2_ControlPort);
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Ratio* plugin = (Ratio*)malloc(sizeof(Ratio));
+
+ if (plugin) {
+ plugin->numerator_is_cv = 0;
+ plugin->denominator_is_cv = 0;
+ plugin->output_is_cv = 0;
+
+ map_uris(&plugin->uris, features);
+ }
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Ratio* plugin = (Ratio*)instance;
+
+ /* Numerator (array of floats of length sample_count) */
+ const float* numerator = plugin->numerator;
+
+ /* Denominator (array of floats of length sample_count) */
+ const float* denominator = plugin->denominator;
+
+ /* Output (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ if (!plugin->output_is_cv) { /* TODO: Avoid this branch */
+ sample_count = 1;
+ }
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float n = numerator[s * plugin->numerator_is_cv];
+ float d = denominator[s * plugin->denominator_is_cv];
+
+ d = COPYSIGNF(f_max(FABSF(d), 1e-16f), d);
+
+ output[s] = n / d;
+ }
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { options_get, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/ratio",
+ instantiate,
+ connect_port,
+ NULL,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/sawtooth.c b/src/sawtooth.c
new file mode 100644
index 0000000..31c9057
--- /dev/null
+++ b/src/sawtooth.c
@@ -0,0 +1,194 @@
+/*
+ An LV2 plugin to generate a bandlimited sawtooth waveform.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+#include "wavedata.h"
+
+#define SAWTOOTH_FREQUENCY 0
+#define SAWTOOTH_OUTPUT 1
+
+typedef struct {
+ const float* frequency;
+ float* output;
+ float phase;
+ uint32_t frequency_is_cv;
+ Wavedata wdat;
+ URIs uris;
+} Sawtooth;
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Sawtooth* plugin = (Sawtooth*)instance;
+
+ switch (port) {
+ case SAWTOOTH_FREQUENCY:
+ plugin->frequency = (const float*)data;
+ break;
+ case SAWTOOTH_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Sawtooth* plugin = (Sawtooth*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case SAWTOOTH_FREQUENCY:
+ plugin->frequency_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Sawtooth* plugin = (Sawtooth*)malloc(sizeof(Sawtooth));
+ if (!plugin) {
+ return NULL;
+ }
+
+ if (wavedata_load(&plugin->wdat, bundle_path, "sawtooth_data",
+ BLOP_DLSYM_SAWTOOTH, sample_rate)) {
+ free(plugin);
+ return 0;
+ }
+
+ plugin->frequency_is_cv = 0;
+ map_uris(&plugin->uris, features);
+ wavedata_get_table(&plugin->wdat, 440.0);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ Sawtooth* plugin = (Sawtooth*)instance;
+
+ wavedata_unload(&plugin->wdat);
+ free(instance);
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Sawtooth* plugin = (Sawtooth*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Sawtooth* plugin = (Sawtooth*)instance;
+
+ /* Frequency (array of float of length 1 or sample_count) */
+ const float* frequency = plugin->frequency;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+
+ for (uint32_t s = 0; s < sample_count; s++) {
+ const float freq = frequency[s * plugin->frequency_is_cv];
+ if (freq != wdat->frequency) {
+ /* Frequency changed, look up table to play */
+ wavedata_get_table(wdat, freq);
+ }
+
+ output[s] = wavedata_get_sample(wdat, phase);
+
+ /* Update phase, wrapping if necessary */
+ phase += wdat->frequency;
+ if (phase < 0.0f) {
+ phase += wdat->sample_rate;
+ } else if (phase > wdat->sample_rate) {
+ phase -= wdat->sample_rate;
+ }
+ }
+ plugin->phase = phase;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/sawtooth",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/sequencer.c b/src/sequencer.c
new file mode 100644
index 0000000..939401b
--- /dev/null
+++ b/src/sequencer.c
@@ -0,0 +1,216 @@
+/*
+ An LV2 plugin to simulate an analogue style step sequencer.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "math_func.h"
+#include "common.h"
+
+#define SEQUENCER_GATE 0
+#define SEQUENCER_TRIGGER 1
+#define SEQUENCER_LOOP_POINT 2
+#define SEQUENCER_RESET 3
+#define SEQUENCER_VALUE_GATE_CLOSED 4
+#define SEQUENCER_VALUE_START 5
+#define SEQUENCER_OUTPUT (SEQUENCER_MAX_INPUTS + 5)
+
+typedef struct {
+ const float* gate;
+ const float* trigger;
+ const float* loop_steps;
+ const float* reset;
+ const float* value_gate_closed;
+ const float* values[SEQUENCER_MAX_INPUTS];
+ float* output;
+ float srate;
+ float inv_srate;
+ float last_gate;
+ float last_trigger;
+ float last_value;
+ unsigned int step_index;
+} Sequencer;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Sequencer* plugin = (Sequencer*)instance;
+
+ switch (port) {
+ case SEQUENCER_GATE:
+ plugin->gate = (const float*)data;
+ break;
+ case SEQUENCER_TRIGGER:
+ plugin->trigger = (const float*)data;
+ break;
+ case SEQUENCER_LOOP_POINT:
+ plugin->loop_steps = (const float*)data;
+ break;
+ case SEQUENCER_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ case SEQUENCER_RESET:
+ plugin->reset = (const float*)data;
+ break;
+ case SEQUENCER_VALUE_GATE_CLOSED:
+ plugin->value_gate_closed = (const float*)data;
+ break;
+ default:
+ if (port >= SEQUENCER_VALUE_START && port < SEQUENCER_OUTPUT) {
+ plugin->values[port - SEQUENCER_VALUE_START] = (const float*)data;
+ }
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Sequencer* plugin = (Sequencer*)malloc(sizeof(Sequencer));
+ if (!plugin) {
+ return NULL;
+ }
+
+ plugin->srate = (float)sample_rate;
+ plugin->inv_srate = 1.0f / plugin->srate;
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Sequencer* plugin = (Sequencer*)instance;
+
+ plugin->last_gate = 0.0f;
+ plugin->last_trigger = 0.0f;
+ plugin->last_value = 0.0f;
+ plugin->step_index = 0;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Sequencer* plugin = (Sequencer*)instance;
+
+ /* Gate */
+ const float* gate = plugin->gate;
+
+ /* Step Trigger */
+ const float* trigger = plugin->trigger;
+
+ /* Loop Steps */
+ const float loop_steps = *(plugin->loop_steps);
+
+ /* Reset to Value on Gate Close */
+ const float reset = *(plugin->reset);
+
+ /* Value used when gate closed */
+ float value_gate_closed = *(plugin->value_gate_closed);
+
+ /* Step Values */
+ float values[SEQUENCER_MAX_INPUTS];
+
+ /* Output */
+ float* output = plugin->output;
+
+ float last_gate = plugin->last_gate;
+ float last_trigger = plugin->last_trigger;
+ float last_value = plugin->last_value;
+
+ unsigned int step_index = plugin->step_index;
+ unsigned int loop_index = LRINTF(loop_steps);
+ int rst = reset > 0.0f;
+ int i;
+
+ loop_index = loop_index == 0 ? 1 : loop_index;
+ loop_index = (loop_index > SEQUENCER_MAX_INPUTS)
+ ? SEQUENCER_MAX_INPUTS
+ : loop_index;
+
+ for (i = 0; i < SEQUENCER_MAX_INPUTS; i++) {
+ values[i] = *(plugin->values[i]);
+ }
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ if (gate[s] > 0.0f) {
+ if (trigger[s] > 0.0f && !(last_trigger > 0.0f)) {
+ if (last_gate > 0.0f) {
+ step_index++;
+ if (step_index >= loop_index) {
+ step_index = 0;
+ }
+ } else {
+ step_index = 0;
+ }
+ }
+
+ output[s] = values[step_index];
+
+ last_value = values[step_index];
+ } else {
+ if (rst) {
+ output[s] = value_gate_closed;
+ } else {
+ output[s] = last_value;
+ }
+
+ step_index = 0;
+ }
+ last_gate = gate[s];
+ last_trigger = trigger[s];
+ }
+
+ plugin->last_gate = last_gate;
+ plugin->last_trigger = last_trigger;
+ plugin->last_value = last_value;
+ plugin->step_index = step_index;
+}
+
+static const LV2_Descriptor descriptor = {
+ SEQUENCER_URI,
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ NULL
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/square.c b/src/square.c
new file mode 100644
index 0000000..0136124
--- /dev/null
+++ b/src/square.c
@@ -0,0 +1,195 @@
+/*
+ An LV2 plugin to generate a bandlimited square waveform.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+#include "wavedata.h"
+
+#define SQUARE_FREQUENCY 0
+#define SQUARE_OUTPUT 1
+
+typedef struct {
+ const float* frequency;
+ float* output;
+ float phase;
+ uint32_t frequency_is_cv;
+ Wavedata wdat;
+ URIs uris;
+} Square;
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Square* plugin = (Square*)instance;
+
+ switch (port) {
+ case SQUARE_FREQUENCY:
+ plugin->frequency = (const float*)data;
+ break;
+ case SQUARE_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Square* plugin = (Square*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case SQUARE_FREQUENCY:
+ plugin->frequency_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Square* plugin = (Square*)malloc(sizeof(Square));
+ if (!plugin) {
+ return NULL;
+ }
+
+ if (wavedata_load(&plugin->wdat, bundle_path, "square_data",
+ BLOP_DLSYM_SQUARE, sample_rate)) {
+ free(plugin);
+ return NULL;
+ }
+
+ plugin->frequency_is_cv = 0;
+ map_uris(&plugin->uris, features);
+ wavedata_get_table(&plugin->wdat, 440.0);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ Square* plugin = (Square*)instance;
+
+ wavedata_unload(&plugin->wdat);
+ free(instance);
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Square* plugin = (Square*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Square* plugin = (Square*)instance;
+
+ /* Frequency (array of float of length sample_count) */
+ const float* frequency = plugin->frequency;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float freq = frequency[s * plugin->frequency_is_cv];
+ if (freq != wdat->frequency) {
+ /* Frequency changed, look up table to play */
+ wavedata_get_table(wdat, freq);
+ }
+
+ /* Get interpolated sample */
+ output[s] = wavedata_get_sample(wdat, phase);
+
+ /* Update phase, wrapping if necessary */
+ phase += wdat->frequency;
+ if (phase < 0.0f) {
+ phase += wdat->sample_rate;
+ } else if (phase > wdat->sample_rate) {
+ phase -= wdat->sample_rate;
+ }
+ }
+ plugin->phase = phase;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/square",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/sum.c b/src/sum.c
new file mode 100644
index 0000000..3023733
--- /dev/null
+++ b/src/sum.c
@@ -0,0 +1,185 @@
+/*
+ An LV2 plugin to calculate the sum of two signals.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+#include "vector_op.h"
+
+#define SUM_INPUT1 0
+#define SUM_INPUT2 1
+#define SUM_OUTPUT 2
+
+typedef struct {
+ const float* input1;
+ const float* input2;
+ float* output;
+ uint32_t input1_is_cv;
+ uint32_t input2_is_cv;
+ uint32_t output_is_cv;
+ URIs uris;
+} Sum;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Sum* plugin = (Sum*)instance;
+
+ switch (port) {
+ case SUM_INPUT1:
+ plugin->input1 = (const float*)data;
+ break;
+ case SUM_INPUT2:
+ plugin->input2 = (const float*)data;
+ break;
+ case SUM_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Sum* plugin = (Sum*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+ switch (o->subject) {
+ case SUM_INPUT1:
+ plugin->input1_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case SUM_INPUT2:
+ plugin->input2_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ plugin->output_is_cv = plugin->input1_is_cv || plugin->input2_is_cv;
+ return ret;
+}
+
+static uint32_t
+options_get(LV2_Handle instance,
+ LV2_Options_Option* options)
+{
+ const Sum* plugin = (const Sum*)instance;
+ uint32_t ret = 0;
+ for (LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT || o->subject != SUM_OUTPUT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else {
+ o->size = sizeof(LV2_URID);
+ o->type = plugin->uris.atom_URID;
+ o->value = (plugin->output_is_cv
+ ? &plugin->uris.lv2_CVPort
+ : &plugin->uris.lv2_ControlPort);
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Sum* plugin = (Sum*)malloc(sizeof(Sum));
+
+ if (plugin) {
+ plugin->input1_is_cv = 0;
+ plugin->input2_is_cv = 0;
+ plugin->output_is_cv = 0;
+
+ map_uris(&plugin->uris, features);
+ }
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ const Sum* const plugin = (Sum*)instance;
+ const float* const input1 = plugin->input1;
+ const float* const input2 = plugin->input2;
+ float* const output = plugin->output;
+
+ VECTOR_OP(+, output,
+ input1, plugin->input1_is_cv,
+ input2, plugin->input2_is_cv);
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { options_get, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/sum",
+ instantiate,
+ connect_port,
+ NULL,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/sync_pulse.c b/src/sync_pulse.c
new file mode 100644
index 0000000..0f24c80
--- /dev/null
+++ b/src/sync_pulse.c
@@ -0,0 +1,213 @@
+/*
+ An LV2 plugin to generate a non-bandlimited variable-pulse waveform with gate
+ for trigger and sync.
+ Copyright 2011 David Robillard
+ Copyright 2003 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+#include "common.h"
+
+#define SYNCPULSE_FREQUENCY 0
+#define SYNCPULSE_PULSEWIDTH 1
+#define SYNCPULSE_GATE 2
+#define SYNCPULSE_OUTPUT 3
+
+typedef struct {
+ const float* frequency;
+ const float* pulsewidth;
+ const float* gate;
+ float* output;
+ float srate;
+ float phase;
+ uint32_t frequency_is_cv;
+ uint32_t pulsewidth_is_cv;
+ URIs uris;
+} SyncPulse;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ SyncPulse* plugin = (SyncPulse*)instance;
+
+ switch (port) {
+ case SYNCPULSE_FREQUENCY:
+ plugin->frequency = (const float*)data;
+ break;
+ case SYNCPULSE_PULSEWIDTH:
+ plugin->pulsewidth = (const float*)data;
+ break;
+ case SYNCPULSE_GATE:
+ plugin->gate = (const float*)data;
+ break;
+ case SYNCPULSE_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ SyncPulse* plugin = (SyncPulse*)malloc(sizeof(SyncPulse));
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case SYNCPULSE_FREQUENCY:
+ plugin->frequency_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case SYNCPULSE_PULSEWIDTH:
+ plugin->pulsewidth_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ SyncPulse* plugin = (SyncPulse*)malloc(sizeof(SyncPulse));
+
+ if (plugin) {
+ plugin->srate = (float)sample_rate;
+ plugin->frequency_is_cv = 0;
+ plugin->pulsewidth_is_cv = 0;
+ map_uris(&plugin->uris, features);
+ }
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ SyncPulse* plugin = (SyncPulse*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ SyncPulse* plugin = (SyncPulse*)instance;
+
+ /* Frequency (array of float of length sample_count) */
+ const float* frequency = plugin->frequency;
+
+ /* Pulse Width (array of float of length sample_count) */
+ const float* pulsewidth = plugin->pulsewidth;
+
+ /* Gate (array of float of length sample_count) */
+ const float* gate = plugin->gate;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ float phase = plugin->phase;
+ float srate = plugin->srate;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ if (gate[s] > 0.0f) {
+ const float freq = frequency[s * plugin->frequency_is_cv];
+ const float pw = pulsewidth[s * plugin->pulsewidth_is_cv];
+ const float pwidth = f_clip(pw, 0.0f, 1.0f) * srate;
+
+ if (phase < pwidth) {
+ output[s] = 1.0f;
+ } else {
+ output[s] = -1.0f;
+ }
+
+ phase += freq;
+ if (phase < 0.0f) {
+ phase += srate;
+ } else if (phase > srate) {
+ phase -= srate;
+ }
+ } else {
+ output[s] = 0.0f;
+ phase = 0.0f;
+ }
+ }
+
+ plugin->phase = phase;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/sync_pulse",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/sync_square.c b/src/sync_square.c
new file mode 100644
index 0000000..38fdaf5
--- /dev/null
+++ b/src/sync_square.c
@@ -0,0 +1,201 @@
+/*
+ An LV2 plugin to generate a non-bandlimited square waveform with gate for
+ trigger and sync.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+
+#define SYNCSQUARE_FREQUENCY 0
+#define SYNCSQUARE_GATE 1
+#define SYNCSQUARE_OUTPUT 2
+
+typedef struct {
+ const float* frequency;
+ const float* gate;
+ float* output;
+ float srate;
+ float nyquist;
+ float phase;
+ uint32_t frequency_is_cv;
+ URIs uris;
+} SyncSquare;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ SyncSquare* plugin = (SyncSquare*)instance;
+
+ switch (port) {
+ case SYNCSQUARE_FREQUENCY:
+ plugin->frequency = (const float*)data;
+ break;
+ case SYNCSQUARE_GATE:
+ plugin->gate = (const float*)data;
+ break;
+ case SYNCSQUARE_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ SyncSquare* plugin = (SyncSquare*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case SYNCSQUARE_FREQUENCY:
+ plugin->frequency_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ SyncSquare* plugin = (SyncSquare*)malloc(sizeof(SyncSquare));
+ if (!plugin) {
+ return NULL;
+ }
+
+ plugin->srate = (float)sample_rate;
+ plugin->nyquist = (float)(sample_rate / 2.0f);
+ plugin->frequency_is_cv = 0;
+ map_uris(&plugin->uris, features);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ SyncSquare* plugin = (SyncSquare*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ SyncSquare* plugin = (SyncSquare*)instance;
+
+ /* Frequency (array of float of length 1 or sample_count) */
+ const float* frequency = plugin->frequency;
+
+ /* Gate (array of float of length sample_count) */
+ const float* gate = plugin->gate;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ float phase = plugin->phase;
+ float srate = plugin->srate;
+ float nyquist = plugin->nyquist;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ if (gate[s] > 0.0f) {
+ const float freq = frequency[s * plugin->frequency_is_cv];
+
+ if (phase < nyquist) {
+ output[s] = 1.0f;
+ } else {
+ output[s] = -1.0f;
+ }
+
+ phase += freq;
+ if (phase < 0.0f) {
+ phase += srate;
+ } else if (phase > srate) {
+ phase -= srate;
+ }
+ } else {
+ output[s] = 0.0f;
+ phase = 0.0f;
+ }
+ }
+
+ plugin->phase = phase;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/sync_square",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/tracker.c b/src/tracker.c
new file mode 100644
index 0000000..dd628bc
--- /dev/null
+++ b/src/tracker.c
@@ -0,0 +1,245 @@
+/*
+ An LV2 plugin to shape a signal in various ways.
+ Copyright 2011 David Robillard
+ Copyright 2003 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "common.h"
+#include "uris.h"
+
+#define TRACKER_GATE 0
+#define TRACKER_HATTACK 1
+#define TRACKER_HDECAY 2
+#define TRACKER_LATTACK 3
+#define TRACKER_LDECAY 4
+#define TRACKER_INPUT 5
+#define TRACKER_OUTPUT 6
+
+typedef struct {
+ const float* gate;
+ const float* hattack;
+ const float* hdecay;
+ const float* lattack;
+ const float* ldecay;
+ const float* input;
+ float* output;
+ float coeff;
+ float last_value;
+ uint32_t hattack_is_cv;
+ uint32_t hdecay_is_cv;
+ uint32_t lattack_is_cv;
+ uint32_t ldecay_is_cv;
+ URIs uris;
+} Tracker;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Tracker* plugin = (Tracker*)instance;
+
+ switch (port) {
+ case TRACKER_GATE:
+ plugin->gate = (const float*)data;
+ break;
+ case TRACKER_HATTACK:
+ plugin->hattack = (const float*)data;
+ break;
+ case TRACKER_HDECAY:
+ plugin->hdecay = (const float*)data;
+ break;
+ case TRACKER_LATTACK:
+ plugin->lattack = (const float*)data;
+ break;
+ case TRACKER_LDECAY:
+ plugin->ldecay = (const float*)data;
+ break;
+ case TRACKER_INPUT:
+ plugin->input = (const float*)data;
+ break;
+ case TRACKER_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Tracker* plugin = (Tracker*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case TRACKER_HATTACK:
+ plugin->hattack_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case TRACKER_HDECAY:
+ plugin->hdecay_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case TRACKER_LATTACK:
+ plugin->lattack_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case TRACKER_LDECAY:
+ plugin->ldecay_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Tracker* plugin = (Tracker*)malloc(sizeof(Tracker));
+ if (!plugin) {
+ return NULL;
+ }
+
+ plugin->coeff = 2.0f * M_PI / (float)sample_rate;
+
+ plugin->hattack_is_cv = 0;
+ plugin->hdecay_is_cv = 0;
+ plugin->lattack_is_cv = 0;
+ plugin->ldecay_is_cv = 0;
+
+ map_uris(&plugin->uris, features);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Tracker* plugin = (Tracker*)instance;
+
+ plugin->last_value = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Tracker* plugin = (Tracker*)instance;
+
+ /* Gate (array of floats of length sample_count) */
+ const float* gate = plugin->gate;
+
+ /* Gate High Attack Rate (array of floats of length 1 or sample_count) */
+ const float* hattack = plugin->hattack;
+
+ /* Gate High Decay Rate (array of floats of length 1 or sample_count) */
+ const float* hdecay = plugin->hdecay;
+
+ /* Gate Low Attack Rate (array of floats of length 1 or sample_count) */
+ const float* lattack = plugin->lattack;
+
+ /* Gate Low Decay Rate (array of floats of length 1 or sample_count) */
+ const float* ldecay = plugin->ldecay;
+
+ /* Input (array of floats of length sample_count) */
+ const float* input = plugin->input;
+
+ /* Output (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ /* Instance Data */
+ float coeff = plugin->coeff;
+ float last_value = plugin->last_value;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float in = input[s];
+ const float ha = hattack[s * plugin->hattack_is_cv];
+ const float hd = hdecay[s * plugin->hdecay_is_cv];
+ const float la = lattack[s * plugin->lattack_is_cv];
+ const float ld = ldecay[s * plugin->ldecay_is_cv];
+
+ float rate;
+ if (gate[s] > 0.0f) {
+ rate = in > last_value ? ha : hd;
+ } else {
+ rate = in > last_value ? la : ld;
+ }
+
+ rate = f_min(1.0f, rate * coeff);
+ last_value = last_value * (1.0f - rate) + in * rate;
+
+ output[s] = last_value;
+ }
+
+ plugin->last_value = last_value;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/tracker",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/triangle.c b/src/triangle.c
new file mode 100644
index 0000000..09c01c2
--- /dev/null
+++ b/src/triangle.c
@@ -0,0 +1,232 @@
+/*
+ An LV2 plugin to generate a bandlimited slope-variable triangle waveform.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+#include "wavedata.h"
+
+#define TRIANGLE_FREQUENCY 0
+#define TRIANGLE_SLOPE 1
+#define TRIANGLE_OUTPUT 2
+
+typedef struct {
+ const float* frequency;
+ const float* slope;
+ float* output;
+ float phase;
+ float min_slope;
+ float max_slope;
+ uint32_t frequency_is_cv;
+ uint32_t slope_is_cv;
+ Wavedata wdat;
+ URIs uris;
+} Triangle;
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Triangle* plugin = (Triangle*)instance;
+
+ switch (port) {
+ case TRIANGLE_FREQUENCY:
+ plugin->frequency = (const float*)data;
+ break;
+ case TRIANGLE_SLOPE:
+ plugin->slope = (const float*)data;
+ break;
+ case TRIANGLE_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Triangle* plugin = (Triangle*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case TRIANGLE_FREQUENCY:
+ plugin->frequency_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case TRIANGLE_SLOPE:
+ plugin->slope_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Triangle* plugin = (Triangle*)malloc(sizeof(Triangle));
+ if (!plugin) {
+ return NULL;
+ }
+
+ if (wavedata_load(&plugin->wdat, bundle_path, "parabola_data",
+ BLOP_DLSYM_PARABOLA, sample_rate)) {
+ free(plugin);
+ return 0;
+ }
+
+ plugin->min_slope = 2.0f / plugin->wdat.sample_rate;
+ plugin->max_slope = 1.0f - plugin->min_slope;
+
+ plugin->frequency_is_cv = 0;
+ plugin->slope_is_cv = 0;
+
+ map_uris(&plugin->uris, features);
+ wavedata_get_table(&plugin->wdat, 440.0);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ Triangle* plugin = (Triangle*)instance;
+
+ wavedata_unload(&plugin->wdat);
+ free(instance);
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Triangle* plugin = (Triangle*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Triangle* plugin = (Triangle*)instance;
+
+ /* Frequency (array of float of length 1 or sample_count) */
+ const float* frequency = plugin->frequency;
+
+ /* Slope (array of float of length 1 or sample_count) */
+ const float* slope = plugin->slope;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+ const float min_slope = plugin->min_slope;
+ const float max_slope = plugin->max_slope;
+
+ float last_slope = slope[0];
+ float slp = f_clip(last_slope, min_slope, max_slope);
+ float phase_shift = slp * wdat->sample_rate;
+ float scale = 1.0f / (8.0f * (slp - (slp * slp)));
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float freq = frequency[s * plugin->frequency_is_cv];
+ if (freq != wdat->frequency) {
+ /* Frequency changed, look up table to play */
+ wavedata_get_table(wdat, freq);
+ }
+
+ const float this_slope = slope[s * plugin->slope_is_cv];
+ if (this_slope != last_slope) {
+ /* Slope changed, recalculate */
+ last_slope = this_slope;
+ slp = f_clip(this_slope, min_slope, max_slope);
+ phase_shift = slp * wdat->sample_rate;
+ scale = 1.0f / (8.0f * (slp - (slp * slp)));
+ }
+
+ /* Get samples from parabola and phase shifted inverted parabola,
+ and scale to compensate */
+ output[s] = (wavedata_get_sample(wdat, phase)
+ - wavedata_get_sample(wdat, phase + phase_shift)) * scale;
+
+ /* Update phase, wrapping if necessary */
+ phase += wdat->frequency;
+ if (phase < 0.0f) {
+ phase += wdat->sample_rate;
+ } else if (phase > wdat->sample_rate) {
+ phase -= wdat->sample_rate;
+ }
+ }
+ plugin->phase = phase;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/triangle",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/wavedata.c b/src/wavedata.c
new file mode 100644
index 0000000..910577f
--- /dev/null
+++ b/src/wavedata.c
@@ -0,0 +1,79 @@
+/*
+ Oscillator wave data loading.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "blop_config.h"
+#include "wavedata.h"
+
+#ifdef _WIN32
+# include <windows.h>
+# define dlopen(path, flags) LoadLibrary(path)
+# define dlclose(lib) FreeLibrary((HMODULE)lib)
+# define dlsym(lib, sym) GetProcAddress((HMODULE)lib, sym)
+# define snprintf _snprintf
+#else
+# include <dlfcn.h>
+#endif
+
+int
+wavedata_load(Wavedata* w,
+ const char* bundle_path,
+ const char* lib_name,
+ const char* wdat_descriptor_name,
+ double sample_rate)
+{
+ const size_t bundle_len = strlen(bundle_path);
+ const size_t lib_name_len = strlen(lib_name);
+ const size_t ext_len = strlen(BLOP_SHLIB_EXT);
+ const size_t path_len = bundle_len + lib_name_len + ext_len + 2;
+ int retval = -1;
+
+ char* lib_path = (char*)malloc(path_len);
+ snprintf(lib_path, path_len, "%s%s%s",
+ bundle_path, lib_name, BLOP_SHLIB_EXT);
+
+ void* handle = dlopen(lib_path, RTLD_NOW);
+ free(lib_path);
+
+ if (handle) {
+ // Avoid pedantic warnings about fetching functions with dlsym
+ typedef void (*VoidFunc)(void);
+ typedef VoidFunc (*VoidFuncGetter)(void*, const char*);
+ VoidFuncGetter dlfunc = (VoidFuncGetter)dlsym;
+
+ typedef int (*DescFunc)(Wavedata*, unsigned long);
+ DescFunc desc_func = (DescFunc)dlfunc(handle, wdat_descriptor_name);
+ if (desc_func) {
+ retval = desc_func(w, (unsigned long)sample_rate);
+ w->data_handle = handle;
+ return retval;
+ }
+ }
+
+ return retval;
+}
+
+void
+wavedata_unload(Wavedata* w)
+{
+ dlclose(w->data_handle);
+}
diff --git a/src/wavegen.c b/src/wavegen.c
new file mode 100644
index 0000000..c722e04
--- /dev/null
+++ b/src/wavegen.c
@@ -0,0 +1,310 @@
+/*
+ A program to generate c header files containing pre-calculated wavedata.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include "wdatutil.h"
+#include "wavedata.h"
+#include "common.h"
+
+static void
+usage(void)
+{
+ int i;
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Generate bandlimited wavedata and write as c header file\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Usage: wavegen -w <Wavename> -r <Sample Rate> -f <Note> -s <Note Step>\n");
+ fprintf(stderr, " -m <Samples> [-o <Output Filename>] [-p <Prefix>]\n");
+ fprintf(stderr, " [-g <Factor>] [-q] [-t] [-h]\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " -w, --wave Name of wave to generate (case insensitive)\n");
+ fprintf(stderr, " -r, --rate Intended playback rate in Samples/Second\n");
+ fprintf(stderr, " -f, --first First MIDI note to generate table for\n");
+ fprintf(stderr, " -s, --step Number of MIDI notes to skip for next table\n");
+ fprintf(stderr, " -m, --min Minimum table size in samples\n");
+ fprintf(stderr, " -o, --output Output Filename, name of file to output\n");
+ fprintf(stderr, " If not given, output is to stdout\n");
+ fprintf(stderr, " -p, --prefix Prefix for declarations in header\n");
+ fprintf(stderr, " -g, --gibbs Compensate for Gibbs' effect\n");
+ fprintf(stderr, " -q, --quiet Surpress stderr output\n");
+ fprintf(stderr, " -t, --test Don't actually generate data\n");
+ fprintf(stderr, " -h, --help Print this text and exit\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Supported waves:\n");
+
+ for (i = 0; i < WAVE_TYPE_COUNT; i++) {
+ fprintf(stderr, " %s (%s)\n", wave_names[i], wave_descriptions[i]);
+ }
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Gibbs' Effect\n");
+ fprintf(stderr, " Gibbs' effect causes overshoot in waves generated from finite\n");
+ fprintf(stderr, " Fourier Series. Compensation can be applied, which will result in\n");
+ fprintf(stderr, " a waveform that sounds slightly less bright.\n");
+ fprintf(stderr, " Use the --gibbs option to set degree of compensatation, from 0.0\n");
+ fprintf(stderr, " (no compensation) to 1.0 (full compensation)\n");
+ fprintf(stderr, "\n");
+}
+
+/**
+ Create bandlimited wavedata header files for various
+ waveforms
+*/
+int
+main(int argc,
+ char** argv)
+{
+ int option_index;
+ int opt;
+ const char* options = "w:r:f:s:m:o:p:g:qth";
+ struct option long_options[] = {
+ { "wave", 1, 0, 'w' },
+ { "rate", 1, 0, 'r' },
+ { "first", 1, 0, 'f' },
+ { "step", 1, 0, 's' },
+ { "min", 1, 0, 'm' },
+ { "output", 1, 0, 'o' },
+ { "prefix", 0, 0, 'p' },
+ { "gibbs", 1, 0, 'g' },
+ { "quiet", 0, 0, 'q' },
+ { "test", 0, 0, 't' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+ };
+ int wavetype = -1;
+ long sample_rate = -1;
+ long first_note = -1;
+ long note_step = -1;
+ long min_table_size = -1;
+ const char* filename = NULL;
+ FILE* file;
+ const char* prefix = NULL;
+ float gibbs = 0.0f;
+ int quiet = 0;
+ int test = 0;
+
+ Wavedata* w;
+ float freq;
+ uint32_t sample_count;
+ unsigned long max_harmonic_hf;
+ unsigned long max_harmonic_lf;
+ unsigned long i;
+
+ size_t strcmplen;
+ size_t len1;
+ size_t len2;
+
+ /* Parse arguments */
+ if (argc == 1) {
+ usage();
+ exit(-1);
+ }
+
+ opterr = 0;
+ while ((opt = getopt_long(argc, argv, options, long_options, &option_index)) != -1) {
+ switch (opt) {
+ case 'w':
+ for (i = 0; i < WAVE_TYPE_COUNT; i++) {
+ len1 = strlen(optarg);
+ len2 = strlen(wave_names[i]);
+ strcmplen = len1 < len2 ? len1 : len2;
+
+ if (!strncmp(optarg, wave_names[i], strcmplen)) {
+ wavetype = i;
+ }
+ }
+ if (wavetype == -1) {
+ fprintf(stderr, "Unrecognised option for Wave: %s\n", optarg);
+ exit(-1);
+ }
+ break;
+ case 'r':
+ sample_rate = (long)atoi(optarg);
+ break;
+ case 'f':
+ first_note = (long)atoi(optarg);
+ break;
+ case 's':
+ note_step = (long)atoi(optarg);
+ break;
+ case 'm':
+ min_table_size = (long)atoi(optarg);
+ break;
+ case 'o':
+ filename = optarg;
+ break;
+ case 'p':
+ prefix = optarg;
+ break;
+ case 'g':
+ gibbs = atof(optarg);
+ break;
+ case 'q':
+ quiet = -1;
+ break;
+ case 't':
+ test = -1;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ break;
+ default:
+ usage();
+ exit(-1);
+ }
+ }
+
+ /* Check basic arguments */
+ if (wavetype == -1) {
+ if (!quiet) {
+ fprintf(stderr, "No wavetype specified.\n");
+ }
+ exit(-1);
+ }
+
+ if (sample_rate == -1) {
+ if (!quiet) {
+ fprintf(stderr, "No sample rate specified.\n");
+ }
+ exit(-1);
+ }
+
+ if (first_note == -1) {
+ if (!quiet) {
+ fprintf(stderr, "No first note specified.\n");
+ }
+ exit(-1);
+ }
+
+ if (note_step == -1) {
+ if (!quiet) {
+ fprintf(stderr, "No note step specified.\n");
+ }
+ exit(-1);
+ }
+
+ if (min_table_size == -1) {
+ if (!quiet) {
+ fprintf(stderr, "No minimum table size specified.\n");
+ }
+ exit(-1);
+ }
+
+ if (gibbs < 0.0f || gibbs > 1.0f) {
+ if (!quiet) {
+ fprintf(stderr, "Gibbs compensation clamped to [0.0, 1.0]\n");
+ fprintf(stderr, " Supplied value: %.2f\n", gibbs);
+ }
+ gibbs = gibbs < 0.0f ? 0.0f : gibbs;
+ gibbs = gibbs > 1.0f ? 1.0f : gibbs;
+ if (!quiet) {
+ fprintf(stderr, " Clamped to: %.2f\n", gibbs);
+ }
+ }
+
+ if (note_step < 1) {
+ if (!quiet) {
+ fprintf(stderr, "Using minimum note step of 1\n");
+ }
+ note_step = 1;
+ }
+
+ /* Get file to write to */
+ if (!filename) {
+ file = stdout;
+ } else {
+ file = fopen(filename, "w");
+ }
+
+ w = wavedata_new(sample_rate);
+
+ if (!w) {
+ if (!quiet) {
+ fprintf(stderr, "Unable to create wavedata\n");
+ }
+
+ exit(-1);
+ }
+
+ freq = FREQ_FROM_NOTE(first_note);
+ max_harmonic_lf = HARM_FROM_FREQ(freq, sample_rate);
+ max_harmonic_hf = max_harmonic_lf;
+
+ for (i = 0; max_harmonic_hf > MIN_HARM(wavetype); i += note_step) {
+ freq = FREQ_FROM_NOTE(first_note + i + note_step);
+ max_harmonic_hf = HARM_FROM_FREQ(freq, sample_rate);
+
+ max_harmonic_hf = ACTUAL_HARM(max_harmonic_hf, wavetype);
+ max_harmonic_lf = ACTUAL_HARM(max_harmonic_lf, wavetype);
+
+ while (max_harmonic_lf == max_harmonic_hf) {
+ i += note_step;
+ freq = FREQ_FROM_NOTE(first_note + i + note_step);
+ max_harmonic_hf = HARM_FROM_FREQ(freq, sample_rate);
+ max_harmonic_hf = ACTUAL_HARM(max_harmonic_hf, wavetype);
+ }
+
+ if (max_harmonic_lf > MIN_EXTRA_HARM(wavetype)) {
+ sample_count = max_harmonic_lf * 2;
+ sample_count = sample_count < min_table_size ? min_table_size : sample_count;
+
+ if (wavedata_add_table(w, sample_count, max_harmonic_lf)) {
+ if (!quiet) {
+ fprintf(stderr, "Could not add wavetable to wavedata\n");
+ }
+
+ wavedata_cleanup(w);
+ exit(-1);
+ }
+ }
+ max_harmonic_lf = max_harmonic_hf;
+ }
+
+ if (!quiet) {
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Generating %s wave\n", wave_names[wavetype]);
+ fprintf(stderr, " Sample Rate: %ld\n", sample_rate);
+ if (gibbs > 0.0f) {
+ fprintf(stderr, " Gibbs' compensation factor: %+.2f\n\n", gibbs);
+ }
+ }
+
+ wavedata_generate_tables(w, (Wavetype)wavetype, gibbs);
+
+ if (!test) {
+ if (wavedata_write(w, file, prefix)) {
+ if (!quiet) {
+ fprintf(stderr, "Could not write to file %s!\n\n", filename);
+ }
+ } else {
+ if (!quiet) {
+ fprintf(stderr, "Written to file %s\n\n", filename);
+ }
+ }
+ }
+
+ wavedata_cleanup(w);
+
+ return 0;
+}
diff --git a/src/wdatutil.c b/src/wdatutil.c
new file mode 100644
index 0000000..86b5fb0
--- /dev/null
+++ b/src/wdatutil.c
@@ -0,0 +1,679 @@
+/*
+ Code to generate wavedata for bandlimited waveforms.
+ Copyright 2011-2014 David Robillard
+ Copyright 2003 Mike Rawes
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "common.h"
+#include "math_func.h"
+#include "wavedata.h"
+#include "wdatutil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void generate_sine(float* samples,
+ uint32_t sample_count);
+void generate_sawtooth(float* samples,
+ uint32_t sample_count,
+ unsigned long harmonics,
+ float gibbs_comp);
+void generate_square(float* samples,
+ uint32_t sample_count,
+ unsigned long harmonics,
+ float gibbs_comp);
+void generate_parabola(float* samples,
+ uint32_t sample_count,
+ unsigned long harmonics,
+ float gibbs_comp);
+
+#ifdef __cplusplus
+} /* extern "C" { */
+#endif
+
+const char* wave_names[] = {
+ "saw",
+ "square",
+ "parabola"
+};
+
+const char* wave_descriptions[] = {
+ "Sawtooth Wave",
+ "Square Wave",
+ "Parabola Wave"
+};
+
+unsigned long wave_first_harmonics[] = {
+ 1,
+ 1,
+ 1
+};
+
+unsigned long wave_harmonic_intervals[] = {
+ 1,
+ 2,
+ 1
+};
+
+Wavedata*
+wavedata_new(double sample_rate)
+{
+ Wavedata* w;
+
+ w = (Wavedata*)malloc(sizeof(Wavedata));
+
+ if (!w) {
+ return 0;
+ }
+
+ w->data_handle = 0;
+ w->table_count = 0;
+ w->tables = 0;
+ w->lookup = 0;
+ w->lookup_max = 0;
+ w->sample_rate = (float)sample_rate;
+ w->nyquist = w->sample_rate * 0.5f;
+
+ return w;
+}
+
+void
+wavedata_cleanup(Wavedata* w)
+{
+ unsigned long ti;
+ Wavetable* t;
+
+ for (ti = 0; ti < w->table_count; ti++) {
+ t = w->tables[ti];
+ if (t) {
+ if (t->samples_hf) {
+ free(t->samples_hf);
+ }
+
+ if (t->samples_lf) {
+ free(t->samples_lf);
+ }
+
+ free(t);
+ }
+ }
+
+ free(w);
+}
+
+int
+wavedata_add_table(Wavedata* w,
+ uint32_t sample_count,
+ unsigned long harmonics)
+{
+ Wavetable** tables;
+ Wavetable* t;
+ size_t bytes;
+
+ t = (Wavetable*)malloc(sizeof(Wavetable));
+
+ if (!t) {
+ return -1;
+ }
+
+ /* Extra 3 samples for interpolation */
+ bytes = (sample_count + 3) * sizeof(float);
+
+ t->samples_lf = (float*)malloc(bytes);
+
+ if (!t->samples_lf) {
+ free(t);
+ return -1;
+ }
+
+ t->samples_hf = (float*)malloc(bytes);
+
+ if (!t->samples_hf) {
+ free(t->samples_lf);
+ free(t);
+ return -1;
+ }
+
+ bytes = (w->table_count + 1) * sizeof(Wavetable*);
+ if (w->table_count == 0) {
+ tables = (Wavetable**)malloc(bytes);
+ } else {
+ tables = (Wavetable**)realloc(w->tables, bytes);
+ }
+
+ if (!tables) {
+ free(t);
+ return -1;
+ }
+
+ t->sample_count = sample_count;
+ t->harmonics = harmonics;
+
+ if (w->lookup_max < harmonics) {
+ w->lookup_max = harmonics;
+ }
+
+ tables[w->table_count] = t;
+ w->tables = tables;
+ w->table_count++;
+
+ return 0;
+}
+
+void
+wavedata_generate_tables(Wavedata* w,
+ Wavetype wavetype,
+ float gibbs_comp)
+{
+ Wavetable* t;
+ float* samples_lf;
+ float* samples_hf;
+ unsigned long h_lf;
+ unsigned long h_hf;
+ unsigned long i;
+
+ for (i = 0; i < w->table_count; i++) {
+ t = w->tables[i];
+
+ h_lf = t->harmonics;
+
+ if (i < w->table_count - 1) {
+ h_hf = w->tables[i + 1]->harmonics;
+ } else {
+ h_hf = 1;
+ }
+
+ samples_lf = t->samples_lf;
+ samples_hf = t->samples_hf;
+ samples_lf++;
+ samples_hf++;
+
+ switch (wavetype) {
+ case SAW:
+ generate_sawtooth(samples_lf, t->sample_count, h_lf, gibbs_comp);
+ generate_sawtooth(samples_hf, t->sample_count, h_hf, gibbs_comp);
+ break;
+ case SQUARE:
+ generate_square(samples_lf, t->sample_count, h_lf, gibbs_comp);
+ generate_square(samples_hf, t->sample_count, h_hf, gibbs_comp);
+ break;
+ case PARABOLA:
+ generate_parabola(samples_lf, t->sample_count, h_lf, gibbs_comp);
+ generate_parabola(samples_hf, t->sample_count, h_hf, gibbs_comp);
+ break;
+ }
+
+ /* Basic denormalization */
+ for (uint32_t s = 0; s < t->sample_count; s++) {
+ samples_lf[s] = FABSF(samples_lf[s]) < SMALLEST_FLOAT ? 0.0 : samples_lf[s];
+ }
+
+ samples_lf--;
+ samples_lf[0] = samples_lf[t->sample_count];
+ samples_lf[t->sample_count + 1] = samples_hf[1];
+ samples_lf[t->sample_count + 2] = samples_hf[2];
+
+ for (uint32_t s = 0; s < t->sample_count; s++) {
+ samples_hf[s] = FABSF(samples_hf[s]) < SMALLEST_FLOAT ? 0.0 : samples_hf[s];
+ }
+
+ samples_hf--;
+ samples_hf[0] = samples_hf[t->sample_count];
+ samples_hf[t->sample_count + 1] = samples_hf[1];
+ samples_hf[t->sample_count + 2] = samples_hf[2];
+ }
+}
+
+static void
+wavedata_write_prototype(FILE* wdat_fp,
+ const char* data_name)
+{
+ fprintf(wdat_fp, "__attribute__((visibility(\"default\")))\n");
+ fprintf(wdat_fp, "int\n");
+ fprintf(
+ wdat_fp,
+ "blop_get_%s (Wavedata * w, unsigned long sample_rate)",
+ data_name);
+}
+
+int
+wavedata_write(Wavedata* w,
+ FILE* wdat_fp,
+ const char* data_name)
+{
+ Wavetable* t = 0;
+ unsigned long table_count;
+ unsigned long i;
+ unsigned long j;
+ unsigned long s;
+ int column;
+ /*
+ * Extra table at end
+ */
+ table_count = w->table_count + 1;
+
+ fprintf(wdat_fp, "#include \"lv2/lv2plug.in/ns/lv2core/lv2.h\"\n");
+ fprintf(wdat_fp, "#include <stdio.h>\n");
+ fprintf(wdat_fp, "#include \"wavedata.h\"\n");
+ fprintf(wdat_fp, "\n");
+ /*
+ * Function prototype
+ */
+ wavedata_write_prototype(wdat_fp, data_name);
+ fprintf(wdat_fp, ";\n\n");
+ /*
+ * Fixed data and tables
+ */
+ fprintf(wdat_fp, "unsigned long ref_count = 0;\n");
+ fprintf(wdat_fp, "unsigned long first_sample_rate = 0;\n");
+ fprintf(wdat_fp, "unsigned long table_count = %ld;\n", table_count);
+ fprintf(wdat_fp, "Wavetable tables[%ld];\n", table_count);
+ fprintf(wdat_fp, "Wavetable * ptables[%ld];\n", table_count);
+ fprintf(wdat_fp, "unsigned long lookup[%ld];\n", w->lookup_max + 1);
+ fprintf(wdat_fp, "unsigned long lookup_max = %ld;\n", w->lookup_max);
+ fprintf(wdat_fp, "\n");
+ /*
+ * Sample data
+ * Each table has an extra 3 samples for interpolation
+ */
+ for (i = 0; i < w->table_count; i++) {
+ t = w->tables[i];
+
+ fprintf(wdat_fp, "static float samples_lf_%ld[%ld] = {\n", i, t->sample_count + 3);
+
+ column = 0;
+ for (s = 0; s < t->sample_count + 3 - 1; s++, column++) {
+ if (column == 5) {
+ fprintf(wdat_fp, "\n");
+ column = 0;
+ }
+ fprintf(wdat_fp, "%+.8ef,", t->samples_lf[s]);
+ }
+
+ if (column == 5) {
+ fprintf(wdat_fp, "\n");
+ }
+
+ fprintf(wdat_fp, "%+.8ef\n", t->samples_lf[s]);
+ fprintf(wdat_fp, "};\n");
+ fprintf(wdat_fp, "\n");
+
+ fprintf(wdat_fp, "static float samples_hf_%ld[%ld] = {\n", i, t->sample_count + 3);
+
+ column = 0;
+ for (s = 0; s < t->sample_count + 3 - 1; s++, column++) {
+ if (column == 5) {
+ fprintf(wdat_fp, "\n");
+ column = 0;
+ }
+ fprintf(wdat_fp, "%+.8ef,", t->samples_hf[s]);
+ }
+
+ if (column == 5) {
+ fprintf(wdat_fp, "\n");
+ }
+
+ fprintf(wdat_fp, "%+.8ef\n", t->samples_hf[s]);
+ fprintf(wdat_fp, "};\n");
+ fprintf(wdat_fp, "\n");
+ }
+
+ fprintf(wdat_fp, "float samples_zero[%ld];\n", t->sample_count + 3);
+ fprintf(wdat_fp, "\n");
+ /*
+ * Function to get Wavedata - the sample rate is needed to calculate
+ * frequencies and related things
+ */
+ wavedata_write_prototype(wdat_fp, data_name);
+ fprintf(wdat_fp, "\n{\n");
+ fprintf(wdat_fp, "\tWavetable * t;\n");
+ fprintf(wdat_fp, "\tunsigned long ti;\n");
+ fprintf(wdat_fp, "\n");
+ /*
+ * Sample rate must be > 0
+ */
+ fprintf(wdat_fp, "\tif (sample_rate == 0)\n");
+ fprintf(wdat_fp, "\t\treturn -1;\n");
+ fprintf(wdat_fp, "\n");
+ /*
+ * First time call - set up all sample rate dependent data
+ */
+ fprintf(wdat_fp, "\tif (first_sample_rate == 0)\n");
+ fprintf(wdat_fp, "\t{\n");
+ fprintf(wdat_fp, "\t\tfirst_sample_rate = sample_rate;\n");
+ fprintf(wdat_fp, "\t\tw->sample_rate = (float) sample_rate;\n");
+ fprintf(wdat_fp, "\t\tw->nyquist = w->sample_rate / 2.0f;\n");
+ fprintf(wdat_fp, "\t\tw->table_count = table_count;\n");
+ fprintf(wdat_fp, "\t\tw->tables = ptables;\n");
+ fprintf(wdat_fp, "\t\tw->lookup = lookup;\n");
+ fprintf(wdat_fp, "\t\tw->lookup_max = lookup_max;\n");
+ fprintf(wdat_fp, "\n");
+ fprintf(wdat_fp, "\t\tfor (ti = 1; ti < table_count - 1; ti++)\n");
+ fprintf(wdat_fp, "\t\t{\n");
+ fprintf(wdat_fp, "\t\t\tt = ptables[ti];\n");
+ fprintf(wdat_fp,
+ "\t\t\tt->min_frequency = w->nyquist / (float) (ptables[ti - 1]->harmonics);\n");
+ fprintf(wdat_fp, "\t\t\tt->max_frequency = w->nyquist / (float) (t->harmonics);\n");
+ fprintf(wdat_fp, "\t\t}\n");
+ fprintf(wdat_fp, "\n");
+ fprintf(wdat_fp, "\t\tt = w->tables[0];\n");
+ fprintf(wdat_fp, "\t\tt->min_frequency = 0.0f;\n");
+ fprintf(wdat_fp, "\t\tt->max_frequency = ptables[1]->min_frequency;\n");
+ fprintf(wdat_fp, "\n");
+ fprintf(wdat_fp, "\t\tt = ptables[table_count - 1];\n");
+ fprintf(wdat_fp, "\t\tt->min_frequency = ptables[w->table_count - 2]->max_frequency;\n");
+ fprintf(wdat_fp, "\t\tt->max_frequency = w->nyquist;\n");
+ fprintf(wdat_fp, "\t\n");
+ fprintf(wdat_fp, "\t\tfor (ti = 0; ti < w->table_count; ti++)\n");
+ fprintf(wdat_fp, "\t\t{\n");
+ fprintf(wdat_fp, "\t\t\tt = w->tables[ti];\n");
+ fprintf(wdat_fp, "\t\t\tt->phase_scale_factor = (float) (t->sample_count) / w->sample_rate;\n");
+ fprintf(wdat_fp,
+ "\t\t\tt->range_scale_factor = 1.0f / (t->max_frequency - t->min_frequency);\n");
+ fprintf(wdat_fp, "\t\t}\n");
+ fprintf(wdat_fp, "\n");
+ fprintf(wdat_fp, "\t\treturn 0;\n");
+ fprintf(wdat_fp, "\t}\n");
+ /*
+ * Already called at least once, so just set up wavedata
+ */
+ fprintf(wdat_fp, "\telse if (sample_rate == first_sample_rate)\n");
+ fprintf(wdat_fp, "\t{\n");
+ fprintf(wdat_fp, "\t\tw->sample_rate = (float) sample_rate;\n");
+ fprintf(wdat_fp, "\t\tw->nyquist = w->sample_rate / 2.0f;\n");
+ fprintf(wdat_fp, "\t\tw->table_count = table_count;\n");
+ fprintf(wdat_fp, "\t\tw->tables = ptables;\n");
+ fprintf(wdat_fp, "\t\tw->lookup = lookup;\n");
+ fprintf(wdat_fp, "\t\tw->lookup_max = lookup_max;\n");
+ fprintf(wdat_fp, "\n");
+ fprintf(wdat_fp, "\t\treturn 0;\n");
+ fprintf(wdat_fp, "\t}\n");
+ /*
+ * Sample rate does not match, so fail
+ *
+ * NOTE: This means multiple sample rates are not supported
+ * This should not present any problems
+ */
+ fprintf(wdat_fp, "\telse\n");
+ fprintf(wdat_fp, "\t{\n");
+ fprintf(wdat_fp, "\t\treturn -1;\n");
+ fprintf(wdat_fp, "\t}\n");
+ fprintf(wdat_fp, "}\n");
+ fprintf(wdat_fp, "\n");
+ /*
+ * _init()
+ * Assemble tables and lookup
+ */
+ fprintf(wdat_fp, "static void\n");
+ fprintf(wdat_fp, "__attribute__ ((constructor))\n");
+ fprintf(wdat_fp, "init (void)\n");
+ fprintf(wdat_fp, "{\n");
+ fprintf(wdat_fp, "\tunsigned long max_harmonic;\n");
+ fprintf(wdat_fp, "\tunsigned long ti;\n");
+ fprintf(wdat_fp, "\tunsigned long li;\n");
+ fprintf(wdat_fp, "\n");
+
+ for (i = 0; i < w->table_count; i++) {
+ t = w->tables[i];
+
+ fprintf(wdat_fp, "\ttables[%ld].sample_count = %ld;\n", i, t->sample_count);
+ fprintf(wdat_fp, "\ttables[%ld].samples_lf = samples_lf_%ld;\n", i, i);
+ fprintf(wdat_fp, "\ttables[%ld].samples_hf = samples_hf_%ld;\n", i, i);
+ fprintf(wdat_fp, "\ttables[%ld].harmonics = %ld;\n", i, t->harmonics);
+ fprintf(wdat_fp, "\n");
+ }
+ /*
+ * Last table - uses same sample data as previous table for lf data,
+ * and zeroes for hf data
+ */
+ i = w->table_count - 1;
+ j = i + 1;
+ t = w->tables[i];
+ /*
+ * Zero silent samples
+ */
+ fprintf(wdat_fp, "\tfor (uint32_t s = 0; s < %ld; s++)\n", t->sample_count + 3);
+ fprintf(wdat_fp, "\t\tsamples_zero[s] = 0.0f;\n");
+ fprintf(wdat_fp, "\n");
+
+ fprintf(wdat_fp, "\ttables[%ld].sample_count = %ld;\n", j, t->sample_count);
+ fprintf(wdat_fp, "\ttables[%ld].samples_lf = samples_hf_%ld;\n", j, i);
+ fprintf(wdat_fp, "\ttables[%ld].samples_hf = samples_zero;\n", j);
+ fprintf(wdat_fp, "\ttables[%ld].harmonics = 1;\n", j);
+ fprintf(wdat_fp, "\n");
+ /*
+ * Get pointers to each wavetable and put them in the pointer array
+ */
+ fprintf(wdat_fp, "\tfor (ti = 0; ti < table_count; ti++)\n");
+ fprintf(wdat_fp, "\t\tptables[ti] = &tables[ti];\n");
+ fprintf(wdat_fp, "\n");
+ /*
+ * Shift all sample offsets forward by one sample
+ * !!! NO! Don't!
+ fprintf (wdat_fp, "\tfor (ti = 0; ti < table_count; ti++)\n");
+ fprintf (wdat_fp, "\t{\n");
+ fprintf (wdat_fp, "\t\tptables[ti]->samples_lf++;\n");
+ fprintf (wdat_fp, "\t\tptables[ti]->samples_hf++;\n");
+ fprintf (wdat_fp, "\t}\n");
+ fprintf (wdat_fp, "\n");
+ */
+ /*
+ * Table lookup vector indexed by harmonic
+ * Add lookup data to vector
+ */
+ fprintf(wdat_fp, "\tli = 0;");
+ fprintf(wdat_fp, "\n");
+ fprintf(wdat_fp, "\tfor (ti = table_count - 1; ti > 0; ti--)\n");
+ fprintf(wdat_fp, "\t{\n");
+ fprintf(wdat_fp, "\t\tmax_harmonic = ptables[ti]->harmonics;\n");
+ fprintf(wdat_fp, "\n");
+ fprintf(wdat_fp, "\t\tfor ( ; li <= max_harmonic; li++)\n");
+ fprintf(wdat_fp, "\t\t\tlookup[li] = ti;\n");
+ fprintf(wdat_fp, "\t}\n");
+ fprintf(wdat_fp, "\n");
+ fprintf(wdat_fp, "\tfor ( ; li <= lookup_max; li++)\n");
+ fprintf(wdat_fp, "\t\tlookup[li] = 0;\n");
+ fprintf(wdat_fp, "}\n");
+
+ return 0;
+}
+
+void
+generate_sawtooth(float* samples,
+ uint32_t sample_count,
+ unsigned long harmonics,
+ float gibbs_comp)
+{
+ double phase_scale = 2.0 * M_PI / (double)sample_count;
+ float scale = 2.0f / M_PI;
+ unsigned long i;
+ unsigned long h;
+ double mhf;
+ double hf;
+ double k;
+ double m;
+ double phase;
+ double partial;
+
+ if (gibbs_comp > 0.0f) {
+ /* Degree of Gibbs Effect compensation */
+ mhf = (double)harmonics;
+ k = M_PI * (double)gibbs_comp / mhf;
+
+ for (i = 0; i < sample_count; i++) {
+ samples[i] = 0.0f;
+ }
+
+ for (h = 1; h <= harmonics; h++) {
+ hf = (double)h;
+
+ /* Gibbs Effect compensation - Hamming window */
+ /* Modified slightly for smoother fade at highest frequencies */
+ m = 0.54 + 0.46 * cos((hf - 0.5 / mhf) * k);
+
+ for (i = 0; i < sample_count; i++) {
+ phase = (double)i * phase_scale;
+ partial = (m / hf) * sin(phase * hf);
+ samples[i] += (float)partial;
+ }
+ }
+
+ for (i = 0; i < sample_count; i++) {
+ samples[i] *= scale;
+ }
+ } else {
+ /* Allow overshoot */
+ for (i = 0; i < sample_count; i++) {
+ phase = (double)i * phase_scale;
+ samples[i] = 0.0f;
+
+ for (h = 1; h <= harmonics; h++) {
+ hf = (double)h;
+ partial = (1.0 / hf) * sin(phase * hf);
+ samples[i] += (float)partial;
+ }
+ samples[i] *= scale;
+ }
+ }
+}
+
+void
+generate_square(float* samples,
+ uint32_t sample_count,
+ unsigned long harmonics,
+ float gibbs_comp)
+{
+ double phase_scale = 2.0 * M_PI / (double)sample_count;
+ float scale = 4.0f / M_PI;
+ unsigned long i;
+ unsigned long h;
+ double mhf;
+ double hf;
+ double k;
+ double m;
+ double phase;
+ double partial;
+
+ if (gibbs_comp > 0.0f) {
+ /* Degree of Gibbs Effect compensation */
+ mhf = (double)harmonics;
+ k = M_PI * (double)gibbs_comp / mhf;
+
+ for (i = 0; i < sample_count; i++) {
+ samples[i] = 0.0f;
+ }
+
+ for (h = 1; h <= harmonics; h += 2) {
+ hf = (double)h;
+
+ /* Gibbs Effect compensation - Hamming window */
+ /* Modified slightly for smoother fade at highest frequencies */
+ m = 0.54 + 0.46 * cos((hf - 0.5 / pow(mhf, 2.2)) * k);
+
+ for (i = 0; i < sample_count; i++) {
+ phase = (double)i * phase_scale;
+ partial = (m / hf) * sin(phase * hf);
+ samples[i] += (float)partial;
+ }
+ }
+
+ for (i = 0; i < sample_count; i++) {
+ samples[i] *= scale;
+ }
+ } else {
+ /* Allow overshoot */
+ for (i = 0; i < sample_count; i++) {
+ phase = (double)i * phase_scale;
+ samples[i] = 0.0f;
+
+ for (h = 1; h <= harmonics; h += 2) {
+ hf = (double)h;
+ partial = (1.0 / hf) * sin(phase * hf);
+ samples[i] += (float)partial;
+ }
+ samples[i] *= scale;
+ }
+ }
+}
+
+void
+generate_parabola(float* samples,
+ uint32_t sample_count,
+ unsigned long harmonics,
+ float gibbs_comp)
+{
+ double phase_scale = 2.0 * M_PI / (double)sample_count;
+ float scale = 2.0f / (M_PI * M_PI);
+ unsigned long i;
+ unsigned long h;
+ //double mhf;
+ double hf;
+ //double k;
+ //double m;
+ double phase;
+ double partial;
+ double sign;
+
+ if (gibbs_comp > 0.0f) {
+ /* Degree of Gibbs Effect compensation */
+ //mhf = (double)harmonics;
+ //k = M_PI * (double)gibbs_comp / mhf;
+
+ for (i = 0; i < sample_count; i++) {
+ samples[i] = 0.0f;
+ }
+
+ sign = -1.0;
+
+ for (h = 1; h <= harmonics; h++) {
+ hf = (double)h;
+
+ /* Gibbs Effect compensation - Hamming window */
+ /* Modified slightly for smoother fade at highest frequencies */
+ //m = 0.54 + 0.46 * cos((hf - 0.5 / mhf) * k);
+
+ for (i = 0; i < sample_count; i++) {
+ phase = (double)i * phase_scale;
+ partial = (sign * 4.0 / (hf * hf)) * cos(phase * hf);
+ samples[i] += (float)partial;
+ }
+ sign = -sign;
+ }
+
+ for (i = 0; i < sample_count; i++) {
+ samples[i] *= scale;
+ }
+ } else {
+ /* Allow overshoot */
+ for (i = 0; i < sample_count; i++) {
+ phase = (double)i * phase_scale;
+ samples[i] = 0.0f;
+ sign = -1.0;
+
+ for (h = 1; h <= harmonics; h++) {
+ hf = (double)h;
+ partial = (sign * 4.0 / (hf * hf)) * cos(phase * hf);
+ samples[i] += (float)partial;
+ sign = -sign;
+ }
+ samples[i] *= scale;
+ }
+ }
+}