Files
lmms/plugins/ladspa_effect/calf/modules_small.cpp
Tobias Doerffel f4dbb2ff5c sed: s/( void )/(), s/FALSE/false/, s/TRUE/true
Replaced remaining occurences of old constants and superfluous "void"
on empty argument list of functions.
(cherry picked from commit 166701f9f3)
2009-08-25 01:42:20 +02:00

1917 lines
61 KiB
C++

/* Calf DSP Library
* Small modules for modular synthesizers
*
* Copyright (C) 2001-2008 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <assert.h>
#include <memory.h>
#include <calf/primitives.h>
#include <calf/biquad.h>
#include <calf/inertia.h>
#include <calf/audio_fx.h>
#include <calf/plugininfo.h>
#include <calf/giface.h>
#include <calf/lv2wrap.h>
#include <calf/osc.h>
#include <calf/modules_small.h>
#include <calf/lv2helpers.h>
#include <stdio.h>
#ifdef ENABLE_EXPERIMENTAL
#if USE_LV2
#define LV2_SMALL_WRAPPER(mod, name) static calf_plugins::lv2_small_wrapper<small_plugins::mod##_audio_module> lv2_small_##mod(name);
#else
#define LV2_SMALL_WRAPPER(...)
#endif
#define SMALL_WRAPPERS(mod, name) LV2_SMALL_WRAPPER(mod, name)
#if USE_LV2
using namespace calf_plugins;
using namespace dsp;
using namespace std;
template<class Module> LV2_Descriptor lv2_small_wrapper<Module>::descriptor;
template<class Module> uint32_t lv2_small_wrapper<Module>::poly_port_types;
namespace small_plugins
{
class filter_base: public null_small_audio_module
{
public:
enum { in_signal, in_cutoff, in_resonance, in_count };
enum { out_signal, out_count};
float *ins[in_count];
float *outs[out_count];
static void port_info(plugin_info_iface *pii)
{
pii->audio_port("in", "In").input();
pii->control_port("cutoff", "Cutoff", 1000).input().log_range(20, 20000);
pii->control_port("res", "Resonance", 0.707).input().log_range(0.707, 20);
pii->audio_port("out", "Out").output();
}
dsp::biquad_d1<float> filter;
void activate() {
filter.reset();
}
inline void process_inner(uint32_t count) {
for (uint32_t i = 0; i < count; i++)
outs[out_signal][i] = filter.process(ins[in_signal][i]);
filter.sanitize();
}
};
class lp_filter_audio_module: public filter_base
{
public:
inline void process(uint32_t count) {
filter.set_lp_rbj(clip<float>(*ins[in_cutoff], 0.0001, 0.48 * srate), *ins[in_resonance], srate);
process_inner(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("lowpass12", "12dB/oct RBJ Lowpass", "lv2:LowpassPlugin");
port_info(pii);
}
};
class hp_filter_audio_module: public filter_base
{
public:
inline void process(uint32_t count) {
filter.set_hp_rbj(clip<float>(*ins[in_cutoff], 0.0001, 0.48 * srate), *ins[in_resonance], srate);
process_inner(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("highpass12", "12dB/oct RBJ Highpass", "lv2:HighpassPlugin");
port_info(pii);
}
};
class bp_filter_audio_module: public filter_base
{
public:
inline void process(uint32_t count) {
filter.set_bp_rbj(clip<float>(*ins[in_cutoff], 0.0001, 0.48 * srate), *ins[in_resonance], srate);
process_inner(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("bandpass6", "6dB/oct RBJ Bandpass", "lv2:BandpassPlugin");
port_info(pii);
}
};
class br_filter_audio_module: public filter_base
{
public:
inline void process(uint32_t count) {
filter.set_br_rbj(clip<float>(*ins[in_cutoff], 0.0001, 0.48 * srate), *ins[in_resonance], srate);
process_inner(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("notch6", "6dB/oct RBJ Bandpass", "lv2:FilterPlugin");
port_info(pii);
}
};
class onepole_filter_base: public null_small_audio_module
{
public:
enum { in_signal, in_cutoff, in_count };
enum { out_signal, out_count};
float *ins[in_count];
float *outs[out_count];
dsp::onepole<float> filter;
static parameter_properties param_props[];
static void port_info(plugin_info_iface *pii)
{
pii->audio_port("In", "in").input();
pii->control_port("Cutoff", "cutoff", 1000).input().log_range(20, 20000);
pii->audio_port("Out", "out").output();
}
/// do not export mode and inertia as CVs, as those are settings and not parameters
void activate() {
filter.reset();
}
};
class onepole_lp_filter_audio_module: public onepole_filter_base
{
public:
void process(uint32_t count) {
filter.set_lp(clip<float>(*ins[in_cutoff], 0.0001, 0.48 * srate), srate);
for (uint32_t i = 0; i < count; i++)
outs[0][i] = filter.process_lp(ins[0][i]);
filter.sanitize();
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("lowpass6", "6dB/oct Lowpass Filter", "lv2:LowpassPlugin");
port_info(pii);
}
};
class onepole_hp_filter_audio_module: public onepole_filter_base
{
public:
void process(uint32_t count) {
filter.set_hp(clip<float>(*ins[in_cutoff], 0.0001, 0.48 * srate), srate);
for (uint32_t i = 0; i < count; i++)
outs[0][i] = filter.process_hp(ins[0][i]);
filter.sanitize();
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("highpass6", "6dB/oct Highpass Filter", "lv2:HighpassPlugin");
port_info(pii);
}
};
class onepole_ap_filter_audio_module: public onepole_filter_base
{
public:
void process(uint32_t count) {
filter.set_ap(clip<float>(*ins[in_cutoff], 0.0001, 0.48 * srate), srate);
for (uint32_t i = 0; i < count; i++)
outs[0][i] = filter.process_ap(ins[0][i]);
filter.sanitize();
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("allpass", "1-pole 1-zero Allpass Filter", "lv2:AllpassPlugin");
port_info(pii);
}
};
/// This works for 1 or 2 operands only...
template<int Inputs>
class audio_operator_audio_module: public small_audio_module_base<Inputs, 1>
{
public:
static void port_info(plugin_info_iface *pii)
{
if (Inputs == 1)
pii->audio_port("in", "In", "").input();
else
{
pii->audio_port("in_1", "In 1", "").input();
pii->audio_port("in_2", "In 2", "").input();
}
pii->audio_port("out", "Out", "").output();
}
};
class min_audio_module: public audio_operator_audio_module<2>
{
public:
void process(uint32_t count) {
for (uint32_t i = 0; i < count; i++)
outs[0][i] = std::min(ins[0][i], ins[1][i]);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("min", "Minimum (A)", "kf:MathOperatorPlugin", "min");
port_info(pii);
}
};
class max_audio_module: public audio_operator_audio_module<2>
{
public:
void process(uint32_t count) {
for (uint32_t i = 0; i < count; i++)
outs[0][i] = std::max(ins[0][i], ins[1][i]);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("max", "Maximum (A)", "kf:MathOperatorPlugin", "max");
port_info(pii);
}
};
class minus_audio_module: public audio_operator_audio_module<2>
{
public:
void process(uint32_t count) {
for (uint32_t i = 0; i < count; i++)
outs[0][i] = ins[0][i] - ins[1][i];
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("minus", "Subtract (A)", "kf:MathOperatorPlugin", "-");
port_info(pii);
}
};
class mul_audio_module: public audio_operator_audio_module<2>
{
public:
void process(uint32_t count) {
for (uint32_t i = 0; i < count; i++)
outs[0][i] = ins[0][i] * ins[1][i];
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("mul", "Multiply (A)", "kf:MathOperatorPlugin", "*");
port_info(pii);
}
};
class neg_audio_module: public audio_operator_audio_module<1>
{
public:
void process(uint32_t count) {
for (uint32_t i = 0; i < count; i++)
outs[0][i] = -ins[0][i];
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("neg", "Negative (A)", "kf:MathOperatorPlugin", "-");
port_info(pii);
}
};
template<class T, int Inputs> struct polymorphic_process;
template<class T> struct polymorphic_process<T, 1>
{
static inline void run(float **ins, float **outs, uint32_t count, uint32_t poly_port_types) {
if (poly_port_types < 2) // control to control or audio to control
*outs[0] = T::process_single(*ins[0]);
else if (poly_port_types == 2) {
outs[0][0] = T::process_single(ins[0][0]); // same as above, but the index might not be 0 in later versions
for (uint32_t i = 1; i < count; i++)
outs[0][i] = outs[0][0];
}
else { // audio to audio
for (uint32_t i = 0; i < count; i++)
outs[0][i] = T::process_single(ins[0][i]);
}
};
};
template<class T> struct polymorphic_process<T, 2>
{
static inline void run(float **ins, float **outs, uint32_t count, uint32_t poly_port_types) {
poly_port_types &= ~1;
if (poly_port_types < 4) // any to control
*outs[0] = T::process_single(*ins[0], *ins[1]);
else if (poly_port_types == 4) { // control+control to audio
outs[0][0] = T::process_single(*ins[0], *ins[1]); // same as above, but the index might not be 0 in later versions
for (uint32_t i = 1; i < count; i++)
outs[0][i] = outs[0][0];
}
else { // {control+audio or audio+control or audio+audio} to audio
// use masks to force 0 for index for control ports
uint32_t mask1 = null_small_audio_module::port_audio_mask(0, poly_port_types);
uint32_t mask2 = null_small_audio_module::port_audio_mask(1, poly_port_types);
for (uint32_t i = 0; i < count; i++)
outs[0][i] = T::process_single(ins[0][i & mask1], ins[1][i & mask2]);
}
};
};
/// This works for 1 or 2 operands only...
template<int Inputs>
class control_operator_audio_module: public small_audio_module_base<Inputs, 1>
{
public:
using small_audio_module_base<Inputs, 1>::ins;
using small_audio_module_base<Inputs, 1>::outs;
using small_audio_module_base<Inputs, 1>::poly_port_types;
static void port_info(plugin_info_iface *pii, control_port_info_iface *cports[Inputs + 1], float in1 = 0, float in2 = 0)
{
int idx = 0;
if (Inputs == 1)
cports[idx++] = &pii->control_port("in", "In", in1, "").polymorphic().poly_audio().input();
else
{
cports[idx++] = &pii->control_port("in_1", "In 1", in1, "").polymorphic().poly_audio().input();
cports[idx++] = &pii->control_port("in_2", "In 2", in2, "").polymorphic().poly_audio().input();
}
cports[idx++] = &pii->control_port("out", "Out", 0, "").poly_audio().output();
}
template<class T> inline void do_process(uint32_t count) {
polymorphic_process<T, Inputs>::run(ins, outs, count, poly_port_types);
}
};
class minus_c_audio_module: public control_operator_audio_module<2>
{
public:
static inline float process_single(float x, float y) {
return x - y;
}
void process(uint32_t count) {
do_process<minus_c_audio_module>(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("minus_c", "Subtract (C)", "kf:MathOperatorPlugin", "-");
control_port_info_iface *cports[3];
port_info(pii, cports);
}
};
class mul_c_audio_module: public control_operator_audio_module<2>
{
public:
static inline float process_single(float x, float y) {
return x * y;
}
void process(uint32_t count) {
do_process<mul_c_audio_module>(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("mul_c", "Multiply (C)", "kf:MathOperatorPlugin", "*");
control_port_info_iface *cports[3];
port_info(pii, cports);
}
};
class neg_c_audio_module: public control_operator_audio_module<1>
{
public:
static inline float process_single(float x) {
return -x;
}
void process(uint32_t count) {
do_process<neg_c_audio_module>(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("neg_c", "Negative (C)", "kf:MathOperatorPlugin", "-");
control_port_info_iface *cports[2];
port_info(pii, cports);
}
};
class min_c_audio_module: public control_operator_audio_module<2>
{
public:
static inline float process_single(float x, float y) {
return std::min(x, y);
}
void process(uint32_t count) {
do_process<min_c_audio_module>(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("min_c", "Minimum (C)", "kf:MathOperatorPlugin", "min");
control_port_info_iface *cports[3];
port_info(pii, cports);
}
};
class max_c_audio_module: public control_operator_audio_module<2>
{
public:
static inline float process_single(float x, float y) {
return std::max(x, y);
}
void process(uint32_t count) {
do_process<max_c_audio_module>(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("max_c", "Maximum (C)", "kf:MathOperatorPlugin", "max");
control_port_info_iface *cports[3];
port_info(pii, cports);
}
};
class less_c_audio_module: public control_operator_audio_module<2>
{
public:
static inline float process_single(float x, float y) {
return x < y;
}
void process(uint32_t count) {
do_process<less_c_audio_module>(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("less_c", "Less than (C)", "kf:MathOperatorPlugin", "<");
control_port_info_iface *cports[2];
port_info(pii, cports);
cports[2]->toggle();
}
};
class level2edge_c_audio_module: public control_operator_audio_module<1>
{
public:
bool last_value;
void activate() {
last_value = false;
}
void process(uint32_t count) {
*outs[0] = (*ins[0] > 0 && !last_value) ? 1.f : 0.f;
last_value = *ins[0] > 0;
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("level2edge_c", "Level to edge (C)", "kf:BooleanPlugin");
control_port_info_iface *cports[2];
port_info(pii, cports);
cports[0]->toggle();
cports[1]->toggle().trigger();
}
};
class int_c_audio_module: public control_operator_audio_module<1>
{
public:
static inline float process_single(float x) {
return (int)x;
}
void process(uint32_t count) {
do_process<int_c_audio_module>(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("int_c", "Integer value (C)", "kf:IntegerPlugin");
control_port_info_iface *cports[2];
port_info(pii, cports);
cports[0]->integer();
cports[1]->integer();
}
};
class bitwise_op_c_module_base: public control_operator_audio_module<2>
{
public:
static void port_info(plugin_info_iface *pii)
{
pii->control_port("in_1", "In 1", 0, "").polymorphic().poly_audio().integer().input();
pii->control_port("in_2", "In 2", 0, "").polymorphic().poly_audio().integer().input();
pii->control_port("out", "Out", 0, "").polymorphic().poly_audio().integer().output();
}
};
class bit_and_c_audio_module: public bitwise_op_c_module_base
{
public:
static inline float process_single(float x, float y) {
return ((int)x) & ((int)y);
}
void process(uint32_t count) {
do_process<bit_and_c_audio_module>(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("bit_and_c", "Bitwise AND (C)", "kf:IntegerPlugin");
port_info(pii);
}
};
class bit_or_c_audio_module: public bitwise_op_c_module_base
{
public:
static inline float process_single(float x, float y) {
return ((int)x) | ((int)y);
}
void process(uint32_t count) {
do_process<bit_or_c_audio_module>(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("bit_or_c", "Bitwise OR (C)", "kf:IntegerPlugin");
port_info(pii);
}
};
class bit_xor_c_audio_module: public bitwise_op_c_module_base
{
public:
static inline float process_single(float x, float y) {
return ((int)x) ^ ((int)y);
}
void process(uint32_t count) {
do_process<bit_xor_c_audio_module>(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("bit_xor_c", "Bitwise XOR (C)", "kf:IntegerPlugin");
port_info(pii);
}
};
class flipflop_c_audio_module: public control_operator_audio_module<1>
{
public:
bool last_value, output;
void activate() {
last_value = false;
output = false;
}
void process(uint32_t count) {
if (*ins[0] > 0 && !last_value)
output = !output;
*outs[0] = output ? 1.f : 0.f;
last_value = *ins[0] > 0;
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("flipflop_c", "T Flip-Flop (C)", "kf:BooleanPlugin");
control_port_info_iface *cports[2];
port_info(pii, cports);
cports[0]->toggle().trigger();
cports[1]->toggle();
}
};
class logical_and_c_audio_module: public control_operator_audio_module<2>
{
public:
static inline float process_single(float x, float y) {
return (x > 0 && y > 0) ? 1.f : 0.f;
}
void process(uint32_t count) {
do_process<logical_and_c_audio_module>(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("logical_and_c", "Logical AND (C)", "kf:BooleanPlugin", "&&");
control_port_info_iface *cports[3];
port_info(pii, cports);
cports[0]->toggle();
cports[1]->toggle();
cports[2]->toggle();
}
};
class logical_or_c_audio_module: public control_operator_audio_module<2>
{
public:
static inline float process_single(float x, float y) {
return (x > 0 || y > 0) ? 1.f : 0.f;
}
void process(uint32_t count) {
do_process<logical_or_c_audio_module>(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("logical_or_c", "Logical OR (C)", "kf:BooleanPlugin", "||");
control_port_info_iface *cports[3];
port_info(pii, cports);
cports[0]->toggle();
cports[1]->toggle();
cports[2]->toggle();
}
};
class logical_xor_c_audio_module: public control_operator_audio_module<2>
{
public:
static inline float process_single(float x, float y) {
return ((x > 0) != (y > 0)) ? 1.f : 0.f;
}
void process(uint32_t count) {
do_process<logical_xor_c_audio_module>(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("logical_xor_c", "Logical XOR (C)", "kf:BooleanPlugin", "xor");
control_port_info_iface *cports[3];
port_info(pii, cports);
cports[0]->toggle();
cports[1]->toggle();
cports[2]->toggle();
}
};
class logical_not_c_audio_module: public control_operator_audio_module<1>
{
public:
static inline float process_single(float x) {
return (x <= 0) ? 1.f : 0.f;
}
void process(uint32_t count) {
do_process<logical_not_c_audio_module>(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("logical_not_c", "Logical NOT (C)", "kf:BooleanPlugin", "!");
control_port_info_iface *cports[2];
port_info(pii, cports);
cports[0]->toggle();
cports[1]->toggle();
}
};
/// converter of trigger signals from audio to control rate
class trigger_a2c_audio_module: public null_small_audio_module
{
public:
enum { in_count = 1, out_count = 1 };
float *ins[in_count];
float *outs[out_count];
void process(uint32_t count) {
for (uint32_t i = 0; i < count; i++)
{
if (ins[0][i] > 0)
{
*outs[0] = 1.f;
return;
}
}
*outs[0] = 0.f;
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("trigger_a2c", "Audio-to-control OR", "kf:BooleanPlugin", "ta2c");
pii->audio_port("in", "In").input();
pii->control_port("out", "Out", 0.f).output().toggle();
}
};
/// Monostable multivibrator like 74121 or 74123, with reset input, progress output (0 to 1), "finished" signal, configurable to allow or forbid retriggering.
class timer_c_audio_module: public null_small_audio_module
{
public:
enum { in_trigger, in_time, in_reset, in_allow_retrig, in_count };
enum { out_running, out_finished, out_progress, out_count };
float *ins[in_count];
float *outs[out_count];
bool running, finished, old_trigger;
double state;
void activate()
{
state = 0.f;
running = false;
finished = false;
old_trigger = false;
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("timer_c", "Timer (C)", "lv2:UtilityPlugin");
pii->control_port("trigger", "Trigger", 0.f).input().toggle().trigger();
pii->control_port("time", "Time", 0.f).input();
pii->control_port("reset", "Reset", 0).input().toggle();
pii->control_port("allow_retrig", "Allow retrig", 0).input().toggle();
pii->control_port("running", "Running", 0.f).output().toggle();
pii->control_port("finished", "Finished", 0.f).output().toggle();
pii->control_port("progress", "Progress", 0.f).output().lin_range(0, 1);
}
void process(uint32_t count)
{
// This is a branch city, which is definitely a bad thing.
// Perhaps I'll add a bunch of __builtin_expect hints some day, but somebody would have to start using it first.
if (*ins[in_reset] > 0)
{
state = 0.0;
running = finished = false;
}
else
if (!old_trigger && *ins[in_trigger] > 0 && (!running || *ins[in_allow_retrig] > 0))
{
state = 0.0;
running = true;
finished = false;
}
else
if (running)
{
float rate = (1.0 / std::max(0.0000001f, *ins[in_time]));
state += rate * odsr * count;
if (state >= 1.0)
{
running = false;
finished = true;
state = 1.0;
}
}
old_trigger = *ins[in_trigger] > 0;
*outs[out_running] = running ? 1.f : 0.f;
*outs[out_finished] = finished ? 1.f : 0.f;
*outs[out_progress] = state;
}
};
/// 4-input priority multiplexer - without inertia. Outputs the first input if gate_1 is true, else second input if gate_2 is true, else... else "Else" input
class prio_mux_c_audio_module: public null_small_audio_module
{
public:
enum { in_in1, in_gate1, in_in2, in_gate2, in_in3, in_gate3, in_in4, in_gate4, in_else, in_count };
enum { out_value, out_count };
float *ins[in_count];
float *outs[out_count];
static void plugin_info(plugin_info_iface *pii)
{
pii->names("prio_mux_c", "Priority Multiplexer (C)", "kf:BooleanPlugin");
for (int i = 1; i <= 4; i++)
{
stringstream numb;
numb << i;
string num = numb.str();
pii->control_port("in_"+num, "In "+num, 0.f).input();
pii->control_port("gate_"+num, "Gate "+num, 0.f).input().toggle();
}
pii->control_port("in_else", "Else", 0.f).input();
pii->control_port("out", "Out", 0.f).output();
}
void process(uint32_t count)
{
for (int i = 0; i < 4; i++)
{
if (*ins[i * 2 + in_gate1] > 0)
{
*outs[out_value] = *ins[i * 2 + in_in1];
return;
}
}
*outs[out_value] = *ins[in_else];
}
};
/// 8-input priority encoder - outputs the index of the first port whose value is >0. 'Any' output is set whenever any of gates is set (which tells
/// apart no inputs set and 0th input set).
template<int N>
class prio_enc_c_audio_module: public null_small_audio_module
{
public:
enum { in_gate1, in_count = in_gate1 + N};
enum { out_value, out_any, out_count };
float *ins[in_count];
float *outs[out_count];
static void plugin_info(plugin_info_iface *pii)
{
char buf[32], buf2[64];
sprintf(buf, "prio_enc%d_c", N);
sprintf(buf2, "%d-input Priority Encoder (C)", N);
pii->names(buf, buf2, "kf:IntegerPlugin");
for (int i = 0; i < N; i++)
{
stringstream numb;
numb << i;
string num = numb.str();
pii->control_port("gate_"+num, "Gate "+num, 0.f).input().toggle();
}
pii->control_port("out", "Out", -1).output().integer();
pii->control_port("any", "Any", -1).output().toggle();
}
void process(uint32_t count)
{
for (int i = 0; i < N; i++)
{
if (*ins[in_gate1 + i] > 0)
{
*outs[out_value] = i;
*outs[out_any] = 1;
return;
}
}
*outs[out_value] = 0;
*outs[out_any] = 0;
}
};
typedef prio_enc_c_audio_module<8> prio_enc8_c_audio_module;
/// 8-input integer multiplexer, outputs the input selected by ((int)select input & 7)
template<int N>
class mux_c_audio_module: public null_small_audio_module
{
public:
enum { in_select, in_in1, in_count = in_in1 + N};
enum { out_value, out_count };
float *ins[in_count];
float *outs[out_count];
static void plugin_info(plugin_info_iface *pii)
{
char buf[32], buf2[64];
sprintf(buf, "mux%d_c", N);
sprintf(buf2, "%d-input Multiplexer (C)", N);
pii->names(buf, buf2, "kf:IntegerPlugin");
pii->control_port("select", "Select", 0.f).input().integer().lin_range(0, N - 1);
for (int i = 0; i < N; i++)
{
stringstream numb;
numb << i;
string num = numb.str();
pii->control_port("in_"+num, "In "+num, 0.f).input();
}
pii->control_port("out", "Out", -1).output();
}
void process(uint32_t count)
{
*outs[out_value] = *ins[in_in1 + ((N - 1) & (int)*ins[in_select])];
}
};
typedef mux_c_audio_module<4> mux4_c_audio_module;
typedef mux_c_audio_module<8> mux8_c_audio_module;
typedef mux_c_audio_module<16> mux16_c_audio_module;
/// Linear-to-exponential mapper
class map_lin2exp_audio_module: public null_small_audio_module
{
public:
enum { in_signal, in_from_min, in_from_max, in_to_min, in_to_max, in_count };
enum { out_signal, out_count };
float *ins[in_count];
float *outs[out_count];
static void plugin_info(plugin_info_iface *pii)
{
pii->names("lin2exp", "Lin-Exp Mapper", "lv2:UtilityPlugin");
pii->control_port("in", "In", 0.f).input();
pii->control_port("from_min", "Min (from)", 0).input();
pii->control_port("from_max", "Max (from)", 1).input();
pii->control_port("to_min", "Min (to)", 20).input();
pii->control_port("to_max", "Max (to)", 20000).input();
pii->control_port("out", "Out", 0.f).output();
}
void process(uint32_t count)
{
float normalized = (*ins[in_signal] - *ins[in_from_min]) / (*ins[in_from_max] - *ins[in_from_min]);
*outs[out_signal] = *ins[in_to_min] * pow(*ins[in_to_max] / *ins[in_to_min], normalized);
}
};
/// Schmitt trigger - http://en.wikipedia.org/wiki/Schmitt_trigger - also outputs a change signal (positive spike whenever output value is changed)
class schmitt_c_audio_module: public null_small_audio_module
{
public:
enum { in_signal, in_low, in_high, in_count };
enum { out_signal, out_change, out_count };
float *ins[in_count];
float *outs[out_count];
bool state;
void activate()
{
state = false;
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("schmitt_c", "Schmitt Trigger (C)", "kf:BooleanPlugin");
pii->control_port("in", "In", 0.f).input();
pii->control_port("low", "Low threshold", 0).input();
pii->control_port("high", "High threshold", 0.5).input();
pii->control_port("out", "Out", 0.f).output();
pii->control_port("change", "Change", 0.f).output();
}
void process(uint32_t count)
{
float value = *ins[in_signal];
bool new_state = state;
if (value <= *ins[in_low])
new_state = false;
if (value >= *ins[in_high])
new_state = true;
*outs[out_signal] = new_state ? 1.f : 0.f;
*outs[out_change] = (new_state != state) ? 1.f : 0.f;
state = new_state;
}
};
/// Stateless 'between' operator (lo <= in <= hi)
class between_c_audio_module: public null_small_audio_module
{
public:
enum { in_signal, in_low, in_high, in_count };
enum { out_signal, out_count };
float *ins[in_count];
float *outs[out_count];
static void plugin_info(plugin_info_iface *pii)
{
pii->names("between_c", "Between (C)", "kf:MathOperatorPlugin");
pii->control_port("in", "In", 0.f).input();
pii->control_port("low", "Low threshold", 0).input();
pii->control_port("high", "High threshold", 1).input();
pii->control_port("out", "Out", 0.f).output();
}
void process(uint32_t count)
{
float value = *ins[in_signal];
*outs[out_signal] = (value >= *ins[in_low] && value <= *ins[in_high]) ? 1.f : 0.f;
}
};
/// Clip to range
class clip_c_audio_module: public null_small_audio_module
{
public:
enum { in_signal, in_min, in_max, in_count };
enum { out_signal, out_count };
float *ins[in_count];
float *outs[out_count];
static void plugin_info(plugin_info_iface *pii)
{
pii->names("clip_c", "Clip (C)", "kf:MathOperatorPlugin", "clip");
pii->control_port("in", "In", 0.f).input();
pii->control_port("min", "Min", 0).input();
pii->control_port("max", "Max", 1).input();
pii->control_port("out", "Out", 0.f).output();
}
void process(uint32_t count)
{
float value = *ins[in_signal];
*outs[out_signal] = std::min(*ins[in_max], std::max(value, *ins[in_min]));
}
};
/// Two input control crossfader
class crossfader2_c_audio_module: public null_small_audio_module
{
public:
enum { in_a, in_b, in_ctl, in_count };
enum { out_value, out_count };
float *ins[in_count];
float *outs[out_count];
static void plugin_info(plugin_info_iface *pii)
{
pii->names("crossfader2_c", "2-in crossfader (C)", "kf:MathOperatorPlugin", "xfC");
pii->control_port("in_a", "In A", 0.f).input();
pii->control_port("in_b", "In B", 0).input();
pii->control_port("mix", "B in mix", 0.5).input();
pii->control_port("out", "Out", 0.f).output();
}
void process(uint32_t count)
{
*outs[out_value] = *ins[in_a] + (*ins[in_b] - *ins[in_a]) * dsp::clip(*ins[in_ctl], 0.f, 1.f);
}
};
/// 2-input multiplexer (if-then-else)
class ifthenelse_c_audio_module: public null_small_audio_module
{
public:
enum { in_if, in_then, in_else, in_count };
enum { out_value, out_count };
float *ins[in_count];
float *outs[out_count];
static void plugin_info(plugin_info_iface *pii)
{
pii->names("ifthenelse_c", "If-Then-Else (C)", "kf:BooleanPlugin", "if");
pii->control_port("if", "If", 0.f).input().toggle();
pii->control_port("then", "Then", 0).input();
pii->control_port("else", "Else", 0).input();
pii->control_port("out", "Out", 0.f).output();
}
void process(uint32_t count)
{
*outs[out_value] = *ins[in_if] > 0 ? *ins[in_then] : *ins[in_else];
}
};
/// Integer counter with definable ranges
class counter_c_audio_module: public null_small_audio_module
{
public:
enum { in_clock, in_min, in_max, in_steps, in_reset, in_count };
enum { out_value, out_carry, out_count };
float *ins[in_count];
float *outs[out_count];
bool state;
int value;
void activate()
{
state = false;
value = 0;
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("counter_c", "Counter (C)", "kf:IntegerPlugin", "cnt");
pii->control_port("clock", "Clock", 0.f).input().toggle().trigger();
pii->control_port("min", "Min", 0).input();
pii->control_port("max", "Max", 15).input();
pii->control_port("steps", "Steps", 16).input().integer();
pii->control_port("reset", "Reset", 0).input().toggle();
pii->control_port("out", "Out", 0.f).output();
pii->control_port("carry", "Carry", 0.f).output().toggle().trigger();
}
void process(uint32_t count)
{
// Yes, this is slower than it should be. I will bother optimizing it when someone starts actually using it.
if (*ins[in_reset] > 0 || *ins[in_steps] < 2.0)
{
state = false;
value = 0;
*outs[out_value] = *ins[in_min];
*outs[out_carry] = 0.f;
return;
}
*outs[out_carry] = 0;
if (!state && *ins[in_clock] > 0)
{
value++;
state = true;
if (value >= (int)*ins[in_steps])
{
value = 0;
*outs[out_carry] = 1;
}
}
else
state = *ins[in_clock] > 0;
*outs[out_value] = *ins[in_min] + (*ins[in_max] - *ins[in_min]) * value / (int)(*ins[in_steps] - 1);
}
};
/// Two input audio crossfader
class crossfader2_a_audio_module: public null_small_audio_module
{
public:
enum { in_a, in_b, in_ctl, in_count };
enum { out_value, out_count };
float *ins[in_count];
float *outs[out_count];
static void plugin_info(plugin_info_iface *pii)
{
pii->names("crossfader2_a", "2-in crossfader (A)", "kf:MathOperatorPlugin", "xfA");
pii->audio_port("in_a", "In A").input();
pii->audio_port("in_b", "In B").input();
pii->audio_port("mix", "B in mix").input();
pii->audio_port("out", "Out").output();
}
void process(uint32_t count)
{
for (uint32_t i = 0; i < count; i++)
outs[out_value][i] = ins[in_a][i] + (ins[in_b][i] - ins[in_a][i]) * dsp::clip(ins[in_ctl][i], 0.f, 1.f);
}
};
/// Base class for LFOs with frequency and reset inputs
class freq_phase_lfo_base: public small_audio_module_base<2, 1>
{
public:
enum { in_freq, in_reset };
double phase;
inline void activate()
{
phase = 0;
}
static void port_info(plugin_info_iface *pii)
{
pii->control_port("freq", "Frequency", 1).input().log_range(0.02, 100);
pii->control_port("reset", "Reset", 0).input().toggle();
pii->control_port("out", "Out", 0).output();
}
inline void check_inputs()
{
if (*ins[in_reset])
phase = 0;
}
inline void advance(uint32_t count)
{
phase += count * *ins[in_freq] * odsr;
if (phase >= 1.0)
phase = fmod(phase, 1.0);
}
};
class square_lfo_audio_module: public freq_phase_lfo_base
{
public:
void process(uint32_t count)
{
check_inputs();
*outs[0] = (phase < 0.5) ? -1 : +1;
advance(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("square_lfo", "Square LFO", "lv2:OscillatorPlugin");
port_info(pii);
}
};
class saw_lfo_audio_module: public freq_phase_lfo_base
{
public:
void process(uint32_t count)
{
check_inputs();
*outs[0] = -1 + 2 * phase;
advance(count);
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("saw_lfo", "Saw LFO", "lv2:OscillatorPlugin");
port_info(pii);
}
};
class pulse_lfo_audio_module: public freq_phase_lfo_base
{
public:
inline void activate()
{
phase = 1.0;
}
void process(uint32_t count)
{
check_inputs();
double oldphase = phase;
advance(count);
*outs[0] = (phase < oldphase) ? 1.f : 0.f;
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("pulse_lfo", "Pulse LFO", "lv2:OscillatorPlugin");
port_info(pii);
}
};
#define SMALL_OSC_TABLE_BITS 12
class freq_only_osc_base_common: public null_small_audio_module
{
public:
typedef waveform_family<SMALL_OSC_TABLE_BITS> waves_type;
enum { in_freq, in_count };
enum { out_signal, out_count};
enum { wave_size = 1 << SMALL_OSC_TABLE_BITS };
float *ins[in_count];
float *outs[out_count];
waves_type *waves;
waveform_oscillator<SMALL_OSC_TABLE_BITS> osc;
/// Fill the array with the original, non-bandlimited, waveform
virtual void get_original_waveform(float data[wave_size]) = 0;
/// This function needs to be virtual to ensure a separate wave family for each class (but not each instance)
virtual waves_type *get_waves() = 0;
void activate() {
waves = get_waves();
}
void process(uint32_t count)
{
osc.set_freq_odsr(*ins[in_freq], odsr);
osc.waveform = waves->get_level(osc.phasedelta);
if (osc.waveform)
{
for (uint32_t i = 0; i < count; i++)
outs[out_signal][i] = osc.get();
}
else
dsp::zero(outs[out_signal], count);
}
static void port_info(plugin_info_iface *pii)
{
pii->control_port("freq", "Frequency", 440).input().log_range(20, 20000);
pii->audio_port("out", "Out").output();
}
/// Generate a wave family (bandlimit levels) from the original wave
waves_type *create_waves() {
waves_type *ptr = new waves_type;
float source[wave_size];
get_original_waveform(source);
bandlimiter<SMALL_OSC_TABLE_BITS> bl;
ptr->make(bl, source);
return ptr;
}
};
template<class T>
class freq_only_osc_base: public freq_only_osc_base_common
{
virtual waves_type *get_waves() {
static waves_type *waves = NULL;
if (!waves)
waves = create_waves();
return waves;
}
};
class square_osc_audio_module: public freq_only_osc_base<square_osc_audio_module>
{
public:
virtual void get_original_waveform(float data[wave_size]) {
for (int i = 0; i < wave_size; i++)
data[i] = (i < wave_size / 2) ? +1 : -1;
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("squareosc", "Square Oscillator", "lv2:OscillatorPlugin");
port_info(pii);
}
};
class saw_osc_audio_module: public freq_only_osc_base<saw_osc_audio_module>
{
public:
virtual void get_original_waveform(float data[wave_size]) {
for (int i = 0; i < wave_size; i++)
data[i] = (i * 2.0 / wave_size) - 1;
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("sawosc", "Saw Oscillator", "lv2:OscillatorPlugin");
port_info(pii);
}
};
class print_c_audio_module: public small_audio_module_base<1, 0>
{
public:
static void plugin_info(plugin_info_iface *pii)
{
pii->names("print_c", "Print To Console (C)", "lv2:UtilityPlugin");
pii->control_port("in", "In", 0).input();
}
void process(uint32_t)
{
printf("%f\n", *ins[0]);
}
};
class print_e_audio_module: public small_audio_module_base<1, 0>
{
public:
static void plugin_info(plugin_info_iface *pii)
{
pii->names("print_e", "Print To Console (E)", "lv2:UtilityPlugin");
pii->event_port("in", "In").input();
}
void dump(LV2_Event_Buffer *event_data)
{
event_port_read_iterator ei(event_data);
while(ei)
{
const lv2_event &event = *ei++;
printf("Event at %d.%d, type %d, size %d:", event.frames, event.subframes, (int)event.type, (int)event.size);
uint32_t size = std::min((uint16_t)16, event.size);
for (uint32_t j = 0; j < size; j++)
printf(" %02X", (uint32_t)event.data[j]);
if (event.size > size)
printf("...\n");
else
printf("\n");
}
}
void process(uint32_t)
{
LV2_Event_Buffer *event_data = (LV2_Event_Buffer *)ins[0];
dump(event_data);
}
};
class print_em_audio_module: public print_e_audio_module
{
public:
LV2_Event_Buffer *events;
static void plugin_info(plugin_info_iface *pii)
{
pii->names("print_em", "Print To Console (EM)", "lv2:UtilityPlugin");
pii->lv2_ttl("lv2:requiredFeature <http://lv2plug.in/ns/dev/contexts> ;");
pii->lv2_ttl("lv2:requiredFeature lv2ctx:MessageContext ;");
pii->lv2_ttl("lv2ctx:requiredContext lv2ctx:MessageContext ;");
pii->event_port("in", "In").input().lv2_ttl("lv2ctx:context lv2ctx:MessageContext ;");
}
void process(uint32_t)
{
}
static uint32_t message_run(LV2_Handle instance, const void *valid_inputs, void *outputs_written)
{
print_em_audio_module *self = (print_em_audio_module *)instance;
if (lv2_contexts_port_is_valid(valid_inputs, 0))
{
printf("message_run (events = %p, count = %d)\n", self->events, self->events->event_count);
self->dump(self->events);
}
return 0;
}
static void message_connect_port(LV2_Handle instance, uint32_t port, void* data)
{
print_em_audio_module *self = (print_em_audio_module *)instance;
printf("message_connect_port %d -> %p\n", port, data);
assert(!port);
self->events = (LV2_Event_Buffer *)data;
}
static inline const void *ext_data(const char *URI) {
static LV2MessageContext ctx_ext_data = { message_run, message_connect_port };
printf("URI=%s\n", URI);
if (!strcmp(URI, LV2_CONTEXT_MESSAGE))
{
return &ctx_ext_data;
}
return NULL;
}
};
class copy_em_audio_module: public small_audio_module_base<0, 0>
{
public:
LV2_Event_Buffer *events_in, *events_out;
static void plugin_info(plugin_info_iface *pii)
{
pii->names("copy_em", "Message pass-through (EM)", "lv2:UtilityPlugin");
pii->lv2_ttl("lv2:requiredFeature lv2ctx:MessageContext ;");
pii->lv2_ttl("lv2:requiredFeature <http://lv2plug.in/ns/dev/contexts> ;");
pii->lv2_ttl("lv2:requiredContext lv2ctx:MessageContext ;");
pii->event_port("in", "In").input().lv2_ttl("lv2ctx:context lv2ctx:MessageContext ;");
pii->event_port("out", "Out").output().lv2_ttl("lv2ctx:context lv2ctx:MessageContext ;");
}
void process(uint32_t)
{
}
static uint32_t message_run(LV2_Handle instance, const void *valid_inputs, void *outputs_written)
{
copy_em_audio_module *self = (copy_em_audio_module *)instance;
return self->message_run(valid_inputs, outputs_written);
}
uint32_t message_run(const void *inputs_written, void *outputs_written)
{
if (lv2_contexts_port_is_valid(inputs_written, 0))
{
event_port_read_iterator ri(events_in);
event_port_write_iterator wi(events_out);
if (events_in->size > events_out->capacity)
{
printf("Buffer capacity exceeded!\n");
return false;
}
while(ri)
{
const lv2_event &event = *ri++;
*wi++ = event;
}
if (events_in->event_count != 0)
{
lv2_contexts_set_port_valid(outputs_written, 1);
return 1;
}
}
lv2_contexts_unset_port_valid(outputs_written, 1);
return 0;
}
static void message_connect_port(LV2_Handle instance, uint32_t port, void* data)
{
copy_em_audio_module *self = (copy_em_audio_module *)instance;
printf("message_connect_port %d -> %p\n", port, data);
if (port == 0) self->events_in = (LV2_Event_Buffer *)data;
if (port == 1) self->events_out = (LV2_Event_Buffer *)data;
}
static inline const void *ext_data(const char *URI) {
static LV2MessageContext ctx_ext_data = { message_run, message_connect_port };
if (!strcmp(URI, LV2_CONTEXT_MESSAGE))
{
printf("URI=%s\n", URI);
return &ctx_ext_data;
}
return NULL;
}
};
template<class Range, int Inputs = 1>
class miditypefilter_m_audio_module: public midi_mixin<small_audio_module_base<Inputs, 2> >
{
public:
static inline void extra_inputs(plugin_info_iface *pii)
{
}
static inline const char *plugin_symbol() { return Range::strings()[0]; }
static inline const char *plugin_name() { return Range::strings()[1]; }
static inline const char *port_symbol() { return Range::strings()[2]; }
static inline const char *port_name() { return Range::strings()[3]; }
static void plugin_info(plugin_info_iface *pii)
{
pii->names(Range::plugin_symbol(), Range::plugin_name(), "kf:MIDIPlugin");
pii->event_port("in", "In").input();
Range::extra_inputs(pii);
pii->event_port(Range::port_symbol(), Range::port_name()).output();
pii->event_port("others", "Others").output();
}
void process(uint32_t)
{
event_port_read_iterator ri((LV2_Event_Buffer *)this->ins[0]);
event_port_write_iterator wi((LV2_Event_Buffer *)this->outs[0]);
event_port_write_iterator wi2((LV2_Event_Buffer *)this->outs[1]);
while(ri)
{
const lv2_event &event = *ri++;
if (event.type == this->midi_event_type && event.size && Range::is_in_range(event.data, this->ins))
*wi++ = event;
else
*wi2++ = event;
}
}
};
class notefilter_m_audio_module: public miditypefilter_m_audio_module<notefilter_m_audio_module>
{
public:
static inline bool is_in_range(const uint8_t *data, float **) { return data[0] >= 0x80 && data[0] <= 0x9F; }
static inline const char **strings() { static const char *s[] = { "notefilter_m", "Note Filter", "note", "Note" }; return s;}
};
class pcfilter_m_audio_module: public miditypefilter_m_audio_module<pcfilter_m_audio_module>
{
public:
static inline bool is_in_range(const uint8_t *data, float **) { return data[0] >= 0xA0 && data[0] <= 0xAF; }
static inline const char **strings() { static const char *s[] = { "pcfilter_m", "Program Change Filter", "pc", "PC" }; return s;}
};
class ccfilter_m_audio_module: public miditypefilter_m_audio_module<ccfilter_m_audio_module>
{
public:
static inline bool is_in_range(const uint8_t *data, float **) { return data[0] >= 0xB0 && data[0] <= 0xBF; }
static inline const char **strings() { static const char *s[] = { "ccfilter_m", "Control Change Filter", "cc", "CC" }; return s;}
};
class pressurefilter_m_audio_module: public miditypefilter_m_audio_module<pressurefilter_m_audio_module>
{
public:
static inline bool is_in_range(const uint8_t *data, float **) { return data[0] >= 0xC0 && data[0] <= 0xDF; }
static inline const char **strings() { static const char *s[] = { "pressurefilter_m", "Pressure Filter", "pressure", "Pressure" }; return s;}
};
class pitchbendfilter_m_audio_module: public miditypefilter_m_audio_module<pitchbendfilter_m_audio_module>
{
public:
static inline bool is_in_range(const uint8_t *data, float **) { return data[0] >= 0xE0 && data[0] <= 0xEF; }
static inline const char **strings() { static const char *s[] = { "pitchbendfilter_m", "Pitch Bend Filter", "pbend", "Pitch Bend" }; return s;}
};
class systemfilter_m_audio_module: public miditypefilter_m_audio_module<systemfilter_m_audio_module>
{
public:
static inline bool is_in_range(const uint8_t *data, float **) { return data[0] >= 0xF0; }
static inline const char **strings() { static const char *s[] = { "systemfilter_m", "System Msg Filter", "system", "System" }; return s;}
};
class channelfilter_m_audio_module: public miditypefilter_m_audio_module<channelfilter_m_audio_module, 3>
{
public:
static inline void extra_inputs(plugin_info_iface *pii)
{
pii->control_port("min", "Min Channel", 1).input().integer().lin_range(1, 16);
pii->control_port("max", "Max Channel", 16).input().integer().lin_range(1, 16);
}
static inline bool is_in_range(const uint8_t *data, float **ins) {
int chnl = 1 + (data[0] & 0xF);
return data[0] < 0xF0 && chnl >= *ins[1] && chnl <= *ins[2];
}
static inline const char **strings() { static const char *s[] = { "channelfilter_m", "Channel Range Filter", "range", "Range" }; return s;}
};
class keyfilter_m_audio_module: public miditypefilter_m_audio_module<keyfilter_m_audio_module, 3>
{
public:
static inline void extra_inputs(plugin_info_iface *pii)
{
pii->control_port("min", "Min Note", 0).input().integer().lin_range(0, 127);
pii->control_port("max", "Max Note", 127).input().integer().lin_range(0, 127);
}
static inline bool is_in_range(const uint8_t *data, float **ins) {
// XXXKF doesn't handle polyphonic aftertouch
return (data[0] >= 0x80 && data[0] <= 0x9F) && data[0] >= *ins[1] && data[1] <= *ins[2];
}
static inline const char **strings() { static const char *s[] = { "keyfilter_m", "Key Range Filter", "range", "Range" }; return s;}
};
class key_less_than_m_audio_module: public miditypefilter_m_audio_module<key_less_than_m_audio_module, 2>
{
public:
static inline void extra_inputs(plugin_info_iface *pii)
{
pii->control_port("threshold", "Threshold", 60).input().integer().lin_range(0, 128);
}
static inline bool is_in_range(const uint8_t *data, float **ins) {
// XXXKF doesn't handle polyphonic aftertouch
return (data[0] >= 0x80 && data[0] <= 0x9F) && data[1] < *ins[1];
}
static inline const char **strings() { static const char *s[] = { "key_less_than_m", "Key Less-Than Filter", "less", "Less" }; return s;}
};
class channel_less_than_m_audio_module: public miditypefilter_m_audio_module<channel_less_than_m_audio_module, 2>
{
public:
static inline void extra_inputs(plugin_info_iface *pii)
{
pii->control_port("threshold", "Threshold", 10).input().integer().lin_range(1, 16);
}
static inline bool is_in_range(const uint8_t *data, float **ins) {
// XXXKF doesn't handle polyphonic aftertouch
return (data[0] < 0xF0) && (1 + (data[0] & 0xF)) < *ins[1];
}
static inline const char **strings() { static const char *s[] = { "channel_less_than_m", "Channel Less-Than Filter", "less", "Less" }; return s;}
};
class transpose_m_audio_module: public midi_mixin<small_audio_module_base<2, 1> >
{
public:
static void plugin_info(plugin_info_iface *pii)
{
pii->names("transpose_m", "Transpose", "kf:MIDIPlugin");
pii->event_port("in", "In").input();
pii->control_port("transpose", "Transpose", 12).input().integer();
pii->event_port("out", "Out").output();
}
void process(uint32_t)
{
event_port_read_iterator ri((LV2_Event_Buffer *)ins[0]);
event_port_write_iterator wi((LV2_Event_Buffer *)outs[0]);
while(ri)
{
const lv2_event &event = *ri++;
if (event.type == this->midi_event_type && event.size == 3 && (event.data[0] >= 0x80 && event.data[0] <= 0x9F))
{
int new_note = event.data[1] + (int)*ins[1];
// ignore out-of-range notes
if (new_note >= 0 && new_note <= 127)
{
// it is not possible to create copies here because they are variable length and would "nicely" overwrite the stack
// so write the original event instead, and then modify the pitch
*wi = event;
wi->data[1] = new_note;
wi++;
}
}
else
*wi++ = event;
}
}
};
class setchannel_m_audio_module: public midi_mixin<small_audio_module_base<2, 1> >
{
public:
static void plugin_info(plugin_info_iface *pii)
{
pii->names("setchannel_m", "Set Channel", "kf:MIDIPlugin");
pii->event_port("in", "In").input();
pii->control_port("channel", "Channel", 1).input().integer().lin_range(1, 16);
pii->event_port("out", "Out").output();
}
void process(uint32_t)
{
event_port_read_iterator ri((LV2_Event_Buffer *)ins[0]);
event_port_write_iterator wi((LV2_Event_Buffer *)outs[0]);
while(ri)
{
const lv2_event &event = *ri++;
if (event.type == this->midi_event_type && (event.data[0] >= 0x80 && event.data[0] <= 0xEF))
{
*wi = event;
// modify channel number in the first byte
wi->data[0] = (wi->data[0] & 0xF0) | (((int)*ins[1] - 1) & 0xF);
wi++;
}
else
*wi++ = event;
}
}
};
class eventmerge_e_audio_module: public event_mixin<small_audio_module_base<2, 1> >
{
public:
static void plugin_info(plugin_info_iface *pii)
{
pii->names("eventmerge_e", "Event Merge (E)", "lv2:UtilityPlugin");
pii->event_port("in_1", "In 1").input();
pii->event_port("in_2", "In 2").input();
pii->event_port("out", "Out").output();
}
void process(uint32_t)
{
event_port_merge_iterator<event_port_read_iterator, event_port_read_iterator> ri((const LV2_Event_Buffer *)ins[0], (const LV2_Event_Buffer *)ins[1]);
event_port_write_iterator wi((LV2_Event_Buffer *)outs[0]);
while(ri)
*wi++ = *ri++;
}
};
class print_a_audio_module: public small_audio_module_base<1, 0>
{
public:
static void plugin_info(plugin_info_iface *pii)
{
pii->names("print_a", "Print To Console (A)", "lv2:UtilityPlugin");
pii->audio_port("in", "In").input();
}
void process(uint32_t)
{
printf("%f\n", *ins[0]);
}
};
template<bool audio>
class quadpower_base: public null_small_audio_module
{
public:
enum { in_value, in_factor, in_count , out_count = 4 };
float *ins[in_count];
float *outs[out_count];
static void plugin_info(plugin_info_iface *pii)
{
const char *names[8] = {"xa", "x*a^1", "xaa", "x*a^2", "xaaa", "x*a^3", "xaaaa", "x*a^4" };
if (audio)
pii->names("quadpower_a", "Quad Power (A)", "kf:MathOperatorPlugin");
else
pii->names("quadpower_c", "Quad Power (C)", "kf:MathOperatorPlugin");
if (audio)
pii->audio_port("x", "x").input();
else
pii->control_port("x", "x", 1).input();
pii->control_port("a", "a", 1).input();
for (int i = 0; i < 8; i += 2)
if (audio)
pii->audio_port(names[i], names[i+1]).output();
else
pii->control_port(names[i], names[i+1], 0).output();
}
};
class quadpower_a_audio_module: public quadpower_base<true>
{
public:
void process(uint32_t count)
{
float a = *ins[in_factor];
for (uint32_t i = 0; i < count; i++)
{
float x = ins[in_value][i];
outs[0][i] = x * a;
outs[1][i] = x * a * a;
outs[2][i] = x * a * a * a;
outs[3][i] = x * a * a * a * a;
}
}
};
class quadpower_c_audio_module: public quadpower_base<false>
{
public:
void process(uint32_t)
{
float x = *ins[in_value];
float a = *ins[in_factor];
*outs[0] = x * a;
*outs[1] = x * a * a;
*outs[2] = x * a * a * a;
*outs[3] = x * a * a * a * a;
}
};
template<class Ramp>
class inertia_c_module_base: public small_audio_module_base<3, 1>
{
public:
enum { in_value, in_inertia, in_immediate };
bool reset;
inertia<Ramp> state;
inertia_c_module_base()
: state(Ramp(1))
{}
void activate()
{
reset = true;
}
static void port_info(plugin_info_iface *pii)
{
pii->control_port("in", "In", 0).input();
pii->control_port("time", "Inertia time", 100).input();
pii->control_port("reset", "Reset", 0).input().toggle().trigger();
pii->control_port("out", "Out", 0).output();
}
void process(uint32_t count)
{
float value = *ins[in_value];
if (reset || *ins[in_immediate] > 0)
{
*outs[0] = value;
state.set_now(value);
reset = false;
}
else
{
if (value != state.get_last())
{
state.ramp.set_length(dsp::clip((int)(srate * 0.001 * *ins[in_inertia]), 1, 10000000));
}
state.set_inertia(value);
*outs[0] = state.get_last();
if (count)
state.step_many(count);
}
}
};
class linear_inertia_c_audio_module: public inertia_c_module_base<linear_ramp>
{
public:
static void plugin_info(plugin_info_iface *pii)
{
pii->names("linear_inertia_c", "Linear Inertia (C)", "lv2:FilterPlugin");
port_info(pii);
}
};
class exp_inertia_c_audio_module: public inertia_c_module_base<exponential_ramp>
{
public:
static void plugin_info(plugin_info_iface *pii)
{
pii->names("exp_inertia_c", "Exponential Inertia (C)", "lv2:FilterPlugin");
port_info(pii);
}
};
class sample_hold_base: public small_audio_module_base<2, 1>
{
public:
enum { in_value, in_gate };
static void port_info(plugin_info_iface *pii, const char *clock_symbol, const char *clock_name)
{
pii->control_port("in", "In", 0).input();
pii->control_port(clock_symbol, clock_name, 0).input().toggle().trigger();
pii->control_port("out", "Out", 0).output();
}
};
class sample_hold_edge_c_audio_module: public sample_hold_base
{
public:
bool prev_clock;
float value;
void activate()
{
prev_clock = false;
value = 0;
}
void process(uint32_t count)
{
if (!prev_clock && *ins[in_gate] > 0)
value = *ins[in_value];
prev_clock = *ins[in_gate] > 0;
*outs[0] = value;
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("sample_hold_edge", "Sample&Hold (Edge, C)", "lv2:UtilityPlugin");
port_info(pii, "clock", "Clock");
}
};
class sample_hold_level_c_audio_module: public sample_hold_base
{
public:
float value;
void activate()
{
value = 0;
}
void process(uint32_t count)
{
if (*ins[in_gate] > 0)
value = *ins[in_value];
*outs[0] = value;
}
static void plugin_info(plugin_info_iface *pii)
{
pii->names("sample_hold_level", "Sample&Hold (Level, C)", "lv2:UtilityPlugin");
port_info(pii, "gate", "Gate");
}
};
class msgread_e_audio_module: public message_mixin<small_audio_module_base<1, 1> >
{
public:
uint32_t set_float_msg, float_type;
static void plugin_info(plugin_info_iface *pii)
{
pii->names("msgread_e", "Msg Read", "lv2:UtilityPlugin");
pii->has_gui();
pii->event_port("in", "In").input();
pii->control_port("out", "Out", 0).output();
}
virtual void map_uris()
{
message_mixin<small_audio_module_base<1, 1> >::map_uris();
set_float_msg = map_uri("http://lv2plug.in/ns/dev/msg", "http://foltman.com/garbage/setFloat");
float_type = map_uri("http://lv2plug.in/ns/dev/types", "http://lv2plug.in/ns/dev/types#float");
}
void process(uint32_t count)
{
event_port_read_iterator ri((const LV2_Event_Buffer *)ins[0]);
while(ri)
{
const lv2_event *event = &*ri++;
if (event->type == message_event_type)
{
struct payload {
uint32_t selector;
uint32_t serial_no;
uint32_t data_size;
uint32_t parg_count;
uint32_t data_type;
float data_value;
uint32_t narg_count;
};
const payload *p = (const payload *)event->data;
if (p->selector == set_float_msg) {
assert(p->parg_count == 1);
assert(p->data_size == 16);
assert(p->data_type == float_type);
*outs[0] = p->data_value;
assert(p->narg_count == 0); // this is just for testing - passing
}
}
}
}
};
};
#define PER_SMALL_MODULE_ITEM(name, id) SMALL_WRAPPERS(name, id)
#include <calf/modulelist.h>
const LV2_Descriptor *calf_plugins::lv2_small_descriptor(uint32_t index)
{
#define PER_SMALL_MODULE_ITEM(name, id) if (!(index--)) return &::lv2_small_##name.descriptor;
#include <calf/modulelist.h>
return NULL;
}
#endif
#endif
void calf_plugins::get_all_small_plugins(plugin_list_info_iface *iface)
{
#if USE_LV2
#define PER_SMALL_MODULE_ITEM(name, id) { plugin_info_iface *pii = &iface->plugin(id); small_plugins::name##_audio_module::plugin_info(pii); pii->finalize(); }
#include <calf/modulelist.h>
#endif
}