mirror of
https://github.com/LMMS/lmms.git
synced 2026-07-14 23:52:26 -04:00
Replaced remaining occurences of old constants and superfluous "void"
on empty argument list of functions.
(cherry picked from commit 166701f9f3)
1917 lines
61 KiB
C++
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
|
|
}
|
|
|