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