aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2011-10-03 05:22:14 +0000
committerDavid Robillard <d@drobilla.net>2011-10-03 05:22:14 +0000
commite550736dbb862d8333a4d7f2ede9804a3d48326c (patch)
tree185b6f0483cbfe2fae87bb61b0e123fb2353b5d5 /src
downloadblop.lv2-e550736dbb862d8333a4d7f2ede9804a3d48326c.tar.gz
blop.lv2-e550736dbb862d8333a4d7f2ede9804a3d48326c.tar.bz2
blop.lv2-e550736dbb862d8333a4d7f2ede9804a3d48326c.zip
Add 'blip', an LV2 port of the LADSPA 'blop' collection.
git-svn-id: http://svn.drobilla.net/lad/trunk/plugins/blip.lv2@3525 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src')
-rw-r--r--src/adsr.c256
-rw-r--r--src/adsr_gt.c264
-rw-r--r--src/amp.c142
-rw-r--r--src/branch.c133
-rw-r--r--src/dahdsr.c685
-rw-r--r--src/difference.c180
-rw-r--r--src/fmod.c192
-rw-r--r--src/include/common.h54
-rw-r--r--src/include/interpolate.h75
-rw-r--r--src/include/lp4pole_filter.h137
-rw-r--r--src/include/math_func.h36
-rw-r--r--src/include/wavedata.h192
-rw-r--r--src/include/wdatutil.h141
-rw-r--r--src/interpolator.c142
-rw-r--r--src/lp4pole.c182
-rw-r--r--src/lp4pole_filter.c55
-rw-r--r--src/product.c156
-rw-r--r--src/pulse.c288
-rw-r--r--src/quantiser.c474
-rw-r--r--src/random.c348
-rw-r--r--src/ratio.c189
-rw-r--r--src/sawtooth.c171
-rw-r--r--src/sequencer.c193
-rw-r--r--src/square.c172
-rw-r--r--src/sum.c156
-rw-r--r--src/sync_pulse.c205
-rw-r--r--src/sync_square.c193
-rw-r--r--src/tracker.c229
-rw-r--r--src/triangle.c307
-rw-r--r--src/wavedata.c141
-rw-r--r--src/wavegen.c319
-rw-r--r--src/wdatutil.c672
32 files changed, 7079 insertions, 0 deletions
diff --git a/src/adsr.c b/src/adsr.c
new file mode 100644
index 0000000..ef4b27e
--- /dev/null
+++ b/src/adsr.c
@@ -0,0 +1,256 @@
+/*
+ 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 {
+ float* signal;
+ float* trigger;
+ float* attack;
+ float* decay;
+ float* sustain;
+ 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 = data;
+ break;
+ case ADSR_TRIGGER:
+ plugin->trigger = data;
+ break;
+ case ADSR_ATTACK:
+ plugin->attack = data;
+ break;
+ case ADSR_DECAY:
+ plugin->decay = data;
+ break;
+ case ADSR_SUSTAIN:
+ plugin->sustain = data;
+ break;
+ case ADSR_RELEASE:
+ plugin->release = data;
+ break;
+ case ADSR_OUTPUT:
+ plugin->output = 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));
+
+ 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
+runAdsr(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Adsr* plugin = (Adsr*)instance;
+
+ /* Driving signal */
+ float* signal = plugin->signal;
+
+ /* Trigger Threshold */
+ float trigger = *(plugin->trigger);
+
+ /* Attack Time (s) */
+ float attack = *(plugin->attack);
+
+ /* Decay Time (s) */
+ float decay = *(plugin->decay);
+
+ /* Sustain Level */
+ 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/blip/adsr",
+ instantiate,
+ connect_port,
+ activate,
+ runAdsr,
+ 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..f9aa225
--- /dev/null
+++ b/src/adsr_gt.c
@@ -0,0 +1,264 @@
+/*
+ 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 {
+ float* gate;
+ float* trigger;
+ float* attack;
+ float* decay;
+ float* sustain;
+ 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 = data;
+ break;
+ case ADSR_TRIGGER:
+ plugin->trigger = data;
+ break;
+ case ADSR_ATTACK:
+ plugin->attack = data;
+ break;
+ case ADSR_DECAY:
+ plugin->decay = data;
+ break;
+ case ADSR_SUSTAIN:
+ plugin->sustain = data;
+ break;
+ case ADSR_RELEASE:
+ plugin->release = data;
+ break;
+ case ADSR_OUTPUT:
+ plugin->output = 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));
+
+ 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
+runAdsr(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Adsr* plugin = (Adsr*)instance;
+
+ /* Gate */
+ float* gate = plugin->gate;
+
+ /* Trigger */
+ float* trigger = plugin->trigger;
+
+ /* Attack Time (s) */
+ float attack = *(plugin->attack);
+
+ /* Decay Time (s) */
+ float decay = *(plugin->decay);
+
+ /* Sustain Level */
+ 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/blip/adsr_gt",
+ instantiate,
+ connect_port,
+ activate,
+ runAdsr,
+ 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..4c528f9
--- /dev/null
+++ b/src/amp.c
@@ -0,0 +1,142 @@
+/*
+ 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/lv2core/lv2.h"
+#include "math_func.h"
+
+#define AMP_GAIN 0
+#define AMP_INPUT 1
+#define AMP_OUTPUT 2
+
+typedef struct {
+ const float* gain;
+ const float* input;
+ float* output;
+} 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 = data;
+ break;
+ case AMP_INPUT:
+ plugin->input = data;
+ break;
+ case AMP_OUTPUT:
+ plugin->output = data;
+ break;
+ }
+}
+
+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));
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+runAmp_gaia_oa(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;
+
+ float gn;
+ float in;
+ float scale;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ gn = gain[s];
+ in = input[s];
+
+ scale = (float)EXPF(M_LN10 * gn * 0.05f);
+
+ output[s] = scale * in;
+ }
+}
+
+static void
+runAmp_gcia_oa(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;
+
+ float in;
+ float scale = (float)EXPF(M_LN10 * gain * 0.05f);
+ for (uint32_t s = 0; s < sample_count; s++) {
+ in = input[s];
+
+ output[s] = scale * in;
+ }
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/amp",
+ instantiate,
+ connect_port,
+ NULL,
+ runAmp_gcia_oa,
+ 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/branch.c b/src/branch.c
new file mode 100644
index 0000000..70329a2
--- /dev/null
+++ b/src/branch.c
@@ -0,0 +1,133 @@
+/*
+ An LV2 plugin to split a signal into two.
+ 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 BRANCH_INPUT 0
+#define BRANCH_OUTPUT1 1
+#define BRANCH_OUTPUT2 2
+
+typedef struct {
+ float* input;
+ float* output1;
+ float* output2;
+} 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 = data;
+ break;
+ case BRANCH_OUTPUT1:
+ plugin->output1 = data;
+ break;
+ case BRANCH_OUTPUT2:
+ plugin->output2 = data;
+ break;
+ }
+}
+
+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));
+
+ 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) */
+ 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) */
+ 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 const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/branch",
+ instantiate,
+ connect_port,
+ NULL,
+ runBranch_ic_ococ,
+ 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/dahdsr.c b/src/dahdsr.c
new file mode 100644
index 0000000..01115bb
--- /dev/null
+++ b/src/dahdsr.c
@@ -0,0 +1,685 @@
+/*
+ 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/lv2core/lv2.h"
+#include "common.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 {
+ float* gate;
+ float* trigger;
+ float* delay;
+ float* attack;
+ float* hold;
+ float* decay;
+ float* sustain;
+ float* release;
+ float* output;
+ float srate;
+ float inv_srate;
+ float last_gate;
+ float last_trigger;
+ float from_level;
+ float level;
+ DAHDSRState state;
+ uint32_t samples;
+} 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 = data;
+ break;
+ case DAHDSR_TRIGGER:
+ plugin->trigger = data;
+ break;
+ case DAHDSR_DELAY:
+ plugin->delay = data;
+ break;
+ case DAHDSR_ATTACK:
+ plugin->attack = data;
+ break;
+ case DAHDSR_HOLD:
+ plugin->hold = data;
+ break;
+ case DAHDSR_DECAY:
+ plugin->decay = data;
+ break;
+ case DAHDSR_SUSTAIN:
+ plugin->sustain = data;
+ break;
+ case DAHDSR_RELEASE:
+ plugin->release = data;
+ break;
+ case DAHDSR_OUTPUT:
+ plugin->output = data;
+ break;
+ }
+}
+
+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));
+
+ plugin->srate = (float)sample_rate;
+ plugin->inv_srate = 1.0f / plugin->srate;
+
+ 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
+runDahdsr_Audio(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Dahdsr* plugin = (Dahdsr*)instance;
+
+ /* Gate */
+ float* gate = plugin->gate;
+
+ /* Trigger */
+ float* trigger = plugin->trigger;
+
+ /* Delay Time (s) */
+ float* delay = plugin->delay;
+
+ /* Attack Time (s) */
+ float* attack = plugin->attack;
+
+ /* Hold Time (s) */
+ float* hold = plugin->hold;
+
+ /* Decay Time (s) */
+ float* decay = plugin->decay;
+
+ /* Sustain Level */
+ float* sustain = plugin->sustain;
+
+ /* Release Time (s) */
+ 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 gat, trg, del, att, hld, dec, sus, rel;
+ float elapsed;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ /* Convert times into rates */
+ del = delay[s] > 0.0f ? inv_srate / delay[s] : srate;
+ att = attack[s] > 0.0f ? inv_srate / attack[s] : srate;
+ hld = hold[s] > 0.0f ? inv_srate / hold[s] : srate;
+ dec = decay[s] > 0.0f ? inv_srate / decay[s] : srate;
+ rel = release[s] > 0.0f ? inv_srate / release[s] : srate;
+
+ gat = gate[s];
+ trg = trigger[s];
+ sus = f_clip(sustain[s], 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 void
+runDahdsr_Control(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Dahdsr* plugin = (Dahdsr*)instance;
+
+ /* Gate */
+ float* gate = plugin->gate;
+
+ /* Trigger */
+ float* trigger = plugin->trigger;
+
+ /* Delay Time (s) */
+ float delay = *(plugin->delay);
+
+ /* Attack Time (s) */
+ float attack = *(plugin->attack);
+
+ /* Hold Time (s) */
+ float hold = *(plugin->hold);
+
+ /* Decay Time (s) */
+ float decay = *(plugin->decay);
+
+ /* Sustain Level */
+ float sustain = *(plugin->sustain);
+
+ /* Release Time (s) */
+ 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 gat, trg, del, att, hld, dec, sus, rel;
+ float elapsed;
+
+ /* Convert times into rates */
+ del = delay > 0.0f ? inv_srate / delay : srate;
+ att = attack > 0.0f ? inv_srate / attack : srate;
+ hld = hold > 0.0f ? inv_srate / hold : srate;
+ dec = decay > 0.0f ? inv_srate / decay : srate;
+ rel = release > 0.0f ? inv_srate / release : srate;
+
+ sus = f_clip(sustain, 0.0f, 1.0f);
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ gat = gate[s];
+ trg = trigger[s];
+
+ /* 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 = gat;
+ last_trigger = trg;
+ }
+
+ 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 void
+runDahdsr_CGT_Control(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Dahdsr* plugin = (Dahdsr*)instance;
+
+ /* Gate */
+ float gate = *(plugin->gate);
+
+ /* Trigger */
+ float trigger = *(plugin->trigger);
+
+ /* Delay Time (s) */
+ float delay = *(plugin->delay);
+
+ /* Attack Time (s) */
+ float attack = *(plugin->attack);
+
+ /* Hold Time (s) */
+ float hold = *(plugin->hold);
+
+ /* Decay Time (s) */
+ float decay = *(plugin->decay);
+
+ /* Sustain Level */
+ float sustain = *(plugin->sustain);
+
+ /* Release Time (s) */
+ 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 gat, trg, del, att, hld, dec, sus, rel;
+ float elapsed;
+
+ /* Convert times into rates */
+ del = delay > 0.0f ? inv_srate / delay : srate;
+ att = attack > 0.0f ? inv_srate / attack : srate;
+ hld = hold > 0.0f ? inv_srate / hold : srate;
+ dec = decay > 0.0f ? inv_srate / decay : srate;
+ rel = release > 0.0f ? inv_srate / release : srate;
+
+ gat = gate;
+ trg = trigger;
+ sus = f_clip(sustain, 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;
+ }
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ 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;
+ }
+
+ plugin->last_gate = gat;
+ plugin->last_trigger = trg;
+ plugin->from_level = from_level;
+ plugin->level = level;
+ plugin->state = state;
+ plugin->samples = samples;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/dahdsr",
+ instantiate,
+ connect_port,
+ activate,
+ runDahdsr_Control,
+ 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/difference.c b/src/difference.c
new file mode 100644
index 0000000..afa9183
--- /dev/null
+++ b/src/difference.c
@@ -0,0 +1,180 @@
+/*
+ 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/lv2core/lv2.h"
+
+#define DIFFERENCE_INPUT 0
+#define DIFFERENCE_MINUS 1
+#define DIFFERENCE_OUTPUT 2
+
+typedef struct {
+ float* input;
+ float* minus;
+ float* output;
+} 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_INPUT:
+ plugin->input = data;
+ break;
+ case DIFFERENCE_MINUS:
+ plugin->minus = data;
+ break;
+ case DIFFERENCE_OUTPUT:
+ plugin->output = data;
+ break;
+ }
+}
+
+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));
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+runDifference_iama_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Difference* plugin = (Difference*)instance;
+
+ /* Input (array of floats of length sample_count) */
+ float* input = plugin->input;
+
+ /* Input to Subtract (array of floats of length sample_count) */
+ float* minus = plugin->minus;
+
+ /* Output (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ float in;
+ float mi;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in = input[s];
+ mi = minus[s];
+
+ output[s] = in - mi;
+ }
+}
+
+static void
+runDifference_iamc_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Difference* plugin = (Difference*)instance;
+
+ /* Input (array of floats of length sample_count) */
+ float* input = plugin->input;
+
+ /* Input to Subtract (float value) */
+ float minus = *(plugin->minus);
+
+ /* Output (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ float in;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in = input[s];
+
+ output[s] = in - minus;
+ }
+}
+
+static void
+runDifference_icma_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Difference* plugin = (Difference*)instance;
+
+ /* Input (float value) */
+ float input = *(plugin->input);
+
+ /* Input to Subtract (array of floats of length sample_count) */
+ float* minus = plugin->minus;
+
+ /* Output (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ float mi;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ mi = minus[s];
+
+ output[s] = input - mi;
+ }
+}
+
+static void
+runDifference_icmc_oc(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Difference* plugin = (Difference*)instance;
+
+ /* Input (float value) */
+ float input = *(plugin->input);
+
+ /* Input to Subtract (float value) */
+ float minus = *(plugin->minus);
+
+ /* Output Frequency (pointer to float value) */
+ float* output = plugin->output;
+
+ output[0] = input - minus;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/difference",
+ instantiate,
+ connect_port,
+ NULL,
+ runDifference_icmc_oc,
+ 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/fmod.c b/src/fmod.c
new file mode 100644
index 0000000..ce3a8f4
--- /dev/null
+++ b/src/fmod.c
@@ -0,0 +1,192 @@
+/*
+ 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/lv2core/lv2.h"
+#include "math_func.h"
+
+#define FMOD_FREQUENCY 0
+#define FMOD_MODULATOR 1
+#define FMOD_OUTPUT 2
+
+typedef struct {
+ float* frequency;
+ float* modulator;
+ float* output;
+} 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 = data;
+ break;
+ case FMOD_MODULATOR:
+ plugin->modulator = data;
+ break;
+ case FMOD_OUTPUT:
+ plugin->output = data;
+ break;
+ }
+}
+
+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));
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+runFmod_fama_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Fmod* plugin = (Fmod*)instance;
+
+ /* Frequency to Modulate (array of floats of length sample_count) */
+ float* frequency = plugin->frequency;
+
+ /* LFO Input (array of floats of length sample_count) */
+ float* modulator = plugin->modulator;
+
+ /* Output Frequency (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ float freq;
+ float mod;
+ float scale;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ freq = frequency[s];
+ mod = modulator[s];
+
+ scale = (float)EXPF(M_LN2 * mod);
+
+ output[s] = scale * freq;
+ }
+}
+
+static void
+runFmod_famc_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Fmod* plugin = (Fmod*)instance;
+
+ /* Frequency to Modulate (array of floats of length sample_count) */
+ float* frequency = plugin->frequency;
+
+ /* Shift (Octaves) (float value) */
+ float modulator = *(plugin->modulator);
+
+ /* Output Frequency (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ float freq;
+ float scale = (float)EXPF(M_LN2 * modulator);
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ freq = frequency[s];
+
+ output[s] = scale * freq;
+ }
+}
+
+static void
+runFmod_fcma_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Fmod* plugin = (Fmod*)instance;
+
+ /* Frequency to Modulate (float value) */
+ float frequency = *(plugin->frequency);
+
+ /* LFO Input (array of floats of length sample_count) */
+ float* modulator = plugin->modulator;
+
+ /* Output Frequency (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ float mod;
+ float scale;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ mod = modulator[s];
+
+ scale = (float)EXPF(M_LN2 * mod);
+
+ output[s] = scale * frequency;
+ }
+}
+
+static void
+runFmod_fcmc_oc(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Fmod* plugin = (Fmod*)instance;
+
+ /* Frequency to Modulate (float value) */
+ float frequency = *(plugin->frequency);
+
+ /* Shift (Octaves) (float value) */
+ float modulator = *(plugin->modulator);
+
+ /* Output Frequency (pointer to float value) */
+ float* output = plugin->output;
+
+ float scale;
+
+ scale = (float)EXPF(M_LN2 * modulator);
+
+ output[0] = scale * frequency;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/fmod",
+ instantiate,
+ connect_port,
+ NULL,
+ runFmod_fcmc_oc,
+ 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/include/common.h b/src/include/common.h
new file mode 100644
index 0000000..0f30aa1
--- /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 blip_common_h
+#define blip_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 /* blip_common_h */
diff --git a/src/include/interpolate.h b/src/include/interpolate.h
new file mode 100644
index 0000000..d61fb3e
--- /dev/null
+++ b/src/include/interpolate.h
@@ -0,0 +1,75 @@
+#ifndef blip_interpolate_h
+#define blip_interpolate_h
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include <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 /* blip_interpolate_h */
diff --git a/src/include/lp4pole_filter.h b/src/include/lp4pole_filter.h
new file mode 100644
index 0000000..adc5cba
--- /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 blip_lp4pole_filter_h
+#define blip_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)
+{
+ 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 /* blip_lp4pole_filter_h */
diff --git a/src/include/math_func.h b/src/include/math_func.h
new file mode 100644
index 0000000..c44075d
--- /dev/null
+++ b/src/include/math_func.h
@@ -0,0 +1,36 @@
+/*
+ * Provide double fallbacks for environments lacking sinf and
+ * friends (e.g. Solaris)
+ */
+
+#ifndef math_func_h
+#define math_func_h
+
+#include <math.h>
+#include "config.h"
+
+#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/wavedata.h b/src/include/wavedata.h
new file mode 100644
index 0000000..82bc70f
--- /dev/null
+++ b/src/include/wavedata.h
@@ -0,0 +1,192 @@
+/*
+ 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 blip_wavedata_h
+#define blip_wavedata_h
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include <config.h>
+#include "math_func.h"
+#include "interpolate.h"
+#include "common.h"
+
+/* Functions identifying wavedata dlls */
+#define BLOP_DLSYM_SAWTOOTH "blip_get_sawtooth"
+#define BLOP_DLSYM_SQUARE "blip_get_square"
+#define BLOP_DLSYM_PARABOLA "blip_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* wdat_descriptor_name,
+ unsigned long 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 /* blip_wavedata_h */
diff --git a/src/include/wdatutil.h b/src/include/wdatutil.h
new file mode 100644
index 0000000..d7bca80
--- /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 blip_wdatutil_h
+#define blip_wdatutil_h
+
+#include <stdio.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "math_func.h"
+#include "wavedata.h"
+
+#define WAVE_TYPE_COUNT 3
+
+extern char* wave_names[];
+extern 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(unsigned long 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,
+ unsigned long 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,
+ char* prefix);
+
+#ifdef __cplusplus
+} /* extern "C" { */
+#endif
+
+#endif /* blip_wdatutil_h */
diff --git a/src/interpolator.c b/src/interpolator.c
new file mode 100644
index 0000000..0324239
--- /dev/null
+++ b/src/interpolator.c
@@ -0,0 +1,142 @@
+/*
+ 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 {
+ 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 = data;
+ break;
+ case INTERPOLATOR_OUTPUT:
+ plugin->output = 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));
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Interpolator* plugin = (Interpolator*)instance;
+
+ plugin->p1 = 0.0f;
+ plugin->p0 = 0.0f;
+}
+
+static void
+runInterpolator(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Interpolator* plugin = (Interpolator*)instance;
+
+ /* Control Input (float value) */
+ 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;
+
+ float interval;
+ float inv_scount = 1.0f / (float)sample_count;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ 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/blip/interpolator",
+ instantiate,
+ connect_port,
+ activate,
+ runInterpolator,
+ 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..d72cb98
--- /dev/null
+++ b/src/lp4pole.c
@@ -0,0 +1,182 @@
+/*
+ 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/lv2core/lv2.h"
+#include "lp4pole_filter.h"
+#include "common.h"
+
+#define LP4POLE_CUTOFF 0
+#define LP4POLE_RESONANCE 1
+#define LP4POLE_INPUT 2
+#define LP4POLE_OUTPUT 3
+
+typedef struct {
+ float* cutoff;
+ float* resonance;
+ float* input;
+ float* output;
+ LP4PoleFilter* lpf;
+} 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 = data;
+ break;
+ case LP4POLE_RESONANCE:
+ plugin->resonance = data;
+ break;
+ case LP4POLE_INPUT:
+ plugin->input = data;
+ break;
+ case LP4POLE_OUTPUT:
+ plugin->output = data;
+ break;
+ }
+}
+
+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);
+ plugin = 0;
+ }
+ }
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Lp4pole* plugin = (Lp4pole*)instance;
+
+ lp4pole_init(plugin->lpf);
+}
+
+static void
+runLp4pole_faraia_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Lp4pole* plugin = (Lp4pole*)instance;
+
+ /* Cutoff Frequency (array of floats of length sample_count) */
+ float* cutoff = plugin->cutoff;
+
+ /* Resonance (array of floats of length sample_count) */
+ float* resonance = plugin->resonance;
+
+ /* Input (array of floats of length sample_count) */
+ float* input = plugin->input;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ LP4PoleFilter* lpf = plugin->lpf;
+
+ float in;
+ float co;
+ float res;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ co = cutoff[s];
+ res = resonance[s];
+ in = input[s];
+
+ lp4pole_set_params(lpf, co, res);
+
+ output[s] = lp4pole_run(lpf, in);
+ }
+}
+
+static void
+runLp4pole_fcrcia_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Lp4pole* plugin = (Lp4pole*)instance;
+
+ /* Cutoff Frequency (float value) */
+ float cutoff = *(plugin->cutoff);
+
+ /* Resonance (float value) */
+ float resonance = *(plugin->resonance);
+
+ /* Input (array of floats of length sample_count) */
+ float* input = plugin->input;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ LP4PoleFilter* lpf = plugin->lpf;
+
+ float in;
+
+ lp4pole_set_params(lpf, cutoff, resonance);
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in = input[s];
+ output[s] = lp4pole_run(lpf, in);
+ }
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/lp4pole",
+ instantiate,
+ connect_port,
+ activate,
+ runLp4pole_fcrcia_oa,
+ 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_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..5c0b488
--- /dev/null
+++ b/src/product.c
@@ -0,0 +1,156 @@
+/*
+ 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/lv2core/lv2.h"
+
+#define PRODUCT_INPUT1 0
+#define PRODUCT_INPUT2 1
+#define PRODUCT_OUTPUT 2
+
+typedef struct {
+ float* input1;
+ float* input2;
+ float* output;
+} 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_INPUT1:
+ plugin->input1 = data;
+ break;
+ case PRODUCT_INPUT2:
+ plugin->input2 = data;
+ break;
+ case PRODUCT_OUTPUT:
+ plugin->output = data;
+ break;
+ }
+}
+
+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));
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+runProduct_iaia_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Product* plugin = (Product*)instance;
+
+ /* First Input (array of floats of length sample_count) */
+ float* input1 = plugin->input1;
+
+ /* Second Input (array of floats of length sample_count) */
+ float* input2 = plugin->input2;
+
+ /* Output (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ float in1;
+ float in2;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in1 = input1[s];
+ in2 = input2[s];
+
+ output[s] = in1 * in2;
+ }
+}
+
+static void
+runProduct_iaic_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Product* plugin = (Product*)instance;
+
+ /* First Input (array of floats of length sample_count) */
+ float* input1 = plugin->input1;
+
+ /* Second Input (float value) */
+ float input2 = *(plugin->input2);
+
+ /* Output (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ float in1;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in1 = input1[s];
+
+ output[s] = in1 * input2;
+ }
+}
+
+static void
+runProduct_icic_oc(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Product* plugin = (Product*)instance;
+
+ /* First Input (float value) */
+ float input1 = *(plugin->input1);
+
+ /* Second Input (float value) */
+ float input2 = *(plugin->input2);
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ output[0] = input1 * input2;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/product",
+ instantiate,
+ connect_port,
+ NULL,
+ runProduct_icic_oc,
+ 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/pulse.c b/src/pulse.c
new file mode 100644
index 0000000..17c8525
--- /dev/null
+++ b/src/pulse.c
@@ -0,0 +1,288 @@
+/*
+ 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/lv2core/lv2.h"
+#include "wavedata.h"
+
+#define PULSE_FREQUENCY 0
+#define PULSE_PULSEWIDTH 1
+#define PULSE_OUTPUT 2
+
+typedef struct {
+ float* frequency;
+ float* pulsewidth;
+ float* output;
+ float phase;
+ Wavedata wdat;
+} Pulse;
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Pulse* plugin = (Pulse*)instance;
+
+ switch (port) {
+ case PULSE_FREQUENCY:
+ plugin->frequency = data;
+ break;
+ case PULSE_PULSEWIDTH:
+ plugin->pulsewidth = data;
+ break;
+ case PULSE_OUTPUT:
+ plugin->output = data;
+ break;
+ }
+}
+
+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 (wavedata_load(&plugin->wdat, BLOP_DLSYM_SAWTOOTH, sample_rate)) {
+ free(plugin);
+ return 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
+runPulse_fapa_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Pulse* plugin = (Pulse*)instance;
+
+ /* Frequency (array of float of length sample_count) */
+ float* frequency = plugin->frequency;
+
+ /* Pulse Width (array of float of length sample_count) */
+ float* pulsewidth = plugin->pulsewidth;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+
+ float freq;
+ float pwidth;
+ float phase_shift;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ freq = frequency[s];
+ pwidth = f_clip(pulsewidth[s], 0.0f, 1.0f);
+ phase_shift = pwidth * wdat->sample_rate;
+
+ /* Lookup which table to use from frequency */
+ wavedata_get_table(wdat, freq);
+
+ /* 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)
+ + 1.0f - (2.0f * pwidth);
+
+ /* 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 void
+runPulse_fapc_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Pulse* plugin = (Pulse*)instance;
+
+ /* Frequency (array of float of length sample_count) */
+ float* frequency = plugin->frequency;
+
+ /* Pulse Width (float value) */
+ float pulsewidth = f_clip(*(plugin->pulsewidth), 0.0f, 1.0f);
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+
+ float freq;
+ float dc_shift = 1.0 - (2.0 * pulsewidth);
+ float phase_shift = pulsewidth * wdat->sample_rate;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ freq = frequency[s];
+
+ /* Lookup which table to use from frequency */
+ wavedata_get_table(wdat, freq);
+
+ /* 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 void
+runPulse_fcpa_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Pulse* plugin = (Pulse*)instance;
+
+ /* Frequency (float value) */
+ float frequency = *(plugin->frequency);
+
+ /* Pulse Width (array of float of length sample_count) */
+ float* pulsewidth = plugin->pulsewidth;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+
+ float pwidth;
+ float phase_shift;
+
+ wavedata_get_table(wdat, frequency);
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ pwidth = f_clip(pulsewidth[s], 0.0f, 1.0f);
+ 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)
+ + 1.0f - (2.0f * pwidth);
+
+ /* 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 void
+runPulse_fcpc_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Pulse* plugin = (Pulse*)instance;
+
+ /* Frequency (float value) */
+ float frequency = *(plugin->frequency);
+
+ /* Pulse Width (float value) */
+ float pulsewidth = f_clip(*(plugin->pulsewidth), 0.0f, 1.0f);
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+
+ float dc_shift = 1.0f - (2.0f * pulsewidth);
+ float phase_shift = pulsewidth * wdat->sample_rate;
+
+ wavedata_get_table(wdat, frequency);
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ /* 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 LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/pulse",
+ instantiate,
+ connect_port,
+ activate,
+ runPulse_fcpc_oa,
+ 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/quantiser.c b/src/quantiser.c
new file mode 100644
index 0000000..ce28866
--- /dev/null
+++ b/src/quantiser.c
@@ -0,0 +1,474 @@
+/*
+ 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 {
+ float* min;
+ float* max;
+ float* match_range;
+ float* mode;
+ float* count;
+ float* values[QUANTISER_MAX_INPUTS];
+ 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
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Quantiser* plugin = (Quantiser*)instance;
+
+ switch (port) {
+ case QUANTISER_RANGE_MIN:
+ plugin->min = data;
+ break;
+ case QUANTISER_RANGE_MAX:
+ plugin->max = data;
+ break;
+ case QUANTISER_MATCH_RANGE:
+ plugin->match_range = data;
+ break;
+ case QUANTISER_MODE:
+ plugin->mode = data;
+ break;
+ case QUANTISER_COUNT:
+ plugin->count = data;
+ break;
+ case QUANTISER_INPUT:
+ plugin->input = data;
+ break;
+ case QUANTISER_OUTPUT:
+ plugin->output = data;
+ break;
+ case QUANTISER_OUTPUT_CHANGED:
+ plugin->output_changed = data;
+ break;
+ default:
+ if (port >= QUANTISER_VALUE_START && port < QUANTISER_OUTPUT) {
+ plugin->values[port - QUANTISER_VALUE_START] = 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;
+}
+
+#if 0
+static void
+runQuantiser_audio(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) */
+ float match_range = *(plugin->match_range);
+
+ /* Mode (float value) */
+ float mode = *(plugin->mode);
+
+ /* Count (float value) */
+ float count = *(plugin->count);
+
+ /* Input (array of float of length sample_count) */
+ float* input = plugin->input;
+
+ /* Values */
+ float* values[QUANTISER_MAX_INPUTS];
+
+ /* 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;
+
+ for (uint32_t s = 0; s < sample_count; s++) {
+ output[s] = input[s];
+ }
+}
+
+#endif
+
+static void
+runQuantiser_control(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) */
+ float match_range = FABSF(*(plugin->match_range));
+
+ /* Mode (float value) */
+ float mode = *(plugin->mode);
+
+ /* Count (float value) */
+ float count = *(plugin->count);
+
+ /* Input (array of float of length sample_count) */
+ 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;
+ 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;
+}
diff --git a/src/random.c b/src/random.c
new file mode 100644
index 0000000..9aab30a
--- /dev/null
+++ b/src/random.c
@@ -0,0 +1,348 @@
+/*
+ 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/lv2core/lv2.h"
+#include <time.h>
+#include "math_func.h"
+#include "common.h"
+
+#define RANDOM_FREQUENCY 0
+#define RANDOM_SMOOTH 1
+#define RANDOM_OUTPUT 2
+
+typedef struct {
+ float* frequency;
+ float* smooth;
+ float* output;
+ float nyquist;
+ float inv_nyquist;
+ float phase;
+ float value1;
+ float value2;
+} 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 = data;
+ break;
+ case RANDOM_SMOOTH:
+ plugin->smooth = data;
+ break;
+ case RANDOM_OUTPUT:
+ plugin->output = data;
+ break;
+ }
+}
+
+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));
+
+ 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;
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Random* plugin = (Random*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+runRandom_fasa_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Random* plugin = (Random*)instance;
+
+ /* Frequency (Hz) (array of floats of length sample_count) */
+ float* frequency = plugin->frequency;
+
+ /* Wave smoothness (array of floats of length sample_count) */
+ 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 freq;
+ float smth;
+ float interval;
+ float result;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ freq = f_clip(frequency[s], 0.0f, nyquist);
+
+ smth = f_clip(smooth[s], 0.0f, 1.0f);
+ 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 void
+runRandom_fasc_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Random* plugin = (Random*)instance;
+
+ /* Frequency (Hz) (array of floats of length sample_count) */
+ float* frequency = plugin->frequency;
+
+ /* Wave smoothness (float value) */
+ float smooth = f_clip(*(plugin->smooth), 0.0f, 1.0f);
+
+ /* 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 freq;
+ float interval = (1.0f - smooth) * 0.5f;
+ float result;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ freq = f_clip(frequency[s], 0.0f, nyquist);
+
+ if (phase < interval) {
+ result = 1.0f;
+ } else if (phase > (1.0f - interval)) {
+ result = -1.0f;
+ } else if (interval > 0.0f) {
+ result = COSF((phase - interval) / smooth * 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 void
+runRandom_fcsa_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Random* plugin = (Random*)instance;
+
+ /* Frequency (Hz) (float value) */
+ float frequency = *(plugin->frequency);
+
+ /* Wave smoothness (array of floats of length sample_count) */
+ float* smooth = plugin->smooth;
+
+ /* Output (pointer to float value) */
+ 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 phase_scale = f_clip(frequency, 0.0f, nyquist) * inv_nyquist;
+ float smth;
+ float interval;
+ float result;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ smth = f_clip(smooth[s], 0.0f, 1.0f);
+ 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 += phase_scale;
+ 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 void
+runRandom_fcsc_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Random* plugin = (Random*)instance;
+
+ /* Frequency (Hz) (float value) */
+ float frequency = *(plugin->frequency);
+
+ /* Wave smoothness (float value) */
+ float smooth = f_clip(*(plugin->smooth), 0.0f, 1.0f);
+
+ /* 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 phase_scale = f_clip(frequency, 0.0f, nyquist) * inv_nyquist;
+ float interval = (1.0f - smooth) * 0.5f;
+ float result;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ if (phase < interval) {
+ result = 1.0f;
+ } else if (phase > (1.0f - interval)) {
+ result = -1.0f;
+ } else if (interval > 0.0f) {
+ result = COSF((phase - interval) / smooth * M_PI);
+ } else {
+ result = COSF(phase * M_PI);
+ }
+
+ result *= (value2 - value1) * 0.5f;
+ result -= (value2 + value1) * 0.5f;
+
+ output[s] = result;
+
+ phase += phase_scale;
+ 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 LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/random",
+ instantiate,
+ connect_port,
+ activate,
+ runRandom_fcsc_oa,
+ 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/ratio.c b/src/ratio.c
new file mode 100644
index 0000000..33fc4e9
--- /dev/null
+++ b/src/ratio.c
@@ -0,0 +1,189 @@
+/*
+ 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/lv2core/lv2.h"
+#include "math_func.h"
+#include "common.h"
+
+#define RATIO_NUMERATOR 0
+#define RATIO_DENOMINATOR 1
+#define RATIO_OUTPUT 2
+
+typedef struct {
+ float* numerator;
+ float* denominator;
+ float* output;
+} 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 = data;
+ break;
+ case RATIO_DENOMINATOR:
+ plugin->denominator = data;
+ break;
+ case RATIO_OUTPUT:
+ plugin->output = data;
+ break;
+ }
+}
+
+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));
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+runRatio_nada_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Ratio* plugin = (Ratio*)instance;
+
+ /* Numerator (array of floats of length sample_count) */
+ float* numerator = plugin->numerator;
+
+ /* Denominator (array of floats of length sample_count) */
+ float* denominator = plugin->denominator;
+
+ /* Output (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ float n;
+ float d;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ n = numerator[s];
+ d = denominator[s];
+
+ d = COPYSIGNF(f_max(FABSF(d), 1e-16f), d);
+
+ output[s] = n / d;
+ }
+}
+
+static void
+runRatio_nadc_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Ratio* plugin = (Ratio*)instance;
+
+ /* Numerator (array of floats of length sample_count) */
+ float* numerator = plugin->numerator;
+
+ /* Denominator (float value) */
+ float denominator = *(plugin->denominator);
+
+ /* Output (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ float n;
+
+ denominator = COPYSIGNF(f_max(FABSF(denominator), 1e-16f), denominator);
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ n = numerator[s];
+
+ output[s] = n / denominator;
+ }
+}
+
+static void
+runRatio_ncda_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Ratio* plugin = (Ratio*)instance;
+
+ /* Numerator (float value) */
+ float numerator = *(plugin->numerator);
+
+ /* Denominator (array of floats of length sample_count) */
+ float* denominator = plugin->denominator;
+
+ /* Output (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ float d;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ d = denominator[s];
+ d = COPYSIGNF(f_max(FABSF(d), 1e-16f), d);
+
+ output[s] = numerator / d;
+ }
+}
+
+static void
+runRatio_ncdc_oc(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Ratio* plugin = (Ratio*)instance;
+
+ /* Numerator (float value) */
+ float numerator = *(plugin->numerator);
+
+ /* Denominator (float value) */
+ float denominator = *(plugin->denominator);
+
+ /* Output Frequency (pointer to float value) */
+ float* output = plugin->output;
+
+ denominator = COPYSIGNF(f_max(FABSF(denominator), 1e-16f), denominator);
+
+ output[0] = numerator / denominator;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/ratio",
+ instantiate,
+ connect_port,
+ NULL,
+ runRatio_ncdc_oc,
+ 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/sawtooth.c b/src/sawtooth.c
new file mode 100644
index 0000000..2e1a3ec
--- /dev/null
+++ b/src/sawtooth.c
@@ -0,0 +1,171 @@
+/*
+ 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/lv2core/lv2.h"
+#include "wavedata.h"
+
+#define SAWTOOTH_FREQUENCY 0
+#define SAWTOOTH_OUTPUT 1
+
+typedef struct {
+ float* frequency;
+ float* output;
+ float phase;
+ Wavedata wdat;
+} Sawtooth;
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Sawtooth* plugin = (Sawtooth*)instance;
+
+ switch (port) {
+ case SAWTOOTH_FREQUENCY:
+ plugin->frequency = data;
+ break;
+ case SAWTOOTH_OUTPUT:
+ plugin->output = data;
+ break;
+ }
+}
+
+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 (wavedata_load(&plugin->wdat, BLOP_DLSYM_SAWTOOTH, sample_rate)) {
+ free(plugin);
+ return 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
+runSawtooth_fa_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Sawtooth* plugin = (Sawtooth*)instance;
+
+ /* Frequency (array of float of length sample_count) */
+ float* frequency = plugin->frequency;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+
+ float freq;
+
+ for (uint32_t s = 0; s < sample_count; s++) {
+ freq = frequency[s];
+
+ /* Lookup 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 void
+runSawtooth_fc_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Sawtooth* plugin = (Sawtooth*)instance;
+
+ /* Frequency (float value) */
+ float frequency = *(plugin->frequency);
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+
+ wavedata_get_table(wdat, frequency);
+
+ for (uint32_t s = 0; s < sample_count; s++) {
+ 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 LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/sawtooth",
+ instantiate,
+ connect_port,
+ activate,
+ runSawtooth_fc_oa,
+ 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/sequencer.c b/src/sequencer.c
new file mode 100644
index 0000000..7b39a50
--- /dev/null
+++ b/src/sequencer.c
@@ -0,0 +1,193 @@
+/*
+ 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 {
+ float* gate;
+ float* trigger;
+ float* loop_steps;
+ float* reset;
+ float* value_gate_closed;
+ 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 = data;
+ break;
+ case SEQUENCER_TRIGGER:
+ plugin->trigger = data;
+ break;
+ case SEQUENCER_LOOP_POINT:
+ plugin->loop_steps = data;
+ break;
+ case SEQUENCER_OUTPUT:
+ plugin->output = data;
+ break;
+ case SEQUENCER_RESET:
+ plugin->reset = data;
+ break;
+ case SEQUENCER_VALUE_GATE_CLOSED:
+ plugin->value_gate_closed = data;
+ break;
+ default:
+ if (port >= SEQUENCER_VALUE_START && port < SEQUENCER_OUTPUT) {
+ plugin->values[port - SEQUENCER_VALUE_START] = 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));
+
+ 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
+runSequencer(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Sequencer* plugin = (Sequencer*)instance;
+
+ /* Gate */
+ float* gate = plugin->gate;
+
+ /* Step Trigger */
+ float* trigger = plugin->trigger;
+
+ /* Loop Steps */
+ float loop_steps = *(plugin->loop_steps);
+
+ /* Reset to Value on Gate Close */
+ 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 = LRINTF(reset);
+ 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;
+}
diff --git a/src/square.c b/src/square.c
new file mode 100644
index 0000000..001a47e
--- /dev/null
+++ b/src/square.c
@@ -0,0 +1,172 @@
+/*
+ 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/lv2core/lv2.h"
+#include "wavedata.h"
+
+#define SQUARE_FREQUENCY 0
+#define SQUARE_OUTPUT 1
+
+typedef struct {
+ float* frequency;
+ float* output;
+ float phase;
+ Wavedata wdat;
+} Square;
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Square* plugin = (Square*)instance;
+
+ switch (port) {
+ case SQUARE_FREQUENCY:
+ plugin->frequency = data;
+ break;
+ case SQUARE_OUTPUT:
+ plugin->output = data;
+ break;
+ }
+}
+
+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 (wavedata_load(&plugin->wdat, BLOP_DLSYM_SQUARE, sample_rate)) {
+ free(plugin);
+ return NULL;
+ }
+
+ 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
+runSquare_fa_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Square* plugin = (Square*)instance;
+
+ /* Frequency (array of float of length sample_count) */
+ float* frequency = plugin->frequency;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+
+ float freq;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ freq = frequency[s];
+
+ /* Get 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 void
+runSquare_fc_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Square* plugin = (Square*)instance;
+
+ /* Frequency (float value) */
+ float frequency = *(plugin->frequency);
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+
+ wavedata_get_table(wdat, frequency);
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ 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 LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/square",
+ instantiate,
+ connect_port,
+ activate,
+ runSquare_fc_oa,
+ 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/sum.c b/src/sum.c
new file mode 100644
index 0000000..5f94796
--- /dev/null
+++ b/src/sum.c
@@ -0,0 +1,156 @@
+/*
+ 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/lv2core/lv2.h"
+
+#define SUM_INPUT1 0
+#define SUM_INPUT2 1
+#define SUM_OUTPUT 2
+
+typedef struct {
+ float* input1;
+ float* input2;
+ float* output;
+} 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 = data;
+ break;
+ case SUM_INPUT2:
+ plugin->input2 = data;
+ break;
+ case SUM_OUTPUT:
+ plugin->output = data;
+ break;
+ }
+}
+
+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));
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+runSum_iaia_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Sum* plugin = (Sum*)instance;
+
+ /* First Input (array of floats of length sample_count) */
+ float* input1 = plugin->input1;
+
+ /* Second Input (array of floats of length sample_count) */
+ float* input2 = plugin->input2;
+
+ /* Output (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ float in1;
+ float in2;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in1 = input1[s];
+ in2 = input2[s];
+
+ output[s] = in1 + in2;
+ }
+}
+
+static void
+runSum_iaic_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Sum* plugin = (Sum*)instance;
+
+ /* First Input (array of floats of length sample_count) */
+ float* input1 = plugin->input1;
+
+ /* Second Input (float value) */
+ float input2 = *(plugin->input2);
+
+ /* Output (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ float in1;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in1 = input1[s];
+
+ output[s] = in1 + input2;
+ }
+}
+
+static void
+runSum_icic_oc(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Sum* plugin = (Sum*)instance;
+
+ /* First Input (float value) */
+ float input1 = *(plugin->input1);
+
+ /* Second Input (float value) */
+ float input2 = *(plugin->input2);
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ output[0] = input1 + input2;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/sum",
+ instantiate,
+ connect_port,
+ NULL,
+ runSum_icic_oc,
+ 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/sync_pulse.c b/src/sync_pulse.c
new file mode 100644
index 0000000..4dc9dad
--- /dev/null
+++ b/src/sync_pulse.c
@@ -0,0 +1,205 @@
+/*
+ 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/lv2core/lv2.h"
+#include "common.h"
+
+#define SYNCPULSE_FREQUENCY 0
+#define SYNCPULSE_PULSEWIDTH 1
+#define SYNCPULSE_GATE 2
+#define SYNCPULSE_OUTPUT 3
+
+typedef struct {
+ float* frequency;
+ float* pulsewidth;
+ float* gate;
+ float* output;
+ float srate;
+ float phase;
+} 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 = data;
+ break;
+ case SYNCPULSE_PULSEWIDTH:
+ plugin->pulsewidth = data;
+ break;
+ case SYNCPULSE_GATE:
+ plugin->gate = data;
+ break;
+ case SYNCPULSE_OUTPUT:
+ plugin->output = data;
+ break;
+ }
+}
+
+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));
+
+ plugin->srate = (float)sample_rate;
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ SyncPulse* plugin = (SyncPulse*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+runSyncPulse_fapaga_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ SyncPulse* plugin = (SyncPulse*)instance;
+
+ /* Frequency (array of float of length sample_count) */
+ float* frequency = plugin->frequency;
+
+ /* Pulse Width (array of float of length sample_count) */
+ float* pulsewidth = plugin->pulsewidth;
+
+ /* Gate (array of float of length sample_count) */
+ float* gate = plugin->gate;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ float phase = plugin->phase;
+ float srate = plugin->srate;
+
+ float freq;
+ float pwidth;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ if (gate[s] > 0.0f) {
+ freq = frequency[s];
+ pwidth = f_clip(pulsewidth[s], 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 void
+runSyncPulse_fcpcga_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ SyncPulse* plugin = (SyncPulse*)instance;
+
+ /* Frequency (float value) */
+ float frequency = *(plugin->frequency);
+
+ /* Pulse Width (float value) */
+ float pulsewidth = f_clip(*(plugin->pulsewidth), 0.0f, 1.0f);
+
+ /* Gate (array of float of length sample_count) */
+ float* gate = plugin->gate;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance Data */
+ float phase = plugin->phase;
+ float srate = plugin->srate;
+
+ pulsewidth *= srate;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ if (gate[s] > 0.0f) {
+ if (phase < pulsewidth) {
+ output[s] = 1.0f;
+ } else {
+ output[s] = -1.0f;
+ }
+
+ phase += frequency;
+ 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 LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/sync_pulse",
+ instantiate,
+ connect_port,
+ activate,
+ runSyncPulse_fcpcga_oa,
+ 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/sync_square.c b/src/sync_square.c
new file mode 100644
index 0000000..df33a09
--- /dev/null
+++ b/src/sync_square.c
@@ -0,0 +1,193 @@
+/*
+ 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/lv2core/lv2.h"
+
+#define SYNCSQUARE_FREQUENCY 0
+#define SYNCSQUARE_GATE 1
+#define SYNCSQUARE_OUTPUT 2
+
+typedef struct {
+ float* frequency;
+ float* gate;
+ float* output;
+ float srate;
+ float nyquist;
+ float phase;
+} 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 = data;
+ break;
+ case SYNCSQUARE_GATE:
+ plugin->gate = data;
+ break;
+ case SYNCSQUARE_OUTPUT:
+ plugin->output = data;
+ break;
+ }
+}
+
+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));
+
+ plugin->srate = (float)sample_rate;
+ plugin->nyquist = (float)(sample_rate / 2);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ SyncSquare* plugin = (SyncSquare*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+runSyncSquare_faga_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ SyncSquare* plugin = (SyncSquare*)instance;
+
+ /* Frequency (array of float of length sample_count) */
+ float* frequency = plugin->frequency;
+
+ /* Gate (array of float of length sample_count) */
+ 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;
+
+ float freq;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ if (gate[s] > 0.0f) {
+ freq = frequency[s];
+
+ 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 void
+runSyncSquare_fcga_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ SyncSquare* plugin = (SyncSquare*)instance;
+
+ /* Frequency (float value) */
+ float frequency = *(plugin->frequency);
+
+ /* Gate (array of float of length sample_count) */
+ 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) {
+ if (phase < nyquist) {
+ output[s] = 1.0f;
+ } else {
+ output[s] = -1.0f;
+ }
+
+ phase += frequency;
+ 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 LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/sync_square",
+ instantiate,
+ connect_port,
+ activate,
+ runSyncSquare_fcga_oa,
+ 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/tracker.c b/src/tracker.c
new file mode 100644
index 0000000..4460a34
--- /dev/null
+++ b/src/tracker.c
@@ -0,0 +1,229 @@
+/*
+ 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/lv2core/lv2.h"
+#include "common.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 {
+ float* gate;
+ float* hattack;
+ float* hdecay;
+ float* lattack;
+ float* ldecay;
+ float* input;
+ float* output;
+ float coeff;
+ float last_value;
+} 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 = data;
+ break;
+ case TRACKER_HATTACK:
+ plugin->hattack = data;
+ break;
+ case TRACKER_HDECAY:
+ plugin->hdecay = data;
+ break;
+ case TRACKER_LATTACK:
+ plugin->lattack = data;
+ break;
+ case TRACKER_LDECAY:
+ plugin->ldecay = data;
+ break;
+ case TRACKER_INPUT:
+ plugin->input = data;
+ break;
+ case TRACKER_OUTPUT:
+ plugin->output = data;
+ break;
+ }
+}
+
+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));
+
+ plugin->coeff = 2.0f * M_PI / (float)sample_rate;
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Tracker* plugin = (Tracker*)instance;
+
+ plugin->last_value = 0.0f;
+}
+
+static void
+runTracker_gaaadaia_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Tracker* plugin = (Tracker*)instance;
+
+ /* Gate (array of floats of length sample_count) */
+ float* gate = plugin->gate;
+
+ /* Gate High Attack Rate (array of floats of length sample_count) */
+ float* hattack = plugin->hattack;
+
+ /* Gate High Decay Rate (array of floats of length sample_count) */
+ float* hdecay = plugin->hdecay;
+
+ /* Gate Low Attack Rate (array of floats of length sample_count) */
+ float* lattack = plugin->lattack;
+
+ /* Gate Low Decay Rate (array of floats of length sample_count) */
+ float* ldecay = plugin->ldecay;
+
+ /* Input (array of floats of length sample_count) */
+ 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;
+
+ float rate;
+ float in;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in = input[s];
+
+ if (gate[s] > 0.0f) {
+ rate = in > last_value ? hattack[s] : hdecay[s];
+ } else {
+ rate = in > last_value ? lattack[s] : ldecay[s];
+ }
+
+ 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 void
+runTracker_gaacdcia_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Tracker* plugin = (Tracker*)instance;
+
+ /* Gate (array of floats of length sample_count) */
+ float* gate = plugin->gate;
+
+ /* Gate High Attack Rate (float value) */
+ float hattack = *(plugin->hattack);
+
+ /* Gate High Decay Rate (float value) */
+ float hdecay = *(plugin->hdecay);
+
+ /* Gate Low Attack Rate (float value) */
+ float lattack = *(plugin->lattack);
+
+ /* Gate Low Decay Rate (float value) */
+ float ldecay = *(plugin->ldecay);
+
+ /* Input (array of floats of length sample_count) */
+ 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;
+
+ float in;
+ float rate;
+
+ hattack = f_min(1.0f, hattack * coeff);
+ hdecay = f_min(1.0f, hdecay * coeff);
+ lattack = f_min(1.0f, lattack * coeff);
+ ldecay = f_min(1.0f, ldecay * coeff);
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in = input[s];
+
+ if (gate[s] > 0.0f) {
+ rate = in > last_value ? hattack : hdecay;
+ } else {
+ rate = in > last_value ? lattack : ldecay;
+ }
+
+ last_value = last_value * (1.0f - rate) + in * rate;
+
+ output[s] = last_value;
+ }
+
+ plugin->last_value = last_value;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/tracker",
+ instantiate,
+ connect_port,
+ activate,
+ runTracker_gaacdcia_oa,
+ 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/triangle.c b/src/triangle.c
new file mode 100644
index 0000000..718369a
--- /dev/null
+++ b/src/triangle.c
@@ -0,0 +1,307 @@
+/*
+ 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/lv2core/lv2.h"
+#include "wavedata.h"
+
+#define TRIANGLE_FREQUENCY 0
+#define TRIANGLE_SLOPE 1
+#define TRIANGLE_OUTPUT 2
+
+typedef struct {
+ float* frequency;
+ float* slope;
+ float* output;
+ float phase;
+ float min_slope;
+ float max_slope;
+ Wavedata wdat;
+} Triangle;
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Triangle* plugin = (Triangle*)instance;
+
+ switch (port) {
+ case TRIANGLE_FREQUENCY:
+ plugin->frequency = data;
+ break;
+ case TRIANGLE_SLOPE:
+ plugin->slope = data;
+ break;
+ case TRIANGLE_OUTPUT:
+ plugin->output = data;
+ break;
+ }
+}
+
+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 (wavedata_load(&plugin->wdat, 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;
+
+ 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
+runTriangle_fasa_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Triangle* plugin = (Triangle*)instance;
+
+ /* Frequency (array of float of length sample_count) */
+ float* frequency = plugin->frequency;
+
+ /* Slope (array of float of length sample_count) */
+ float* slope = plugin->slope;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+ float min_slope = plugin->min_slope;
+ float max_slope = plugin->max_slope;
+
+ float freq;
+ float slp;
+ float phase_shift;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ freq = frequency[s];
+ slp = f_clip(slope[s], min_slope, max_slope);
+ phase_shift = slp * wdat->sample_rate;
+
+ /* Lookup which table to use from frequency */
+ wavedata_get_table(wdat, freq);
+
+ /* 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))
+ / (8.0f * (slp - (slp * slp)));
+
+ /* 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 void
+runTriangle_fasc_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Triangle* plugin = (Triangle*)instance;
+
+ /* Frequency (array of float of length sample_count) */
+ float* frequency = plugin->frequency;
+
+ /* Slope (float value) */
+ float slope = *(plugin->slope);
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+ float min_slope = plugin->min_slope;
+ float max_slope = plugin->max_slope;
+
+ float freq;
+ float phase_shift;
+ float scale;
+
+ slope = f_clip(slope, min_slope, max_slope);
+ scale = 1.0f / (8.0f * (slope - (slope * slope)));
+ phase_shift = slope * wdat->sample_rate;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ freq = frequency[s];
+
+ /* Lookup which table to use from frequency */
+ wavedata_get_table(wdat, freq);
+
+ /* 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 void
+runTriangle_fcsa_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Triangle* plugin = (Triangle*)instance;
+
+ /* Frequency (float value) */
+ float frequency = *(plugin->frequency);
+
+ /* Slope (array of float of length sample_count) */
+ float* slope = plugin->slope;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+ float min_slope = plugin->min_slope;
+ float max_slope = plugin->max_slope;
+
+ float slp;
+ float phase_shift;
+
+ wavedata_get_table(wdat, frequency);
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ slp = f_clip(slope[s], min_slope, max_slope);
+ phase_shift = slp * wdat->sample_rate;
+
+ /* 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))
+ / (8.0f * (slp - (slp * slp)));
+
+ /* 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 void
+runTriangle_fcsc_oa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Triangle* plugin = (Triangle*)instance;
+
+ /* Frequency (float value) */
+ float frequency = *(plugin->frequency);
+
+ /* Slope (float value) */
+ float slope = *(plugin->slope);
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+ float min_slope = plugin->min_slope;
+ float max_slope = plugin->max_slope;
+
+ float scale;
+ float phase_shift;
+
+ slope = f_clip(slope, min_slope, max_slope);
+ scale = 1.0f / (8.0f * (slope - (slope * slope)));
+ phase_shift = slope * wdat->sample_rate;
+
+ wavedata_get_table(wdat, frequency);
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ /* 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 LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blip/triangle",
+ instantiate,
+ connect_port,
+ activate,
+ runTriangle_fcsc_oa,
+ 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/wavedata.c b/src/wavedata.c
new file mode 100644
index 0000000..8f7e359
--- /dev/null
+++ b/src/wavedata.c
@@ -0,0 +1,141 @@
+/*
+ Oscillator wave data generation.
+ 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 <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include <config.h>
+#include "wavedata.h"
+
+#ifndef WAVEDATA_SUBDIR
+#warning *** No wavedata subdir given, using default 'blip_files'
+#define WAVEDATA_SUBDIR "blip_files"
+#endif
+
+int
+wavedata_load(Wavedata* w,
+ const char* wdat_descriptor_name,
+ unsigned long sample_rate)
+{
+ const char* subdir = WAVEDATA_SUBDIR;
+ char* ladspa_path;
+ const char* start;
+ const char* end;
+ int extra;
+ size_t subdirlen = strlen(WAVEDATA_SUBDIR);
+ size_t length;
+ size_t pathlen;
+ char* path;
+ char* filename;
+ DIR* dp;
+ struct dirent* ep;
+ struct stat sb;
+ void* handle;
+ int (*desc_func)(Wavedata*, unsigned long);
+ int retval = -1;
+
+ /* Get LADPSA_PATH, if available */
+ ladspa_path = getenv("LV2_PATH");
+ if (!ladspa_path) {
+ ladspa_path = "/usr/lib/ladspa:/usr/local/lib/ladspa";
+ }
+
+ start = ladspa_path;
+ while (*start != '\0') {
+ while (*start == ':') {
+ start++;
+ }
+ end = start;
+ while (*end != ':' && *end != '\0') {
+ end++;
+ }
+ if (end - start > 0) {
+ extra = (*(end - 1) == '/') ? 0 : 1;
+ path = (char*)malloc(end - start + extra + subdirlen + 1 + 1);
+ if (path) {
+ strncpy(path, start, end - start);
+ if (extra == 1) {
+ path[end - start] = '/';
+ }
+
+ path[end - start + extra] = '\0';
+
+ if (subdirlen > 0) {
+ strncat(path, subdir, subdirlen);
+ path[end - start + extra + subdirlen] = '/';
+ path[end - start + extra + subdirlen + 1] = '\0';
+ } else {
+ path[end - start + extra + subdirlen] = '\0';
+ }
+
+ dp = opendir(path);
+ if (dp) {
+ pathlen = strlen(path);
+ while ((ep = readdir(dp))) {
+ /* Stat file to get type */
+ length = pathlen + strlen(ep->d_name);
+ filename = (char*)malloc(length + 1);
+ if (filename) {
+ strncpy(filename, path, pathlen);
+
+ filename[pathlen] = '\0';
+ filename = strncat(filename, ep->d_name, strlen(ep->d_name));
+ filename[length] = '\0';
+
+ if (!stat(filename, &sb)) {
+ /* We only want regular files */
+ if (S_ISREG(sb.st_mode)) {
+ /* Whew. Now see if we've got the right dll */
+ handle = dlopen(filename, RTLD_NOW);
+
+ if (handle) {
+ desc_func = dlsym(handle, wdat_descriptor_name);
+
+ if (desc_func) {
+ free(filename);
+ free(path);
+ retval = desc_func(w, sample_rate);
+ w->data_handle = handle;
+ return retval;
+ }
+ }
+ }
+ }
+ free(filename);
+ }
+ }
+ closedir(dp);
+ }
+ free(path);
+ }
+ }
+ start = end;
+ }
+ 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..3728920
--- /dev/null
+++ b/src/wavegen.c
@@ -0,0 +1,319 @@
+/*
+ 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 "lv2/lv2plug.in/ns/lv2core/lv2.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;
+ char* filename = NULL;
+ FILE* file;
+ 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 (!strncasecmp(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 = strdup(optarg);
+ break;
+ case 'p':
+ prefix = strdup(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, 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);
+ }
+ }
+ }
+
+ if (filename) {
+ free(filename);
+ }
+
+ if (prefix) {
+ free(prefix);
+ }
+
+ wavedata_cleanup(w);
+
+ return 0;
+}
diff --git a/src/wdatutil.c b/src/wdatutil.c
new file mode 100644
index 0000000..190e8f5
--- /dev/null
+++ b/src/wdatutil.c
@@ -0,0 +1,672 @@
+/*
+ Code to generate wavedata for bandlimited waveforms.
+ 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 <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "common.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
+
+char* wave_names[] = {
+ "Saw",
+ "Square",
+ "Parabola"
+};
+
+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,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ 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;
+}
+
+static 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;
+}
+
+static 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 s;
+ 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];
+ }
+}
+
+int
+wavedata_write(Wavedata* w,
+ FILE* wdat_fp,
+ 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 "lv 2 / 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");
+ /*
+ * 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 (uint32_t 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 (uint32_t 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
+ */
+ fprintf(wdat_fp, "int\n");
+ fprintf(
+ wdat_fp,
+ "blip_get_%s (Wavedata * w, double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)\n" ,
+ data_name);
+ fprintf(wdat_fp, "{\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, "void\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, "\tunsigned long s;\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;
+}
+
+static 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;
+ }
+ }
+}
+
+static 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;
+ }
+ }
+}
+
+static 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;
+ }
+ }
+}