diff options
author | David Robillard <d@drobilla.net> | 2011-10-03 05:22:14 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2011-10-03 05:22:14 +0000 |
commit | e550736dbb862d8333a4d7f2ede9804a3d48326c (patch) | |
tree | 185b6f0483cbfe2fae87bb61b0e123fb2353b5d5 /src | |
download | blop.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.c | 256 | ||||
-rw-r--r-- | src/adsr_gt.c | 264 | ||||
-rw-r--r-- | src/amp.c | 142 | ||||
-rw-r--r-- | src/branch.c | 133 | ||||
-rw-r--r-- | src/dahdsr.c | 685 | ||||
-rw-r--r-- | src/difference.c | 180 | ||||
-rw-r--r-- | src/fmod.c | 192 | ||||
-rw-r--r-- | src/include/common.h | 54 | ||||
-rw-r--r-- | src/include/interpolate.h | 75 | ||||
-rw-r--r-- | src/include/lp4pole_filter.h | 137 | ||||
-rw-r--r-- | src/include/math_func.h | 36 | ||||
-rw-r--r-- | src/include/wavedata.h | 192 | ||||
-rw-r--r-- | src/include/wdatutil.h | 141 | ||||
-rw-r--r-- | src/interpolator.c | 142 | ||||
-rw-r--r-- | src/lp4pole.c | 182 | ||||
-rw-r--r-- | src/lp4pole_filter.c | 55 | ||||
-rw-r--r-- | src/product.c | 156 | ||||
-rw-r--r-- | src/pulse.c | 288 | ||||
-rw-r--r-- | src/quantiser.c | 474 | ||||
-rw-r--r-- | src/random.c | 348 | ||||
-rw-r--r-- | src/ratio.c | 189 | ||||
-rw-r--r-- | src/sawtooth.c | 171 | ||||
-rw-r--r-- | src/sequencer.c | 193 | ||||
-rw-r--r-- | src/square.c | 172 | ||||
-rw-r--r-- | src/sum.c | 156 | ||||
-rw-r--r-- | src/sync_pulse.c | 205 | ||||
-rw-r--r-- | src/sync_square.c | 193 | ||||
-rw-r--r-- | src/tracker.c | 229 | ||||
-rw-r--r-- | src/triangle.c | 307 | ||||
-rw-r--r-- | src/wavedata.c | 141 | ||||
-rw-r--r-- | src/wavegen.c | 319 | ||||
-rw-r--r-- | src/wdatutil.c | 672 |
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; + } + } +} |