CALF: updated up to commit 022dbc37d9d4bf1af2f33a288844156bdc13d27f

This commit is contained in:
Tobias Doerffel
2012-01-13 21:53:25 +01:00
parent 9af89ec862
commit 0bcd267063
22 changed files with 1882 additions and 838 deletions

View File

@@ -7,8 +7,10 @@ Torben Hohn <torbenh@gmx.de>
Markus Schmidt <schmidt@boomshop.net>
Tom Szilagyi <tomszilagyi@gmail.com>
Damien Zammit <damien.zammit@gmail.com>
Christian Holschuh
Additional bugfixes/enhancement patches:
David Täht <d@teklibre.com>
Dave Robillard <dave@drobilla.net>
Alexandre Prokoudine <alexandre.prokoudine@gmail.com>
Carl Hetherington <cth@carlh.net>

View File

@@ -351,6 +351,8 @@ tap_distortion::tap_distortion()
is_active = false;
srate = 0;
meter = 0.f;
prev_med = prev_out = 0.f;
drive_old = blend_old = -1.f;
}
void tap_distortion::activate()
@@ -530,3 +532,211 @@ bool simple_lfo::get_dot(float &x, float &y, int &size, cairo_iface *context) co
return true;
}
/// Lookahead Limiter by Christian Holschuh and Markus Schmidt
lookahead_limiter::lookahead_limiter() {
is_active = false;
channels = 2;
id = 0;
buffer_size = 0;
overall_buffer_size = 0;
att = 1.f;
att_max = 1.0;
pos = 0;
delta = 0.f;
_delta = 0.f;
peak = 0.f;
over_s = 0;
over_c = 1.f;
attack = 0.005;
__attack = -1;
use_multi = false;
weight = 1.f;
_sanitize = false;
auto_release = false;
asc_active = false;
}
void lookahead_limiter::activate()
{
is_active = true;
pos = 0;
}
void lookahead_limiter::set_multi(bool set) { use_multi = set; }
void lookahead_limiter::deactivate()
{
is_active = false;
}
float lookahead_limiter::get_attenuation()
{
float a = att_max;
att_max = 1.0;
return a;
}
void lookahead_limiter::set_sample_rate(uint32_t sr)
{
srate = sr;
// rebuild buffer
overall_buffer_size = (int)(srate * (100.f / 1000.f) * channels) + channels; // buffer size attack rate multiplied by 2 channels
buffer = (float*) calloc(overall_buffer_size, sizeof(float));
memset(buffer, 0, overall_buffer_size * sizeof(float)); // reset buffer to zero
pos = 0;
}
void lookahead_limiter::set_params(float l, float a, float r, float w, bool ar, bool d)
{
limit = l;
attack = a / 1000.f;
release = r / 1000.f;
auto_release = ar;
debug = d;
weight = w;
//if(debug) printf("%.5f\n", release);
if( attack != __attack) {
int bs = (int)(srate * attack * channels);
buffer_size = bs - bs % channels; // buffer size attack rate
__attack = attack;
_sanitize = true;
pos = 0;
}
}
void lookahead_limiter::process(float &left, float &right, float * multi_buffer)
{
// PROTIP: harming paying customers enough to make them develop a competing
// product may be considered an example of a less than sound business practice.
// write left and right to buffer
buffer[pos] = 0.f;
buffer[pos + 1] = 0.f;
if(!_sanitize) {
buffer[pos] = left;
buffer[pos + 1] = right;
}
// are we using multiband? get the multiband coefficient
float multi_coeff = (use_multi) ? multi_buffer[pos] : 1.f;
//if(debug and pos%10 == 0) printf("%03d: %.5f\n", pos, multi_buffer[pos]);
// input peak - impact in left or right channel?
peak = fabs(left) > fabs(right) ? fabs(left) : fabs(right);
// if we have a peak in input over our limit, check if delta to reach is
// more important than actual delta
if(peak > limit * multi_coeff * weight or multi_coeff < 1.f) {
_delta = ((limit * multi_coeff * weight) / peak - att) / (buffer_size / channels - channels);
if(_delta < delta) {
delta = _delta;
}
}
// switch left and right pointers to output
left = buffer[(pos + channels) % buffer_size];
right = buffer[(pos + channels + 1) % buffer_size];
// check multiband coefficient again for output pointer
multi_coeff = (use_multi) ? multi_buffer[(pos + channels) % buffer_size] : 1.f;
// output peak - impact in left or right channel?
peak = fabs(left) > fabs(right) ? fabs(left) : fabs(right);
// output is over the limit?
// then we have to search for new delta.
// the idea is to calculate a delta for every peak and always use the
// lowest. this produces a soft transition between limiting targets without
// passing values above limit
asc_active = false;
if(peak > limit * multi_coeff * weight) {
// default is to do a release
delta = (1.f - att) / (srate * release);
unsigned int j;
float b_sum = 0.f;
unsigned int b_sum_c = 0;
for(unsigned int i = channels; i < buffer_size; i += channels) {
// iterate over buffer (except input and output pointer positions)
// and search for maximum slope
j = (i + pos + channels) % buffer_size;
float _multi_coeff = (use_multi) ? multi_buffer[j] : 1.f;
float _peak = fabs(buffer[j]) > fabs(buffer[j + 1]) ? fabs(buffer[j]) : fabs(buffer[j + 1]);
// calculate steepness of slope
if(_peak > limit * _multi_coeff * weight) {
_delta = ((limit * _multi_coeff * weight) / _peak - att) / (i / channels);
// if slope is steeper, use it, fucker.
if(_delta < delta) {
delta = _delta;
}
b_sum += _peak;
b_sum_c ++;
}
}
if(auto_release) {
// This is Auto-Smoothness-Control (wink wink, nudge nudge)
// check if releasing to average level of peaks is steeper than
// releasing to 1.f
_delta = ((limit * weight) / (float)(b_sum / b_sum_c) - att) / (srate * release);
asc_active = _delta < delta ? true : false;
delta = _delta < delta ? _delta : delta;
} else {
asc_active = false;
}
}
// change the attenuation level
att += delta;
// ...and calculate outpout from it
left *= att;
right *= att;
if(_sanitize) {
left = 0.f;
right = 0.f;
}
// release time seems over
if (att > 1.0f) {
att = 1.0f;
delta = 0.0f;
}
// security personnel pawing your values
if(att < 0.f) {
// if this happens we're doomed!!
// may happen on manually lowering attack
att = 0.0000000001;
delta = (1.0f - att) / (srate * release);
}
if(att != 1.f and 1 - att < 0.0000000000001) {
// denormalize att
att = 1.f;
}
if(delta != 0.f and fabs(delta) < 0.00000000000001) {
// denormalize delta
delta = 0.f;
}
// post treatment (denormal, limit)
denormal(&left);
denormal(&right);
left = std::max(left, -limit * multi_coeff * weight);
left = std::min(left, limit * multi_coeff * weight);
right = std::max(right, -limit * multi_coeff * weight);
right = std::min(right, limit * multi_coeff * weight);
att_max = (att < att_max) ? att : att_max; // store max atten for meter output
pos = (pos + channels) % buffer_size;
if(pos == 0) _sanitize = false;
}
bool lookahead_limiter::get_arc() {
return asc_active;
}

View File

@@ -565,6 +565,48 @@ public:
bool get_dot(float &x, float &y, int &size, calf_plugins::cairo_iface *context) const;
};
/// Lookahead Limiter by Markus Schmidt and Christian Holschuh
class lookahead_limiter {
private:
public:
float limit, attack, release, weight;
float __attack;
uint32_t srate;
float att;
float att_max;
unsigned int pos;
unsigned int buffer_size;
unsigned int overall_buffer_size;
bool is_active;
bool debug;
bool auto_release;
bool asc_active;
float *buffer;
int channels;
float delta;
float _delta;
float peak;
unsigned int over_s;
float over_c;
bool use_multi;
unsigned int id;
bool _sanitize;
static inline void denormal(volatile float *f) {
*f += 1e-18;
*f -= 1e-18;
}
bool get_arc();
lookahead_limiter();
void set_multi(bool set);
void process(float &left, float &right, float *multi_buffer);
void set_sample_rate(uint32_t sr);
void set_params(float l, float a, float r, float weight = 1.f, bool ar = false, bool d = false);
float get_attenuation();
void activate();
void deactivate();
};
#if 0
{ to keep editor happy
#endif

View File

@@ -429,6 +429,11 @@ struct biquad_d2: public biquad_coeffs<Coeff>
/// direct II form with two state variables
inline T process(T in)
{
dsp::sanitize_denormal(in);
dsp::sanitize(in);
dsp::sanitize(w1);
dsp::sanitize(w2);
T tmp = in - w1 * b1 - w2 * b2;
T out = tmp * a0 + w1 * a1 + w2 * a2;
w2 = w1;

View File

@@ -92,7 +92,7 @@ struct simple_delay {
*/
template<class U>
inline void get_interp(U &odata, int delay, float udelay) {
// assert(delay >= 0 && delay < N-1);
// assert(delay >= 0 && delay <= N-1);
int ppos = wrap_around<N>(pos + N - delay);
int pppos = wrap_around<N>(ppos + N - 1);
odata = lerp(data[ppos], data[pppos], udelay);

View File

@@ -1,130 +0,0 @@
/* Calf DSP Library
* API wrappers for LADSPA/DSSI
*
* Copyright (C) 2007-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 02111-1307, USA.
*/
#ifndef __CALF_LADSPA_WRAP_H
#define __CALF_LADSPA_WRAP_H
#if USE_LADSPA
#include <string.h>
#include <ladspa.h>
#if USE_DSSI
#include <dssi.h>
#endif
#include "giface.h"
#include "preset.h"
namespace calf_plugins {
struct ladspa_plugin_metadata_set;
/// A template implementing plugin_ctl_iface for a given plugin
struct ladspa_instance: public plugin_ctl_iface
{
audio_module_iface *module;
const plugin_metadata_iface *metadata;
ladspa_plugin_metadata_set *ladspa;
bool activate_flag;
float **ins, **outs, **params;
#if USE_DSSI
dssi_feedback_sender *feedback_sender;
#endif
ladspa_instance(audio_module_iface *_module, ladspa_plugin_metadata_set *_ladspa, int sample_rate);
virtual const line_graph_iface *get_line_graph_iface() const { return module->get_line_graph_iface(); }
virtual float get_param_value(int param_no);
virtual void set_param_value(int param_no, float value);
virtual bool activate_preset(int bank, int program);
virtual char *configure(const char *key, const char *value);
virtual float get_level(unsigned int port) { return 0.f; }
virtual void execute(int cmd_no) {
module->execute(cmd_no);
}
virtual void send_configures(send_configure_iface *sci) {
module->send_configures(sci);
}
virtual int send_status_updates(send_updates_iface *sui, int last_serial) { return module->send_status_updates(sui, last_serial); }
void run(unsigned long SampleCount);
#if USE_DSSI
/// Utility function: handle MIDI event (only handles a subset in this version)
void process_dssi_event(snd_seq_event_t &event);
void run_synth(unsigned long SampleCount, snd_seq_event_t *Events, unsigned long EventCount);
#endif
virtual const plugin_metadata_iface *get_metadata_iface() const
{
return metadata;
}
};
/// Set of metadata produced by LADSPA wrapper for LADSPA-related purposes
struct ladspa_plugin_metadata_set
{
/// LADSPA descriptor
LADSPA_Descriptor descriptor;
/// LADSPA descriptor for DSSI (uses a different name for the plugin, otherwise same as descriptor)
LADSPA_Descriptor descriptor_for_dssi;
#if USE_DSSI
/// Extended DSSI descriptor (points to descriptor_for_dssi for things like name/label/port info etc.)
DSSI_Descriptor dssi_descriptor;
DSSI_Program_Descriptor dssi_default_program;
std::vector<plugin_preset> *presets;
std::vector<DSSI_Program_Descriptor> *preset_descs;
#endif
int input_count, output_count, param_count;
const plugin_metadata_iface *metadata;
ladspa_plugin_metadata_set();
void prepare(const plugin_metadata_iface *md, LADSPA_Handle (*cb_instantiate)(const struct _LADSPA_Descriptor * Descriptor, unsigned long sample_rate));
void prepare_dssi();
~ladspa_plugin_metadata_set();
};
/// A wrapper class for plugin class object (there is only one ladspa_wrapper singleton for many instances of the same plugin)
template<class Module>
struct ladspa_wrapper
{
static ladspa_plugin_metadata_set output;
private:
ladspa_wrapper(const plugin_metadata_iface *md)
{
output.prepare(md, cb_instantiate);
}
public:
/// LADSPA instantiation function (create a plugin instance)
static LADSPA_Handle cb_instantiate(const struct _LADSPA_Descriptor * Descriptor, unsigned long sample_rate)
{
return new ladspa_instance(new Module, &output, sample_rate);
}
/// Get a wrapper singleton - used to prevent initialization order problems which were present in older versions
static ladspa_plugin_metadata_set &get() {
static ladspa_wrapper instance(new typename Module::metadata_class);
return instance.output;
}
};
};
#endif
#endif

View File

@@ -28,7 +28,7 @@
#include <lv2.h>
#include <calf/giface.h>
#include <calf/lv2_event.h>
#include <calf/lv2_persist.h>
#include <calf/lv2_state.h>
#include <calf/lv2_progress.h>
#include <calf/lv2_uri_map.h>
#include <string.h>
@@ -86,17 +86,17 @@ struct lv2_instance: public plugin_ctl_iface, public progress_report_iface
void send_configures(send_configure_iface *sci) {
module->send_configures(sci);
}
void impl_restore(LV2_Persist_Retrieve_Function retrieve, void *callback_data)
void impl_restore(LV2_State_Retrieve_Function retrieve, void *callback_data)
{
const char *const *vars = module->get_metadata_iface()->get_configure_vars();
if (!vars)
return;
assert(uri_map);
uint32_t string_type = uri_map->uri_to_id(uri_map, NULL, "http://lv2plug.in/ns/ext/atom#String");
uint32_t string_type = uri_map->uri_to_id(uri_map->callback_data, NULL, "http://lv2plug.in/ns/ext/atom#String");
assert(string_type);
for (unsigned int i = 0; vars[i]; i++)
{
const uint32_t key = uri_map->uri_to_id(uri_map, NULL, vars[i]);
const uint32_t key = uri_map->uri_to_id(uri_map->callback_data, NULL, vars[i]);
size_t len = 0;
uint32_t type = 0;
uint32_t flags = 0;
@@ -182,7 +182,7 @@ struct lv2_wrapper
typedef lv2_instance instance;
static LV2_Descriptor descriptor;
static LV2_Calf_Descriptor calf_descriptor;
static LV2_Persist persist;
static LV2_State_Interface state_iface;
std::string uri;
lv2_wrapper()
@@ -197,8 +197,8 @@ struct lv2_wrapper
descriptor.deactivate = cb_deactivate;
descriptor.cleanup = cb_cleanup;
descriptor.extension_data = cb_ext_data;
persist.save = cb_persist_save;
persist.restore = cb_persist_restore;
state_iface.save = cb_state_save;
state_iface.restore = cb_state_restore;
calf_descriptor.get_pci = cb_get_pci;
}
@@ -294,16 +294,18 @@ struct lv2_wrapper
{
if (!strcmp(URI, "http://foltman.com/ns/calf-plugin-instance"))
return &calf_descriptor;
if (!strcmp(URI, LV2_PERSIST_URI))
return &persist;
if (!strcmp(URI, LV2_STATE_INTERFACE_URI))
return &state_iface;
return NULL;
}
static void cb_persist_save(LV2_Handle Instance, LV2_Persist_Store_Function store, void *callback_data)
static void cb_state_save(LV2_Handle Instance,
LV2_State_Store_Function store, LV2_State_Handle handle,
uint32_t flags, const LV2_Feature *const * features)
{
instance *const inst = (instance *)Instance;
struct store_state: public send_configure_iface
{
LV2_Persist_Store_Function store;
LV2_State_Store_Function store;
void *callback_data;
instance *inst;
uint32_t string_data_type;
@@ -311,24 +313,26 @@ struct lv2_wrapper
virtual void send_configure(const char *key, const char *value)
{
(*store)(callback_data,
inst->uri_map->uri_to_id(inst->uri_map, NULL, key),
inst->uri_map->uri_to_id(inst->uri_map->callback_data, NULL, key),
value,
strlen(value) + 1,
string_data_type,
LV2_PERSIST_IS_POD|LV2_PERSIST_IS_PORTABLE);
LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE);
}
};
// A host that supports Persist MUST support URI-Map as well.
// A host that supports State MUST support URI-Map as well.
assert(inst->uri_map);
store_state s;
s.store = store;
s.callback_data = callback_data;
s.callback_data = handle;
s.inst = inst;
s.string_data_type = inst->uri_map->uri_to_id(inst->uri_map, NULL, "http://lv2plug.in/ns/ext/atom#String");
s.string_data_type = inst->uri_map->uri_to_id(inst->uri_map->callback_data, NULL, "http://lv2plug.in/ns/ext/atom#String");
inst->send_configures(&s);
}
static void cb_persist_restore(LV2_Handle Instance, LV2_Persist_Retrieve_Function retrieve, void *callback_data)
static void cb_state_restore(LV2_Handle Instance,
LV2_State_Retrieve_Function retrieve, LV2_State_Handle callback_data,
uint32_t flags, const LV2_Feature *const * features)
{
instance *const inst = (instance *)Instance;
inst->impl_restore(retrieve, callback_data);

View File

@@ -184,14 +184,15 @@ struct multibandcompressor_metadata: public plugin_metadata<multibandcompressor_
param_freq0, param_freq1, param_freq2,
param_sep0, param_sep1, param_sep2,
param_q0, param_q1, param_q2,
param_mode,
param_threshold0, param_ratio0, param_attack0, param_release0, param_makeup0, param_knee0,
param_detection0, param_compression0, param_output0, param_bypass0, param_mute0,
param_detection0, param_compression0, param_output0, param_bypass0, param_solo0,
param_threshold1, param_ratio1, param_attack1, param_release1, param_makeup1, param_knee1,
param_detection1, param_compression1, param_output1, param_bypass1, param_mute1,
param_detection1, param_compression1, param_output1, param_bypass1, param_solo1,
param_threshold2, param_ratio2, param_attack2, param_release2, param_makeup2, param_knee2,
param_detection2, param_compression2, param_output2, param_bypass2, param_mute2,
param_detection2, param_compression2, param_output2, param_bypass2, param_solo2,
param_threshold3, param_ratio3, param_attack3, param_release3, param_makeup3, param_knee3,
param_detection3, param_compression3, param_output3, param_bypass3, param_mute3,
param_detection3, param_compression3, param_output3, param_bypass3, param_solo3,
param_count };
PLUGIN_NAME_ID_LABEL("multiband_compressor", "multibandcompressor", "Multiband Compressor")
};
@@ -230,6 +231,40 @@ struct sidechaingate_metadata: public plugin_metadata<sidechaingate_metadata>
PLUGIN_NAME_ID_LABEL("sidechaingate", "sidechaingate", "Sidechain Gate")
};
/// Markus's limiter - metadata
struct limiter_metadata: public plugin_metadata<limiter_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out,
STEREO_VU_METER_PARAMS,
param_limit, param_attack, param_release,
param_att,
param_asc, param_asc_led,
param_count };
PLUGIN_NAME_ID_LABEL("limiter", "limiter", "Limiter")
};
/// Markus's multibandlimiter - metadata
struct multibandlimiter_metadata: public plugin_metadata<multibandlimiter_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out,
STEREO_VU_METER_PARAMS,
param_freq0, param_freq1, param_freq2,
param_sep0, param_sep1, param_sep2,
param_q0, param_q1, param_q2,
param_mode,
param_limit, param_attack, param_release, param_minrel,
param_att0, param_att1, param_att2, param_att3,
param_weight0, param_weight1, param_weight2, param_weight3,
param_release0, param_release1, param_release2, param_release3,
param_solo0, param_solo1, param_solo2, param_solo3,
param_effrelease0, param_effrelease1, param_effrelease2, param_effrelease3,
param_asc, param_asc_led,
param_count };
PLUGIN_NAME_ID_LABEL("multiband_limiter", "multibandlimiter", "Multiband Limiter")
};
/// Markus's 5-band EQ - metadata
struct equalizer5band_metadata: public plugin_metadata<equalizer5band_metadata>
{
@@ -322,6 +357,31 @@ struct bassenhancer_metadata: public plugin_metadata<bassenhancer_metadata>
param_freq, param_listen, param_count };
PLUGIN_NAME_ID_LABEL("bassenhancer", "bassenhancer", "Bass Enhancer")
};
/// Markus's Mono Module - metadata
struct stereo_metadata: public plugin_metadata<stereo_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 1, outs_optional = 1, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out,
STEREO_VU_METER_PARAMS, param_balance_in, param_balance_out, param_softclip,
param_mute_l, param_mute_r, param_phase_l, param_phase_r,
param_mode, param_slev, param_sbal, param_mlev, param_mpan,
param_widener, param_delay,
param_meter_phase,
param_count };
PLUGIN_NAME_ID_LABEL("stereo", "stereo", "Stereo Tools")
};
/// Markus's Mono Module - metadata
struct mono_metadata: public plugin_metadata<mono_metadata>
{
enum { in_count = 1, out_count = 2, ins_optional = 1, outs_optional = 1, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out,
param_meter_in, param_meter_outL, param_meter_outR, param_clip_in,param_clip_outL, param_clip_outR,
param_balance_out, param_softclip,
param_mute_l, param_mute_r, param_phase_l, param_phase_r,
param_delay,
param_count };
PLUGIN_NAME_ID_LABEL("mono", "mono", "Mono Input")
};
/// Organ - enums for parameter IDs etc. (this mess is caused by organ split between plugin and generic class - which was
/// a bad design decision and should be sorted out some day) XXXKF @todo

View File

@@ -15,6 +15,8 @@
PER_MODULE_ITEM(deesser, false, "deesser")
PER_MODULE_ITEM(gate, false, "gate")
PER_MODULE_ITEM(sidechaingate, false, "sidechaingate")
PER_MODULE_ITEM(limiter, false, "limiter")
PER_MODULE_ITEM(multibandlimiter, false, "multibandlimiter")
PER_MODULE_ITEM(pulsator, false, "pulsator")
PER_MODULE_ITEM(equalizer5band, false, "eq5")
PER_MODULE_ITEM(equalizer8band, false, "eq8")
@@ -22,6 +24,8 @@
PER_MODULE_ITEM(saturator, false, "saturator")
PER_MODULE_ITEM(exciter, false, "exciter")
PER_MODULE_ITEM(bassenhancer, false, "bassenhancer")
PER_MODULE_ITEM(mono, false, "mono")
PER_MODULE_ITEM(stereo, false, "stereo")
#ifdef ENABLE_EXPERIMENTAL
PER_MODULE_ITEM(fluidsynth, true, "fluidsynth")
PER_MODULE_ITEM(wavetable, true, "wavetable")

View File

@@ -100,9 +100,10 @@ public:
, inertia_resonance(dsp::exponential_ramp(128), 20)
, inertia_gain(dsp::exponential_ramp(128), 1.0)
, timer(128)
{
is_active = false;
}
, is_active(false)
, last_generation(-1)
, last_calculated_generation(-2)
{}
void calculate_filter()
{
@@ -195,6 +196,7 @@ public:
: filter_module_with_inertia<dsp::biquad_filter_module, filter_metadata>(ins, outs, params)
{
last_generation = 0;
old_mode = old_resonance = old_cutoff = -1;
}
void params_changed()
{
@@ -239,6 +241,63 @@ private:
void adjust_gain_according_to_filter_mode(int velocity);
};
#define MATH_E 2.718281828
class mono_audio_module:
public audio_module<mono_metadata>
{
typedef mono_audio_module AM;
uint32_t srate;
bool active;
uint32_t clip_in, clip_outL, clip_outR;
float meter_in, meter_outL, meter_outR;
float * buffer;
unsigned int pos;
unsigned int buffer_size;
void softclip(float &s) {
int ph = s / fabs(s);
s = s > 0.63 ? ((0.63 + 0.36) * ph * (1 - pow(MATH_E, (1.f / 3) * (0.63 + s * ph)))) : s;
}
public:
mono_audio_module();
void params_changed();
void activate();
void set_sample_rate(uint32_t sr);
void deactivate();
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
};
class stereo_audio_module:
public audio_module<stereo_metadata>
{
typedef stereo_audio_module AM;
float LL, LR, RL, RR;
uint32_t srate;
bool active;
uint32_t clip_inL, clip_inR, clip_outL, clip_outR;
float meter_inL, meter_inR, meter_outL, meter_outR, meter_phase;
float * buffer;
unsigned int pos;
unsigned int buffer_size;
void softclip(float &s) {
int ph = s / fabs(s);
s = s > 0.63 ? ((0.63 + 0.36) * ph * (1 - pow(MATH_E, (1.f / 3) * (0.63 + s * ph)))) : s;
}
public:
stereo_audio_module();
void params_changed();
void activate();
void set_sample_rate(uint32_t sr);
void deactivate();
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
};
};
#endif

View File

@@ -165,12 +165,14 @@ class multibandcompressor_audio_module: public audio_module<multibandcompressor_
private:
typedef multibandcompressor_audio_module AM;
static const int strips = 4;
bool mute[strips];
bool solo[strips];
bool no_solo;
uint32_t clip_inL, clip_inR, clip_outL, clip_outR;
float meter_inL, meter_inR, meter_outL, meter_outR;
gain_reduction_audio_module strip[strips];
dsp::biquad_d2<float> lpL0, lpR0, lpL1, lpR1, lpL2, lpR2, hpL0, hpR0, hpL1, hpR1, hpL2, hpR2;
dsp::biquad_d2<float> lpL[strips - 1][3], lpR[strips - 1][3], hpL[strips - 1][3], hpR[strips - 1][3];
float freq_old[strips - 1], sep_old[strips - 1], q_old[strips - 1];
int mode, mode_old;
public:
uint32_t srate;
bool is_active;

View File

@@ -0,0 +1,92 @@
/* Calf DSP plugin pack
* Limiter related plugins
*
* Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
*
* 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 02111-1307, USA.
*/
#ifndef CALF_MODULES_LIMIT_H
#define CALF_MODULES_LIMIT_H
#include <assert.h>
#include <limits.h>
#include "biquad.h"
#include "inertia.h"
#include "audio_fx.h"
#include "giface.h"
#include "metadata.h"
#include "plugin_tools.h"
namespace calf_plugins {
/// Limiter by Markus Schmidt and Christian Holschuh
class limiter_audio_module: public audio_module<limiter_metadata>, public line_graph_iface {
private:
typedef limiter_audio_module AM;
uint32_t clip_inL, clip_inR, clip_outL, clip_outR, asc_led;
int mode, mode_old;
float meter_inL, meter_inR, meter_outL, meter_outR;
dsp::lookahead_limiter limiter;
public:
uint32_t srate;
bool is_active;
limiter_audio_module();
void activate();
void deactivate();
void params_changed();
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
void set_sample_rate(uint32_t sr);
};
/// Multiband Limiter by Markus Schmidt and Christian Holschuh
class multibandlimiter_audio_module: public audio_module<multibandlimiter_metadata>, public line_graph_iface {
private:
typedef multibandlimiter_audio_module AM;
static const int strips = 4;
uint32_t clip_inL, clip_inR, clip_outL, clip_outR, asc_led;
int mode, mode_old;
bool solo[strips];
bool no_solo;
float meter_inL, meter_inR, meter_outL, meter_outR;
dsp::lookahead_limiter strip[strips];
dsp::lookahead_limiter broadband;
dsp::biquad_d2<float> lpL[strips - 1][3], lpR[strips - 1][3], hpL[strips - 1][3], hpR[strips - 1][3];
float freq_old[strips - 1], sep_old[strips - 1], q_old[strips - 1];
unsigned int pos;
unsigned int buffer_size;
unsigned int overall_buffer_size;
float __attack;
float *buffer;
int channels;
float striprel[strips];
float weight[strips];
bool _sanitize;
public:
uint32_t srate;
bool is_active;
multibandlimiter_audio_module();
void activate();
void deactivate();
void params_changed();
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
void set_sample_rate(uint32_t sr);
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
};
};
#endif

View File

@@ -1,2 +0,0 @@
#include <calf/osctl.h>

View File

@@ -407,6 +407,16 @@ inline void sanitize(float &value)
value = 0.f;
}
/**
* Force already-denormal float value to zero
*/
inline void sanitize_denormal(float& value)
{
if (((*(unsigned int *) &value) & 0x7f800000) == 0) {
value = 0;
}
}
/**
* Force "small enough" double value to zero
*/

View File

@@ -21,7 +21,6 @@
#include <config.h>
#include <limits.h>
#include <calf/giface.h>
#include <calf/osctlnet.h>
#include <calf/utils.h>
using namespace std;
@@ -385,106 +384,6 @@ uint32_t mod_matrix_metadata::get_table_rows() const
///////////////////////////////////////////////////////////////////////////////////////
#if USE_EXEC_GUI
struct osc_cairo_control: public cairo_iface
{
osctl::osc_inline_typed_strstream &os;
osc_cairo_control(osctl::osc_inline_typed_strstream &_os) : os(_os) {}
virtual void set_source_rgba(float r, float g, float b, float a = 1.f)
{
os << (uint32_t)LGI_SET_RGBA << r << g << b << a;
}
virtual void set_line_width(float width)
{
os << (uint32_t)LGI_SET_WIDTH << width;
}
};
static void serialize_graphs(osctl::osc_inline_typed_strstream &os, const line_graph_iface *graph, std::vector<int> &params)
{
osc_cairo_control cairoctl(os);
for (size_t i = 0; i < params.size(); i++)
{
int index = params[i];
os << (uint32_t)LGI_GRAPH;
os << (uint32_t)index;
for (int j = 0; ; j++)
{
float data[128];
if (graph->get_graph(index, j, data, 128, &cairoctl))
{
os << (uint32_t)LGI_SUBGRAPH;
os << (uint32_t)128;
for (int p = 0; p < 128; p++)
os << data[p];
}
else
break;
}
for (int j = 0; ; j++)
{
float x, y;
int size = 3;
if (graph->get_dot(index, j, x, y, size, &cairoctl))
os << (uint32_t)LGI_DOT << x << y << (uint32_t)size;
else
break;
}
for (int j = 0; ; j++)
{
float pos = 0;
bool vertical = false;
string legend;
if (graph->get_gridline(index, j, pos, vertical, legend, &cairoctl))
os << (uint32_t)LGI_LEGEND << pos << (uint32_t)(vertical ? 1 : 0) << legend;
else
break;
}
os << (uint32_t)LGI_END_ITEM;
}
os << (uint32_t)LGI_END;
}
calf_plugins::dssi_feedback_sender::dssi_feedback_sender(const char *URI, const line_graph_iface *_graph)
{
graph = _graph;
is_client_shared = false;
client = new osctl::osc_client;
client->bind("0.0.0.0", 0);
client->set_url(URI);
}
calf_plugins::dssi_feedback_sender::dssi_feedback_sender(osctl::osc_client *_client, const line_graph_iface *_graph)
{
graph = _graph;
client = _client;
is_client_shared = true;
}
void calf_plugins::dssi_feedback_sender::add_graphs(const calf_plugins::parameter_properties *props, int num_params)
{
for (int i = 0; i < num_params; i++)
{
if (props[i].flags & PF_PROP_GRAPH)
indices.push_back(i);
}
}
void calf_plugins::dssi_feedback_sender::update()
{
if (graph)
{
osctl::osc_inline_typed_strstream os;
serialize_graphs(os, graph, indices);
client->send("/lineGraph", os);
}
}
calf_plugins::dssi_feedback_sender::~dssi_feedback_sender()
{
if (!is_client_shared)
delete client;
}
table_via_configure::table_via_configure()
{

View File

@@ -297,6 +297,7 @@ CALF_PLUGIN_INFO(sidechaincompressor) = { 0x8517, "Sidechaincompressor", "Calf S
CALF_PORT_NAMES(multibandcompressor) = {"In L", "In R", "Out L", "Out R"};
const char *multibandcompressor_detection_names[] = { "RMS", "Peak" };
const char *multibandcompressor_filter_choices[] = { "12dB", "36dB"};
CALF_PORT_PROPS(multibandcompressor) = {
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass", "Bypass" },
@@ -311,7 +312,7 @@ CALF_PORT_PROPS(multibandcompressor) = {
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_outL", "0dB-OutL" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_outR", "0dB-OutR" },
{ 100, 10, 20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "freq0", "Split 1/2" },
{ 120, 10, 20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "freq0", "Split 1/2" },
{ 1000, 10, 20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "freq1", "Split 2/3" },
{ 6000, 10, 20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "freq2", "Split 3/4" },
@@ -319,61 +320,62 @@ CALF_PORT_PROPS(multibandcompressor) = {
{ -0.17, -0.5, 0.5, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "sep1", "S2" },
{ -0.17, -0.5, 0.5, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "sep2", "S3" },
{ 0.895025, 0.25, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_GRAPH, NULL, "q0", "Q1" },
{ 0.895025, 0.25, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_GRAPH, NULL, "q1", "Q2" },
{ 0.895025, 0.25, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_GRAPH, NULL, "q2", "Q3" },
{ 0.7762471166286917, 0.25, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_GRAPH, NULL, "q0", "Q1" },
{ 0.7762471166286917, 0.25, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_GRAPH, NULL, "q1", "Q2" },
{ 0.7762471166286917, 0.25, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_GRAPH, NULL, "q2", "Q3" },
{ 1, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, multibandcompressor_filter_choices, "mode", "Filter Mode" },
{ 0.0625, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold0", "Threshold 1" },
{ 3, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio0", "Ratio 1" },
{ 50, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack0", "Attack 1" },
{ 100, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release0", "Release 1" },
{ 0.25, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold0", "Threshold 1" },
{ 2, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio0", "Ratio 1" },
{ 150, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack0", "Attack 1" },
{ 300, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release0", "Release 1" },
{ 2, 1, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup0", "Makeup 1" },
{ 2.828427125, 1, 8, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "knee0", "Knee 1" },
{ 1, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, multibandcompressor_detection_names, "detection0", "Detection 1" },
{ 1, 0.03125, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "compression0", "Gain Reduction 1" },
{ 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, multibandcompressor_detection_names, "detection0", "Detection 1" },
{ 1, 0.0625, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "compression0", "Gain Reduction 1" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "output0", "Output 1" },
{ 1, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass0", "Bypass 1" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "mute0", "Mute 1" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "solo0", "Solo 1" },
{ 0.03125, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold1", "Threshold 2" },
{ 3, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio1", "Ratio 2" },
{ 25, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack1", "Attack 2" },
{ 50, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release1", "Release 2" },
{ 0.125, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold1", "Threshold 2" },
{ 2, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio1", "Ratio 2" },
{ 100, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack1", "Attack 2" },
{ 200, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release1", "Release 2" },
{ 2, 1, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup1", "Makeup 2" },
{ 2.828427125, 1, 8, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "knee1", "Knee 2" },
{ 1, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, multibandcompressor_detection_names, "detection1", "Detection 2" },
{ 1, 0.03125, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "compression1", "Gain Reduction 2" },
{ 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, multibandcompressor_detection_names, "detection1", "Detection 2" },
{ 1, 0.0625, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "compression1", "Gain Reduction 2" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "output1", "Output 2" },
{ 1, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass1", "Bypass 2" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "mute1", "Mute 2" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "solo1", "Solo 2" },
{ 0.015625, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold2", "Threshold 3" },
{ 3, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio2", "Ratio 3" },
{ 12.5, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack2", "Attack 3" },
{ 25, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release2", "Release 3" },
{ 0.0625, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold2", "Threshold 3" },
{ 2, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio2", "Ratio 3" },
{ 50, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack2", "Attack 3" },
{ 100, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release2", "Release 3" },
{ 2, 1, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup2", "Makeup 3" },
{ 2.828427125, 1, 8, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "knee2", "Knee 3" },
{ 1, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, multibandcompressor_detection_names, "detection2", "Detection 3" },
{ 1, 0.03125, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "compression2", "Gain Reduction 3" },
{ 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, multibandcompressor_detection_names, "detection2", "Detection 3" },
{ 1, 0.0625, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "compression2", "Gain Reduction 3" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "output2", "Output 3" },
{ 1, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass2", "Bypass 3" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "mute2", "Mute 3" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "solo2", "Solo 3" },
{ 0.0078125, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold3", "Threshold 4" },
{ 3, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio3", "Ratio 4" },
{ 6.25, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack3", "Attack 4" },
{ 12.5, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release3", "Release 4" },
{ 0.03125, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold3", "Threshold 4" },
{ 2, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio3", "Ratio 4" },
{ 25, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack3", "Attack 4" },
{ 50, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release3", "Release 4" },
{ 2, 1, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup3", "Makeup 4" },
{ 2.828427125, 1, 8, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "knee3", "Knee 4" },
{ 1, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, multibandcompressor_detection_names, "detection3", "Detection 4" },
{ 1, 0.03125, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "compression3", "Gain Reduction 4" },
{ 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, multibandcompressor_detection_names, "detection3", "Detection 4" },
{ 1, 0.0625, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "compression3", "Gain Reduction 4" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "output3", "Output 4" },
{ 1, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass3", "Bypass 4" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "mute3", "Mute 4" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "solo3", "Solo 4" },
{}
};
@@ -390,7 +392,7 @@ const char *deesser_mode_names[] = { "Wide", "Split" };
CALF_PORT_PROPS(deesser) = {
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass", "Bypass" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "detected", "Detected" },
{ 0, 0.03125, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "compression", "Gain Reduction" },
{ 0, 0.0625, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "compression", "Gain Reduction" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "detected_led", "Active" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_out", "Out" },
{ 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, deesser_detection_names, "detection", "Detection" },
@@ -489,6 +491,108 @@ CALF_PORT_PROPS(sidechaingate) = {
CALF_PLUGIN_INFO(sidechaingate) = { 0x8504, "Sidechaingate", "Calf Sidechain Gate", "Markus Schmidt / Damien Zammit / Thor Harald Johansen", calf_plugins::calf_copyright_info, "ExpanderPlugin" };
////////////////////////////////////////////////////////////////////////////
CALF_PORT_NAMES(limiter) = {"In L", "In R", "Out L", "Out R"};
CALF_PORT_PROPS(limiter) = {
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass", "Bypass" },
{ 1, 0, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_in", "Input" },
{ 1, 0, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_out", "Output" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_inL", "Input L" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_inR", "Input R" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_outL", "Output L" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_outR", "Output R" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_inL", "0dB-InL" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_inR", "0dB-InR" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_outL", "0dB-OutL" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_outR", "0dB-OutR" },
{ 1, 0.0625, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "limit", "Limit" },
{ 5, 0.1, 10, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack", "Lookahead" },
{ 50, 1, 1000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release", "Release" },
{ 1, 0.125, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "att", "Attenuation" },
{ 1, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "asc", "ASC" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "asc_led", "asc active" },
{}
};
CALF_PLUGIN_INFO(limiter) = { 0x8521, "Limiter", "Calf Limiter", "Christian Holschuh / Markus Schmidt", calf_plugins::calf_copyright_info, "LimiterPlugin" };
////////////////////////////////////////////////////////////////////////////
CALF_PORT_NAMES(multibandlimiter) = {"In L", "In R", "Out L", "Out R"};
const char *multibandlimiter_filter_choices[] = { "12dB", "36dB"};
CALF_PORT_PROPS(multibandlimiter) = {
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass", "Bypass" },
{ 1, 0, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_in", "Input" },
{ 1, 0, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_out", "Output" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_inL", "Input L" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_inR", "Input R" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_outL", "Output L" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_outR", "Output R" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_inL", "0dB-InL" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_inR", "0dB-InR" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_outL", "0dB-OutL" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_outR", "0dB-OutR" },
{ 100, 10, 20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "freq0", "Split 1/2" },
{ 750, 10, 20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "freq1", "Split 2/3" },
{ 5000, 10, 20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "freq2", "Split 3/4" },
{ -0.17, -0.5, 0.5, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "sep0", "S1" },
{ -0.17, -0.5, 0.5, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "sep1", "S2" },
{ -0.17, -0.5, 0.5, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "sep2", "S3" },
{ 0.7762471166286917, 0.25, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_GRAPH, NULL, "q0", "Q1" },
{ 0.7762471166286917, 0.25, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_GRAPH, NULL, "q1", "Q2" },
{ 0.7762471166286917, 0.25, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_GRAPH, NULL, "q2", "Q3" },
{ 1, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, multibandlimiter_filter_choices, "mode", "Filter Mode" },
{ 1, 0.0625, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "limit", "Limit" },
{ 4, 0.1, 10, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack", "Lookahead" },
{ 30, 1, 1000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release", "Release" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "minrel", "Min Release" },
{ 1, 0.125, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "att0", "Low" },
{ 1, 0.125, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "att1", "LMid" },
{ 1, 0.125, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "att2", "HMid" },
{ 1, 0.125, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "att3", "Hi" },
{ 0.2f, -1.f, 1.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "weight0", "Weight 1" },
{ -0.2f, -1.f, 1.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "weight1", "Weight 2" },
{ 0.2f, -1.f, 1.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "weight2", "Weight 3" },
{ -0.2f, -1.f, 1.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "weight3", "Weight 4" },
{ 0.5f, -1.f, 1.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "release0", "Release 1" },
{ 0.2f, -1.f, 1.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "release1", "Release 2" },
{ -0.2f, -1.f, 1.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "release2", "Release 3" },
{ -0.5f, -1.f, 1.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "release3", "Release 4" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "solo0", "Solo 1" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "solo1", "Solo 2" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "solo2", "Solo 3" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "solo3", "Solo 4" },
{ 1, 0.f, 1000, 0, PF_FLOAT | PF_UNIT_MSEC | PF_PROP_OUTPUT, NULL, "effrelease0", "Effectively Release 1" },
{ 1, 0.f, 1000, 0, PF_FLOAT | PF_UNIT_MSEC | PF_PROP_OUTPUT, NULL, "effrelease1", "Effectively Release 2" },
{ 1, 0.f, 1000, 0, PF_FLOAT | PF_UNIT_MSEC | PF_PROP_OUTPUT, NULL, "effrelease2", "Effectively Release 3" },
{ 1, 0.f, 1000, 0, PF_FLOAT | PF_UNIT_MSEC | PF_PROP_OUTPUT, NULL, "effrelease3", "Effectively Release 4" },
{ 1, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "asc", "ASC" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "asc_led", "asc active" },
{}
};
CALF_PLUGIN_INFO(multibandlimiter) = { 0x8520, "Multibandlimiter", "Calf Multiband Limiter", "Markus Schmidt / Christian Holschuh", calf_plugins::calf_copyright_info, "LimiterPlugin" };
////////////////////////////////////////////////////////////////////////////
// A few macros to make
@@ -690,6 +794,79 @@ CALF_PORT_PROPS(bassenhancer) = {
CALF_PLUGIN_INFO(bassenhancer) = { 0x8532, "BassEnhancer", "Calf Bass Enhancer", "Markus Schmidt / Krzysztof Foltman", calf_plugins::calf_copyright_info, "DistortionPlugin" };
////////////////////////////////////////////////////////////////////////////
CALF_PORT_NAMES(mono) = {"In", "Out L", "Out R"};
CALF_PORT_PROPS(mono) = {
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass", "Bypass" },
{ 1, 0, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_in", "Input" },
{ 1, 0, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_out", "Output" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_in", "Input" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_outL", "Output L" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_outR", "Output R" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_in", "0dB-In" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_outL", "0dB-OutL" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_outR", "0dB-OutR" },
{ 0.f, -1.f, 1.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "balance_out", "Balance" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "softclip", "Softclip" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "mutel", "Mute L" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "muter", "Mute R" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "phasel", "Phase L" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "phaser", "Phase R" },
{ 0.f, -20.f, 20.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "delay", "Delay" },
{}
};
CALF_PLUGIN_INFO(mono) = { 0x8589, "MonoInput", "Calf Mono Input", "Markus Schmidt", calf_plugins::calf_copyright_info, "Utility" };
////////////////////////////////////////////////////////////////////////////
CALF_PORT_NAMES(stereo) = {"In L", "In R", "Out L", "Out R"};
const char *stereo_mode_names[] = { "LR ▸ LR (Stereo Default)", "LR ▸ MS (Stereo to Mid-Side)", "MS ▸ LR (Mid-Side to Stereo)", "LR ▸ LL (Mono Left Channel)", "LR ▸ RR (Mono Right Channel)", "LR ▸ L+R (Mono Sum L+R)", "LR ▸ RL (Stereo Flip Channels)" };
CALF_PORT_PROPS(stereo) = {
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass", "Bypass" },
{ 1, 0, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_in", "Input" },
{ 1, 0, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_out", "Output" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_inL", "Input L" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_inR", "Input R" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_outL", "Output L" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_outR", "Output R" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_inL", "0dB-InL" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_inR", "0dB-InR" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_outL", "0dB-OutL" },
{ 0, 0, 1, 0, PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_outR", "0dB-OutR" },
{ 0.f, -1.f, 1.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "balance_in", "Balance In" },
{ 0.f, -1.f, 1.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "balance_out", "Balance Out" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "softclip", "Softclip" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "mutel", "Mute L" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "muter", "Mute R" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "phasel", "Phase L" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "phaser", "Phase R" },
{ 0, 0, 6, 0, PF_ENUM | PF_CTL_COMBO, stereo_mode_names, "mode", "Mode" },
{ 0.f, -1.f, 1.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "slev", "S Level" },
{ 0.f, -1.f, 1.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "sbal", "S Balance" },
{ 0.f, -1.f, 1.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "mlev", "M Level" },
{ 0.f, -1.f, 1.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "mpan", "M Panorama" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "widener", "Widener" },
{ 0.f, -20.f, 20.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "delay", "Delay" },
{ 0.f, 0.f, 1.f, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_COEF | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_phase", "Phase Correlation" },
{}
};
CALF_PLUGIN_INFO(stereo) = { 0x8588, "StereoTools", "Calf Stereo Tools", "Markus Schmidt", calf_plugins::calf_copyright_info, "Utility" };
////////////////////////////////////////////////////////////////////////////
CALF_PORT_NAMES(monosynth) = {

View File

@@ -445,3 +445,353 @@ bool filterclavier_audio_module::get_graph(int index, int subindex, float *data,
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////
stereo_audio_module::stereo_audio_module() {
active = false;
clip_inL = 0.f;
clip_inR = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
}
void stereo_audio_module::activate() {
active = true;
}
void stereo_audio_module::deactivate() {
active = false;
}
void stereo_audio_module::params_changed() {
float slev = 2 * *params[param_slev]; // stereo level ( -2 -> 2 )
float sbal = 1 + *params[param_sbal]; // stereo balance ( 0 -> 2 )
float mlev = 2 * *params[param_mlev]; // mono level ( -2 -> 2 )
float mpan = 1 + *params[param_mpan]; // mono pan ( 0 -> 2 )
switch((int)*params[param_mode])
{
case 0:
default:
//LR->LR
LL = (mlev * (2.f - mpan) + slev * (2.f - sbal));
LR = (mlev * mpan - slev * sbal);
RL = (mlev * (2.f - mpan) - slev * (2.f - sbal));
RR = (mlev * mpan + slev * sbal);
break;
case 1:
//LR->MS
LL = (2.f - mpan) * (2.f - sbal);
LR = mpan * (2.f - sbal) * -1;
RL = (2.f - mpan) * sbal;
RR = mpan * sbal;
break;
case 2:
//MS->LR
LL = mlev * (2.f - sbal);
LR = mlev * mpan;
RL = slev * (2.f - sbal);
RR = slev * sbal * -1;
break;
case 3:
case 4:
case 5:
case 6:
//LR->LL
LL = 0.f;
LR = 0.f;
RL = 0.f;
RR = 0.f;
break;
}
}
uint32_t stereo_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
for(uint32_t i = offset; i < offset + numsamples; i++) {
if(*params[param_bypass] > 0.5) {
outs[0][i] = ins[0][i];
outs[1][i] = ins[1][i];
clip_inL = 0.f;
clip_inR = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
} else {
// let meters fall a bit
clip_inL -= std::min(clip_inL, numsamples);
clip_inR -= std::min(clip_inR, numsamples);
clip_outL -= std::min(clip_outL, numsamples);
clip_outR -= std::min(clip_outR, numsamples);
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
float L = ins[0][i];
float R = ins[1][i];
// levels in
L *= *params[param_level_in];
R *= *params[param_level_in];
// balance in
L *= (1.f - std::max(0.f, *params[param_balance_in]));
R *= (1.f + std::min(0.f, *params[param_balance_in]));
// copy / flip / mono ...
switch((int)*params[param_mode])
{
case 0:
default:
// LR > LR
break;
case 1:
// LR > MS
break;
case 2:
// MS > LR
break;
case 3:
// LR > LL
R = L;
break;
case 4:
// LR > RR
L = R;
break;
case 5:
// LR > L+R
L = (L + R) / 2;
R = L;
break;
case 6:
// LR > RL
float tmp = L;
L = R;
R = tmp;
break;
}
// softclip
if(*params[param_softclip]) {
int ph;
ph = L / fabs(L);
L = L > 0.63 ? ph * (0.63 + 0.36 * (1 - pow(MATH_E, (1.f / 3) * (0.63 + L * ph)))) : L;
ph = R / fabs(R);
R = R > 0.63 ? ph * (0.63 + 0.36 * (1 - pow(MATH_E, (1.f / 3) * (0.63 + R * ph)))) : R;
}
// GUI stuff
if(L > meter_inL) meter_inL = L;
if(R > meter_inR) meter_inR = R;
if(L > 1.f) clip_inL = srate >> 3;
if(R > 1.f) clip_inR = srate >> 3;
// mute
L *= (1 - floor(*params[param_mute_l] + 0.5));
R *= (1 - floor(*params[param_mute_r] + 0.5));
// phase
L *= (2 * (1 - floor(*params[param_phase_l] + 0.5))) - 1;
R *= (2 * (1 - floor(*params[param_phase_r] + 0.5))) - 1;
// LR/MS
L += LL*L + RL*R;
R += RR*R + LR*L;
// widener
L += *params[param_widener] * R * -1;
R += *params[param_widener] * L * -1;
// delay
buffer[pos] = L;
buffer[pos + 1] = R;
int nbuf = srate * (fabs(*params[param_delay]) / 1000.f);
nbuf -= nbuf % 2;
if(*params[param_delay] > 0.f) {
R = buffer[(pos - (int)nbuf + 1 + buffer_size) % buffer_size];
} else if (*params[param_delay] < 0.f) {
L = buffer[(pos - (int)nbuf + buffer_size) % buffer_size];
}
pos = (pos + 2) % buffer_size;
// balance out
L *= (1.f - std::max(0.f, *params[param_balance_out]));
R *= (1.f + std::min(0.f, *params[param_balance_out]));
// level
L *= *params[param_level_out];
R *= *params[param_level_out];
//output
outs[0][i] = L;
outs[1][i] = R;
// clip LED's
if(L > 1.f) clip_outL = srate >> 3;
if(R > 1.f) clip_outR = srate >> 3;
if(L > meter_outL) meter_outL = L;
if(R > meter_outR) meter_outR = R;
// phase meter
if(fabs(L) > 0.001 and fabs(R) > 0.001) {
meter_phase = fabs(fabs(L+R) > 0.000000001 ? sin(fabs((L-R)/(L+R))) : 0.f);
} else {
meter_phase = 0.f;
}
}
}
// draw meters
SET_IF_CONNECTED(clip_inL);
SET_IF_CONNECTED(clip_inR);
SET_IF_CONNECTED(clip_outL);
SET_IF_CONNECTED(clip_outR);
SET_IF_CONNECTED(meter_inL);
SET_IF_CONNECTED(meter_inR);
SET_IF_CONNECTED(meter_outL);
SET_IF_CONNECTED(meter_outR);
SET_IF_CONNECTED(meter_phase);
return outputs_mask;
}
void stereo_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
// rebuild buffer
buffer_size = (int)(srate * 0.05 * 2.f); // buffer size attack rate multiplied by 2 channels
buffer = (float*) calloc(buffer_size, sizeof(float));
memset(buffer, 0, buffer_size * sizeof(float)); // reset buffer to zero
pos = 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////
mono_audio_module::mono_audio_module() {
active = false;
clip_in = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_in = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
}
void mono_audio_module::activate() {
active = true;
}
void mono_audio_module::deactivate() {
active = false;
}
void mono_audio_module::params_changed() {
}
uint32_t mono_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
for(uint32_t i = offset; i < offset + numsamples; i++) {
if(*params[param_bypass] > 0.5) {
outs[0][i] = ins[0][i];
outs[1][i] = ins[0][i];
clip_in = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_in = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
} else {
// let meters fall a bit
clip_in -= std::min(clip_in, numsamples);
clip_outL -= std::min(clip_outL, numsamples);
clip_outR -= std::min(clip_outR, numsamples);
meter_in = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
float L = ins[0][i];
// levels in
L *= *params[param_level_in];
// softclip
if(*params[param_softclip]) {
int ph = L / fabs(L);
L = L > 0.63 ? ph * (0.63 + 0.36 * (1 - pow(MATH_E, (1.f / 3) * (0.63 + L * ph)))) : L;
}
// GUI stuff
if(L > meter_in) meter_in = L;
if(L > 1.f) clip_in = srate >> 3;
float R = L;
// mute
L *= (1 - floor(*params[param_mute_l] + 0.5));
R *= (1 - floor(*params[param_mute_r] + 0.5));
// phase
L *= (2 * (1 - floor(*params[param_phase_l] + 0.5))) - 1;
R *= (2 * (1 - floor(*params[param_phase_r] + 0.5))) - 1;
// delay
buffer[pos] = L;
buffer[pos + 1] = R;
int nbuf = srate * (fabs(*params[param_delay]) / 1000.f);
nbuf -= nbuf % 2;
if(*params[param_delay] > 0.f) {
R = buffer[(pos - (int)nbuf + 1 + buffer_size) % buffer_size];
} else if (*params[param_delay] < 0.f) {
L = buffer[(pos - (int)nbuf + buffer_size) % buffer_size];
}
pos = (pos + 2) % buffer_size;
// balance out
L *= (1.f - std::max(0.f, *params[param_balance_out]));
R *= (1.f + std::min(0.f, *params[param_balance_out]));
// level
L *= *params[param_level_out];
R *= *params[param_level_out];
//output
outs[0][i] = L;
outs[1][i] = R;
// clip LED's
if(L > 1.f) clip_outL = srate >> 3;
if(R > 1.f) clip_outR = srate >> 3;
if(L > meter_outL) meter_outL = L;
if(R > meter_outR) meter_outR = R;
}
}
// draw meters
SET_IF_CONNECTED(clip_in);
SET_IF_CONNECTED(clip_outL);
SET_IF_CONNECTED(clip_outR);
SET_IF_CONNECTED(meter_in);
SET_IF_CONNECTED(meter_outL);
SET_IF_CONNECTED(meter_outR);
return outputs_mask;
}
void mono_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
// rebuild buffer
buffer_size = (int)srate * 0.05 * 2; // delay buffer size multiplied by 2 channels
buffer = (float*) calloc(buffer_size, sizeof(float));
memset(buffer, 0, buffer_size * sizeof(float)); // reset buffer to zero
pos = 0;
}

View File

@@ -28,7 +28,7 @@ using namespace calf_plugins;
#define SET_IF_CONNECTED(name) if (params[AM::param_##name] != NULL) *params[AM::param_##name] = name;
/// Multibandcompressor by Markus Schmidt
/// Multiband Compressor by Markus Schmidt
///
/// This module splits the signal in four different bands
/// and sends them through multiple filters (implemented by
@@ -50,6 +50,12 @@ multibandcompressor_audio_module::multibandcompressor_audio_module()
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
for(int i = 0; i < strips - 1; i ++) {
freq_old[i] = -1;
sep_old[i] = -1;
q_old[i] = -1;
}
mode_old = -1;
}
void multibandcompressor_audio_module::activate()
@@ -75,39 +81,77 @@ void multibandcompressor_audio_module::deactivate()
void multibandcompressor_audio_module::params_changed()
{
// determine mute/solo states
solo[0] = *params[param_solo0] > 0.f ? true : false;
solo[1] = *params[param_solo1] > 0.f ? true : false;
solo[2] = *params[param_solo2] > 0.f ? true : false;
solo[3] = *params[param_solo3] > 0.f ? true : false;
no_solo = (*params[param_solo0] > 0.f ||
*params[param_solo1] > 0.f ||
*params[param_solo2] > 0.f ||
*params[param_solo3] > 0.f) ? false : true;
int i;
int j1;
switch(mode) {
case 0:
default:
j1 = 0;
break;
case 1:
j1 = 2;
break;
}
// set the params of all filters
if(*params[param_freq0] != freq_old[0] or *params[param_sep0] != sep_old[0] or *params[param_q0] != q_old[0]) {
lpL0.set_lp_rbj((float)(*params[param_freq0] * (1 - *params[param_sep0])), *params[param_q0], (float)srate);
lpR0.copy_coeffs(lpL0);
hpL0.set_hp_rbj((float)(*params[param_freq0] * (1 + *params[param_sep0])), *params[param_q0], (float)srate);
hpR0.copy_coeffs(hpL0);
if(*params[param_freq0] != freq_old[0] or *params[param_sep0] != sep_old[0] or *params[param_q0] != q_old[0] or *params[param_mode] != mode_old) {
lpL[0][0].set_lp_rbj((float)(*params[param_freq0] * (1 - *params[param_sep0])), *params[param_q0], (float)srate);
hpL[0][0].set_hp_rbj((float)(*params[param_freq0] * (1 + *params[param_sep0])), *params[param_q0], (float)srate);
lpR[0][0].copy_coeffs(lpL[0][0]);
hpR[0][0].copy_coeffs(hpL[0][0]);
for(i = 1; i <= j1; i++) {
lpL[0][i].copy_coeffs(lpL[0][0]);
hpL[0][i].copy_coeffs(hpL[0][0]);
lpR[0][i].copy_coeffs(lpL[0][0]);
hpR[0][i].copy_coeffs(hpL[0][0]);
}
freq_old[0] = *params[param_freq0];
sep_old[0] = *params[param_sep0];
q_old[0] = *params[param_q0];
}
if(*params[param_freq1] != freq_old[1] or *params[param_sep1] != sep_old[1] or *params[param_q1] != q_old[1]) {
lpL1.set_lp_rbj((float)(*params[param_freq1] * (1 - *params[param_sep1])), *params[param_q1], (float)srate);
lpR1.copy_coeffs(lpL1);
hpL1.set_hp_rbj((float)(*params[param_freq1] * (1 + *params[param_sep1])), *params[param_q1], (float)srate);
hpR1.copy_coeffs(hpL1);
if(*params[param_freq1] != freq_old[1] or *params[param_sep1] != sep_old[1] or *params[param_q1] != q_old[1] or *params[param_mode] != mode_old) {
lpL[1][0].set_lp_rbj((float)(*params[param_freq1] * (1 - *params[param_sep1])), *params[param_q1], (float)srate);
hpL[1][0].set_hp_rbj((float)(*params[param_freq1] * (1 + *params[param_sep1])), *params[param_q1], (float)srate);
lpR[1][0].copy_coeffs(lpL[1][0]);
hpR[1][0].copy_coeffs(hpL[1][0]);
for(i = 1; i <= j1; i++) {
lpL[1][i].copy_coeffs(lpL[1][0]);
hpL[1][i].copy_coeffs(hpL[1][0]);
lpR[1][i].copy_coeffs(lpL[1][0]);
hpR[1][i].copy_coeffs(hpL[1][0]);
}
freq_old[1] = *params[param_freq1];
sep_old[1] = *params[param_sep1];
q_old[1] = *params[param_q1];
}
if(*params[param_freq2] != freq_old[2] or *params[param_sep2] != sep_old[2] or *params[param_q2] != q_old[2]) {
lpL2.set_lp_rbj((float)(*params[param_freq2] * (1 - *params[param_sep2])), *params[param_q2], (float)srate);
lpR2.copy_coeffs(lpL2);
hpL2.set_hp_rbj((float)(*params[param_freq2] * (1 + *params[param_sep2])), *params[param_q2], (float)srate);
hpR2.copy_coeffs(hpL2);
if(*params[param_freq2] != freq_old[2] or *params[param_sep2] != sep_old[2] or *params[param_q2] != q_old[2] or *params[param_mode] != mode_old) {
lpL[2][0].set_lp_rbj((float)(*params[param_freq2] * (1 - *params[param_sep2])), *params[param_q2], (float)srate);
hpL[2][0].set_hp_rbj((float)(*params[param_freq2] * (1 + *params[param_sep2])), *params[param_q2], (float)srate);
lpR[2][0].copy_coeffs(lpL[2][0]);
hpR[2][0].copy_coeffs(hpL[2][0]);
for(i = 1; i <= j1; i++) {
lpL[2][i].copy_coeffs(lpL[2][0]);
hpL[2][i].copy_coeffs(hpL[2][0]);
lpR[2][i].copy_coeffs(lpL[2][0]);
hpR[2][i].copy_coeffs(hpL[2][0]);
}
freq_old[2] = *params[param_freq2];
sep_old[2] = *params[param_sep2];
q_old[2] = *params[param_q2];
}
// set the params of all strips
strip[0].set_params(*params[param_attack0], *params[param_release0], *params[param_threshold0], *params[param_ratio0], *params[param_knee0], *params[param_makeup0], *params[param_detection0], 1.f, *params[param_bypass0], *params[param_mute0]);
strip[1].set_params(*params[param_attack1], *params[param_release1], *params[param_threshold1], *params[param_ratio1], *params[param_knee1], *params[param_makeup1], *params[param_detection1], 1.f, *params[param_bypass1], *params[param_mute1]);
strip[2].set_params(*params[param_attack2], *params[param_release2], *params[param_threshold2], *params[param_ratio2], *params[param_knee2], *params[param_makeup2], *params[param_detection2], 1.f, *params[param_bypass2], *params[param_mute2]);
strip[3].set_params(*params[param_attack3], *params[param_release3], *params[param_threshold3], *params[param_ratio3], *params[param_knee3], *params[param_makeup3], *params[param_detection3], 1.f, *params[param_bypass3], *params[param_mute3]);
strip[0].set_params(*params[param_attack0], *params[param_release0], *params[param_threshold0], *params[param_ratio0], *params[param_knee0], *params[param_makeup0], *params[param_detection0], 1.f, *params[param_bypass0], !(solo[0] || no_solo));
strip[1].set_params(*params[param_attack1], *params[param_release1], *params[param_threshold1], *params[param_ratio1], *params[param_knee1], *params[param_makeup1], *params[param_detection1], 1.f, *params[param_bypass1], !(solo[1] || no_solo));
strip[2].set_params(*params[param_attack2], *params[param_release2], *params[param_threshold2], *params[param_ratio2], *params[param_knee2], *params[param_makeup2], *params[param_detection2], 1.f, *params[param_bypass2], !(solo[2] || no_solo));
strip[3].set_params(*params[param_attack3], *params[param_release3], *params[param_threshold3], *params[param_ratio3], *params[param_knee3], *params[param_makeup3], *params[param_detection3], 1.f, *params[param_bypass3], !(solo[3] || no_solo));
}
void multibandcompressor_audio_module::set_sample_rate(uint32_t sr)
@@ -156,12 +200,6 @@ uint32_t multibandcompressor_audio_module::process(uint32_t offset, uint32_t num
} else {
// process all strips
// determine mute state of strips
mute[0] = *params[param_mute0] > 0.f ? true : false;
mute[1] = *params[param_mute1] > 0.f ? true : false;
mute[2] = *params[param_mute2] > 0.f ? true : false;
mute[3] = *params[param_mute3] > 0.f ? true : false;
// let meters fall a bit
clip_inL -= std::min(clip_inL, numsamples);
clip_inR -= std::min(clip_inR, numsamples);
@@ -181,47 +219,37 @@ uint32_t multibandcompressor_audio_module::process(uint32_t offset, uint32_t num
// out vars
float outL = 0.f;
float outR = 0.f;
int j1;
for (int i = 0; i < strips; i ++) {
// cycle trough strips
if (!mute[i]) {
if (solo[i] || no_solo) {
// strip unmuted
float left = inL;
float right = inR;
// send trough filters
switch (i) {
switch(mode) {
case 0:
left = lpL0.process(left);
right = lpR0.process(right);
lpL0.sanitize();
lpR0.sanitize();
default:
j1 = 0;
break;
case 1:
left = lpL1.process(left);
right = lpR1.process(right);
left = hpL0.process(left);
right = hpR0.process(right);
lpL1.sanitize();
lpR1.sanitize();
hpL0.sanitize();
hpR0.sanitize();
break;
case 2:
left = lpL2.process(left);
right = lpR2.process(right);
left = hpL1.process(left);
right = hpR1.process(right);
lpL2.sanitize();
lpR2.sanitize();
hpL1.sanitize();
hpR1.sanitize();
break;
case 3:
left = hpL2.process(left);
right = hpR2.process(right);
hpL2.sanitize();
hpR2.sanitize();
j1 = 2;
break;
}
for (int j = 0; j <= j1; j++){
if(i + 1 < strips) {
left = lpL[i][j].process(left);
right = lpR[i][j].process(right);
lpL[i][j].sanitize();
lpR[i][j].sanitize();
}
if(i - 1 >= 0) {
left = hpL[i - 1][j].process(left);
right = hpR[i - 1][j].process(right);
hpL[i - 1][j].sanitize();
hpR[i - 1][j].sanitize();
}
}
// process gain reduction
strip[i].process(left, right);
// sum up output
@@ -237,8 +265,16 @@ uint32_t multibandcompressor_audio_module::process(uint32_t offset, uint32_t num
// even out filters gain reduction
// 3dB - levelled manually (based on default sep and q settings)
outL *= 1.414213562;
outR *= 1.414213562;
switch(mode) {
case 0:
outL *= 1.414213562;
outR *= 1.414213562;
break;
case 1:
outL *= 0.88;
outR *= 0.88;
break;
}
// out level
outL *= *params[param_level_out];
@@ -496,6 +532,10 @@ sidechaincompressor_audio_module::sidechaincompressor_audio_module()
f2_freq_old1 = 0.f;
f1_level_old1 = 0.f;
f2_level_old1 = 0.f;
f1_freq_old = 0.f;
f2_freq_old = 0.f;
f1_level_old = 0.f;
f2_level_old = 0.f;
sc_mode_old1 = WIDEBAND;
meters.reset();
}
@@ -869,6 +909,13 @@ deesser_audio_module::deesser_audio_module()
f1_level_old1 = 0.f;
f2_level_old1 = 0.f;
f2_q_old1 = 0.f;
f1_freq_old = 0.f;
f2_freq_old = 0.f;
f1_level_old = 0.f;
f2_level_old = 0.f;
f2_q_old = 0.f;
detected_led = 0;
clip_led = 0;
}
void deesser_audio_module::activate()
@@ -1605,6 +1652,17 @@ gain_reduction_audio_module::gain_reduction_audio_module()
old_detection = 0.f;
old_bypass = 0.f;
old_mute = 0.f;
linSlope = 0.f;
attack = 0.f;
release = 0.f;
detection = -1;
stereo_link = -1;
threshold = -1;
ratio = -1;
knee = -1;
makeup = -1;
bypass = -1;
mute = -1;
}
void gain_reduction_audio_module::activate()
@@ -1617,7 +1675,7 @@ void gain_reduction_audio_module::activate()
l = r = 0.f;
float byp = bypass;
bypass = 0.0;
process(l, r);
process(l, r, 0, 0);
bypass = byp;
}
@@ -1650,14 +1708,16 @@ void gain_reduction_audio_module::process(float &left, float &right, const float
if(bypass < 0.5f) {
// this routine is mainly copied from thor's compressor module
// greatest sounding compressor I've heard!
bool rms = detection == 0;
bool average = stereo_link == 0;
bool rms = (detection == 0);
bool average = (stereo_link == 0);
float attack_coeff = std::min(1.f, 1.f / (attack * srate / 4000.f));
float release_coeff = std::min(1.f, 1.f / (release * srate / 4000.f));
float absample = average ? (fabs(*det_left) + fabs(*det_right)) * 0.5f : std::max(fabs(*det_left), fabs(*det_right));
if(rms) absample *= absample;
dsp::sanitize(linSlope);
linSlope += (absample - linSlope) * (absample > linSlope ? attack_coeff : release_coeff);
float gain = 1.f;
if(linSlope > 0.f) {
@@ -1767,7 +1827,7 @@ bool gain_reduction_audio_module::get_dot(int subindex, float &x, float &y, int
if(bypass > 0.5f or mute > 0.f) {
return false;
} else {
bool rms = detection == 0;
bool rms = (detection == 0);
float det = rms ? sqrt(detected) : detected;
x = 0.5 + 0.5 * dB_grid(det);
y = dB_grid(bypass > 0.5f or mute > 0.f ? det : output_level(det));
@@ -1831,7 +1891,15 @@ expander_audio_module::expander_audio_module()
is_active = false;
srate = 0;
last_generation = 0;
range = -1.f;
threshold = -1.f;
ratio = -1.f;
knee = -1.f;
makeup = -1.f;
detection = -1.f;
bypass = -1.f;
mute = -1.f;
stereo_link = -1.f;
old_range = 0.f;
old_threshold = 0.f;
old_ratio = 0.f;
@@ -1842,6 +1910,8 @@ expander_audio_module::expander_audio_module()
old_mute = 0.f;
old_trigger = 0.f;
old_stereo_link = 0.f;
linSlope = -1;
linKneeStop = 0;
}
void expander_audio_module::activate()
@@ -1865,7 +1935,7 @@ void expander_audio_module::deactivate()
void expander_audio_module::update_curve()
{
bool rms = detection == 0;
bool rms = (detection == 0);
float linThreshold = threshold;
if (rms)
linThreshold = linThreshold * linThreshold;
@@ -1891,11 +1961,13 @@ void expander_audio_module::process(float &left, float &right, const float *det_
}
if(bypass < 0.5f) {
// this routine is mainly copied from Damien's expander module based on Thor's compressor
bool rms = detection == 0;
bool average = stereo_link == 0;
bool rms = (detection == 0);
bool average = (stereo_link == 0);
float absample = average ? (fabs(*det_left) + fabs(*det_right)) * 0.5f : std::max(fabs(*det_left), fabs(*det_right));
if(rms) absample *= absample;
dsp::sanitize(linSlope);
linSlope += (absample - linSlope) * (absample > linSlope ? attack_coeff : release_coeff);
float gain = 1.f;
if(linSlope > 0.f) {
@@ -1910,7 +1982,7 @@ void expander_audio_module::process(float &left, float &right, const float *det_
}
float expander_audio_module::output_level(float slope) const {
bool rms = detection == 0;
bool rms = (detection == 0);
return slope * output_gain(rms ? slope*slope : slope, rms) * makeup;
}
@@ -2001,7 +2073,7 @@ bool expander_audio_module::get_dot(int subindex, float &x, float &y, int &size,
if(bypass > 0.5f or mute > 0.f) {
return false;
} else {
bool rms = detection == 0;
bool rms = (detection == 0);
float det = rms ? sqrt(detected) : detected;
x = 0.5 + 0.5 * dB_grid(det);
y = dB_grid(bypass > 0.5f or mute > 0.f ? det : output_level(det));

View File

@@ -37,6 +37,12 @@ saturator_audio_module::saturator_audio_module()
is_active = false;
srate = 0;
meter_drive = 0.f;
lp_pre_freq_old = -1;
hp_pre_freq_old = -1;
lp_post_freq_old = -1;
hp_post_freq_old = -1;
p_freq_old = -1;
p_level_old = -1;
}
void saturator_audio_module::activate()

View File

@@ -0,0 +1,646 @@
/* Calf DSP plugin pack
* Limiter related plugins
*
* Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
*
* 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 <limits.h>
#include <memory.h>
#include <calf/giface.h>
#include <calf/modules_limit.h>
using namespace dsp;
using namespace calf_plugins;
#define SET_IF_CONNECTED(name) if (params[AM::param_##name] != NULL) *params[AM::param_##name] = name;
/// Limiter by Markus Schmidt and Christian Holschuh
///
/// This module provides the lookahead limiter as a simple audio module
/// with choosable lookahead and release time.
///////////////////////////////////////////////////////////////////////////////////////////////
limiter_audio_module::limiter_audio_module()
{
is_active = false;
srate = 0;
// zero all displays
clip_inL = 0.f;
clip_inR = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
asc_led = 0.f;
}
void limiter_audio_module::activate()
{
is_active = true;
// set all filters and strips
params_changed();
limiter.activate();
}
void limiter_audio_module::deactivate()
{
is_active = false;
limiter.deactivate();
}
void limiter_audio_module::params_changed()
{
limiter.set_params(*params[param_limit], *params[param_attack], *params[param_release], 1.f, *params[param_asc], true);
}
void limiter_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
limiter.set_sample_rate(srate);
}
uint32_t limiter_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
{
bool bypass = *params[param_bypass] > 0.5f;
numsamples += offset;
if(bypass) {
// everything bypassed
while(offset < numsamples) {
outs[0][offset] = ins[0][offset];
outs[1][offset] = ins[1][offset];
++offset;
}
// displays, too
clip_inL = 0.f;
clip_inR = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
asc_led = 0.f;
} else {
// let meters fall a bit
clip_inL -= std::min(clip_inL, numsamples);
clip_inR -= std::min(clip_inR, numsamples);
clip_outL -= std::min(clip_outL, numsamples);
clip_outR -= std::min(clip_outR, numsamples);
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
asc_led -= std::min(asc_led, numsamples);
while(offset < numsamples) {
// cycle through samples
float inL = ins[0][offset];
float inR = ins[1][offset];
// in level
inR *= *params[param_level_in];
inL *= *params[param_level_in];
// out vars
float outL = inL;
float outR = inR;
// process gain reduction
float fickdich[0];
limiter.process(outL, outR, fickdich);
if(limiter.get_arc())
asc_led = srate >> 3;
// autolevel
outL /= *params[param_limit];
outR /= *params[param_limit];
// out level
outL *= *params[param_level_out];
outR *= *params[param_level_out];
// send to output
outs[0][offset] = outL;
outs[1][offset] = outR;
// clip LED's
if(inL > 1.f) {
clip_inL = srate >> 3;
}
if(inR > 1.f) {
clip_inR = srate >> 3;
}
if(outL > 1.f) {
clip_outL = srate >> 3;
}
if(outR > 1.f) {
clip_outR = srate >> 3;
}
// set up in / out meters
if(inL > meter_inL) {
meter_inL = inL;
}
if(inR > meter_inR) {
meter_inR = inR;
}
if(outL > meter_outL) {
meter_outL = outL;
}
if(outR > meter_outR) {
meter_outR = outR;
}
// next sample
++offset;
} // cycle trough samples
} // process (no bypass)
// draw meters
SET_IF_CONNECTED(clip_inL);
SET_IF_CONNECTED(clip_inR);
SET_IF_CONNECTED(clip_outL);
SET_IF_CONNECTED(clip_outR);
SET_IF_CONNECTED(meter_inL);
SET_IF_CONNECTED(meter_inR);
SET_IF_CONNECTED(meter_outL);
SET_IF_CONNECTED(meter_outR);
if (params[param_asc_led] != NULL) *params[param_asc_led] = asc_led;
if (*params[param_att]) {
if(bypass)
*params[param_att] = 1.f;
else
*params[param_att] = limiter.get_attenuation();
}
// whatever has to be returned x)
return outputs_mask;
}
/// Multiband Limiter by Markus Schmidt and Christian Holschuh
///
/// This module splits the signal in four different bands
/// and sends them through multiple filters (implemented by
/// Krzysztof). They are processed by a lookahead limiter module afterwards
/// and summed up to the final output again.
///////////////////////////////////////////////////////////////////////////////////////////////
multibandlimiter_audio_module::multibandlimiter_audio_module()
{
is_active = false;
srate = 0;
// zero all displays
clip_inL = 0.f;
clip_inR = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
asc_led = 0.f;
__attack = -1.f;
channels = 2;
buffer_size = 0;
overall_buffer_size = 0;
_sanitize = false;
for(int i = 0; i < strips - 1; i ++) {
freq_old[i] = -1;
sep_old[i] = -1;
q_old[i] = -1;
}
mode_old = 0;
}
void multibandlimiter_audio_module::activate()
{
is_active = true;
// set all filters and strips
params_changed();
// activate all strips
for (int j = 0; j < strips; j ++) {
strip[j].activate();
strip[j].set_multi(true);
strip[j].id = j;
}
broadband.activate();
pos = 0;
}
void multibandlimiter_audio_module::deactivate()
{
is_active = false;
// deactivate all strips
for (int j = 0; j < strips; j ++) {
strip[j].deactivate();
}
broadband.deactivate();
}
void multibandlimiter_audio_module::params_changed()
{
// determine mute/solo states
solo[0] = *params[param_solo0] > 0.f ? true : false;
solo[1] = *params[param_solo1] > 0.f ? true : false;
solo[2] = *params[param_solo2] > 0.f ? true : false;
solo[3] = *params[param_solo3] > 0.f ? true : false;
no_solo = (*params[param_solo0] > 0.f ||
*params[param_solo1] > 0.f ||
*params[param_solo2] > 0.f ||
*params[param_solo3] > 0.f) ? false : true;
mode_old = mode;
mode = *params[param_mode];
int i;
int j1;
switch(mode) {
case 0:
default:
j1 = 0;
break;
case 1:
j1 = 2;
break;
}
// set the params of all filters
if(*params[param_freq0] != freq_old[0] or *params[param_sep0] != sep_old[0] or *params[param_q0] != q_old[0] or *params[param_mode] != mode_old) {
lpL[0][0].set_lp_rbj((float)(*params[param_freq0] * (1 - *params[param_sep0])), *params[param_q0], (float)srate);
hpL[0][0].set_hp_rbj((float)(*params[param_freq0] * (1 + *params[param_sep0])), *params[param_q0], (float)srate);
lpR[0][0].copy_coeffs(lpL[0][0]);
hpR[0][0].copy_coeffs(hpL[0][0]);
for(i = 1; i <= j1; i++) {
lpL[0][i].copy_coeffs(lpL[0][0]);
hpL[0][i].copy_coeffs(hpL[0][0]);
lpR[0][i].copy_coeffs(lpL[0][0]);
hpR[0][i].copy_coeffs(hpL[0][0]);
}
freq_old[0] = *params[param_freq0];
sep_old[0] = *params[param_sep0];
q_old[0] = *params[param_q0];
}
if(*params[param_freq1] != freq_old[1] or *params[param_sep1] != sep_old[1] or *params[param_q1] != q_old[1] or *params[param_mode] != mode_old) {
lpL[1][0].set_lp_rbj((float)(*params[param_freq1] * (1 - *params[param_sep1])), *params[param_q1], (float)srate);
hpL[1][0].set_hp_rbj((float)(*params[param_freq1] * (1 + *params[param_sep1])), *params[param_q1], (float)srate);
lpR[1][0].copy_coeffs(lpL[1][0]);
hpR[1][0].copy_coeffs(hpL[1][0]);
for(i = 1; i <= j1; i++) {
lpL[1][i].copy_coeffs(lpL[1][0]);
hpL[1][i].copy_coeffs(hpL[1][0]);
lpR[1][i].copy_coeffs(lpL[1][0]);
hpR[1][i].copy_coeffs(hpL[1][0]);
}
freq_old[1] = *params[param_freq1];
sep_old[1] = *params[param_sep1];
q_old[1] = *params[param_q1];
}
if(*params[param_freq2] != freq_old[2] or *params[param_sep2] != sep_old[2] or *params[param_q2] != q_old[2] or *params[param_mode] != mode_old) {
lpL[2][0].set_lp_rbj((float)(*params[param_freq2] * (1 - *params[param_sep2])), *params[param_q2], (float)srate);
hpL[2][0].set_hp_rbj((float)(*params[param_freq2] * (1 + *params[param_sep2])), *params[param_q2], (float)srate);
lpR[2][0].copy_coeffs(lpL[2][0]);
hpR[2][0].copy_coeffs(hpL[2][0]);
for(i = 1; i <= j1; i++) {
lpL[2][i].copy_coeffs(lpL[2][0]);
hpL[2][i].copy_coeffs(hpL[2][0]);
lpR[2][i].copy_coeffs(lpL[2][0]);
hpR[2][i].copy_coeffs(hpL[2][0]);
}
freq_old[2] = *params[param_freq2];
sep_old[2] = *params[param_sep2];
q_old[2] = *params[param_q2];
}
// set the params of all strips
float rel;
rel = *params[param_release] * pow(0.25, *params[param_release0] * -1);
rel = (*params[param_minrel] > 0.5) ? std::max(2500 * (1.f / 30), rel) : rel;
weight[0] = pow(0.25, *params[param_weight0] * -1);
strip[0].set_params(*params[param_limit], *params[param_attack], rel, weight[0], *params[param_asc], true);
*params[param_effrelease0] = rel;
rel = *params[param_release] * pow(0.25, *params[param_release1] * -1);
rel = (*params[param_minrel] > 0.5) ? std::max(2500 * (1.f / *params[param_freq0]), rel) : rel;
weight[1] = pow(0.25, *params[param_weight1] * -1);
strip[1].set_params(*params[param_limit], *params[param_attack], rel, weight[1], *params[param_asc]);
*params[param_effrelease1] = rel;
rel = *params[param_release] * pow(0.25, *params[param_release2] * -1);
rel = (*params[param_minrel] > 0.5) ? std::max(2500 * (1.f / *params[param_freq1]), rel) : rel;
weight[2] = pow(0.25, *params[param_weight2] * -1);
strip[2].set_params(*params[param_limit], *params[param_attack], rel, weight[2], *params[param_asc]);
*params[param_effrelease2] = rel;
rel = *params[param_release] * pow(0.25, *params[param_release3] * -1);
rel = (*params[param_minrel] > 0.5) ? std::max(2500 * (1.f / *params[param_freq2]), rel) : rel;
weight[3] = pow(0.25, *params[param_weight3] * -1);
strip[3].set_params(*params[param_limit], *params[param_attack], rel, weight[3], *params[param_asc]);
*params[param_effrelease3] = rel;
broadband.set_params(*params[param_limit], *params[param_attack], rel, 1.f, *params[param_asc]);
// rebuild multiband buffer
if( *params[param_attack] != __attack) {
int bs = (int)(srate * (*params[param_attack] / 1000.f) * channels);
buffer_size = bs - bs % channels; // buffer size attack rate
__attack = *params[param_attack];
_sanitize = true;
pos = 0;
}
}
void multibandlimiter_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
// rebuild buffer
overall_buffer_size = (int)(srate * (100.f / 1000.f) * channels) + channels; // buffer size max attack rate
buffer = (float*) calloc(overall_buffer_size, sizeof(float));
memset(buffer, 0, overall_buffer_size * sizeof(float)); // reset buffer to zero
pos = 0;
// set srate of all strips
for (int j = 0; j < strips; j ++) {
strip[j].set_sample_rate(srate);
}
broadband.set_sample_rate(srate);
}
#define BYPASSED_COMPRESSION(index) \
if(params[param_att##index] != NULL) \
*params[param_att##index] = 1.0; \
#define ACTIVE_COMPRESSION(index) \
if(params[param_att##index] != NULL) \
*params[param_att##index] = strip[index].get_attenuation(); \
uint32_t multibandlimiter_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
{
bool bypass = *params[param_bypass] > 0.5f;
numsamples += offset;
float batt = 0.f;
if(bypass) {
// everything bypassed
while(offset < numsamples) {
outs[0][offset] = ins[0][offset];
outs[1][offset] = ins[1][offset];
++offset;
}
// displays, too
clip_inL = 0.f;
clip_inR = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
asc_led = 0.f;
} else {
// process all strips
// let meters fall a bit
clip_inL -= std::min(clip_inL, numsamples);
clip_inR -= std::min(clip_inR, numsamples);
clip_outL -= std::min(clip_outL, numsamples);
clip_outR -= std::min(clip_outR, numsamples);
asc_led -= std::min(asc_led, numsamples);
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
float _tmpL[channels];
float _tmpR[channels];
while(offset < numsamples) {
float inL = 0.f;
float inR = 0.f;
// cycle through samples
if(!_sanitize) {
inL = ins[0][offset];
inR = ins[1][offset];
}
// in level
inR *= *params[param_level_in];
inL *= *params[param_level_in];
// even out filters gain reduction
// 3dB - levelled manually (based on default sep and q settings)
switch(mode) {
case 0:
inL *= 1.414213562;
inR *= 1.414213562;
break;
case 1:
inL *= 0.88;
inR *= 0.88;
break;
}
// out vars
float outL = 0.f;
float outR = 0.f;
int j1;
float left;
float right;
float sum_left = 0.f;
float sum_right = 0.f;
bool asc_active = false;
for (int i = 0; i < strips; i++) {
left = inL;
right = inR;
// send trough filters
switch(mode) {
// how many filter passes? (12/36dB)
case 0:
default:
j1 = 0;
break;
case 1:
j1 = 2;
break;
}
for (int j = 0; j <= j1; j++){
if(i + 1 < strips) {
// do lowpass on all bands except most high
left = lpL[i][j].process(left);
right = lpR[i][j].process(right);
lpL[i][j].sanitize();
lpR[i][j].sanitize();
}
if(i - 1 >= 0) {
// do highpass on all bands except most low
left = hpL[i - 1][j].process(left);
right = hpR[i - 1][j].process(right);
hpL[i - 1][j].sanitize();
hpR[i - 1][j].sanitize();
}
}
// remember filtered values for limiting
// (we need multiband_coeff before we can call the limiter bands)
_tmpL[i] = left;
_tmpR[i] = right;
// sum up for multiband coefficient
sum_left += ((fabs(left) > *params[param_limit]) ? *params[param_limit] * (fabs(left) / left) : left) * weight[i];
sum_right += ((fabs(right) > *params[param_limit]) ? *params[param_limit] * (fabs(right) / right) : right) * weight[i];
} // process single strip with filter
// write multiband coefficient to buffer
buffer[pos] = std::min(*params[param_limit] / std::max(fabs(sum_left), fabs(sum_right)), 1.0);
for (int i = 0; i < strips; i++) {
// process gain reduction
strip[i].process(_tmpL[i], _tmpR[i], buffer);
// sum up output of limiters
if (solo[i] || no_solo) {
outL += _tmpL[i];
outR += _tmpR[i];
}
asc_active = asc_active || strip[i].get_arc();
} // process single strip again for limiter
float fickdich[0];
broadband.process(outL, outR, fickdich);
batt = broadband.get_attenuation();
// autolevel
outL /= *params[param_limit];
outR /= *params[param_limit];
// out level
outL *= *params[param_level_out];
outR *= *params[param_level_out];
// send to output
outs[0][offset] = outL;
outs[1][offset] = outR;
// clip LED's
if(ins[0][offset] * *params[param_level_in] > 1.f) {
clip_inL = srate >> 3;
}
if(ins[1][offset] * *params[param_level_in] > 1.f) {
clip_inR = srate >> 3;
}
if(outL > 1.f) {
clip_outL = srate >> 3;
}
if(outR > 1.f) {
clip_outR = srate >> 3;
}
// set up in / out meters
if(ins[0][offset] * *params[param_level_in] > meter_inL) {
meter_inL = ins[0][offset] * *params[param_level_in];
}
if(ins[1][offset] * *params[param_level_in] > meter_inR) {
meter_inR = ins[1][offset] * *params[param_level_in];
}
if(outL > meter_outL) {
meter_outL = outL;
}
if(outR > meter_outR) {
meter_outR = outR;
}
if(asc_active) {
asc_led = srate >> 3;
}
// next sample
++offset;
pos = (pos + channels) % buffer_size;
if(pos == 0) _sanitize = false;
} // cycle trough samples
} // process all strips (no bypass)
// draw meters
SET_IF_CONNECTED(clip_inL);
SET_IF_CONNECTED(clip_inR);
SET_IF_CONNECTED(clip_outL);
SET_IF_CONNECTED(clip_outR);
SET_IF_CONNECTED(meter_inL);
SET_IF_CONNECTED(meter_inR);
SET_IF_CONNECTED(meter_outL);
SET_IF_CONNECTED(meter_outR);
if (params[param_asc_led] != NULL) *params[param_asc_led] = asc_led;
// draw strip meters
if(bypass > 0.5f) {
if(params[param_att0] != NULL) *params[param_att0] = 1.0;
if(params[param_att1] != NULL) *params[param_att1] = 1.0;
if(params[param_att2] != NULL) *params[param_att2] = 1.0;
if(params[param_att3] != NULL) *params[param_att3] = 1.0;
} else {
if(params[param_att0] != NULL) *params[param_att0] = strip[0].get_attenuation() * batt;
if(params[param_att1] != NULL) *params[param_att1] = strip[1].get_attenuation() * batt;
if(params[param_att2] != NULL) *params[param_att2] = strip[2].get_attenuation() * batt;
if(params[param_att3] != NULL) *params[param_att3] = strip[3].get_attenuation() * batt;
}
// whatever has to be returned x)
return outputs_mask;
}
bool multibandlimiter_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
{
if (!is_active or subindex > 3)
return false;
float ret;
double freq;
int j1;
for (int i = 0; i < points; i++)
{
ret = 1.f;
freq = 20.0 * pow (20000.0 / 20.0, i * 1.0 / points);
switch(mode) {
case 0:
default:
j1 = 0;
break;
case 1:
j1 = 2;
break;
}
for(int j = 0; j <= j1; j ++) {
switch(subindex) {
case 0:
ret *= lpL[0][j].freq_gain(freq, (float)srate);
break;
case 1:
ret *= hpL[0][j].freq_gain(freq, (float)srate);
ret *= lpL[1][j].freq_gain(freq, (float)srate);
break;
case 2:
ret *= hpL[1][j].freq_gain(freq, (float)srate);
ret *= lpL[2][j].freq_gain(freq, (float)srate);
break;
case 3:
ret *= hpL[2][j].freq_gain(freq, (float)srate);
break;
}
}
data[i] = dB_grid(ret);
}
if (*params[param_bypass] > 0.5f)
context->set_source_rgba(0.35, 0.4, 0.2, 0.3);
else {
context->set_source_rgba(0.35, 0.4, 0.2, 1);
context->set_line_width(1.5);
}
return true;
}
bool multibandlimiter_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
{
if (!is_active) {
return false;
} else {
vertical = (subindex & 1) != 0;
return get_freq_gridline(subindex, pos, vertical, legend, context);
}
}

View File

@@ -51,6 +51,12 @@ void monosynth_audio_module::activate() {
last_pwshift1 = last_pwshift2 = 0;
last_stretch1 = 65536;
queue_note_on_and_off = false;
prev_wave1 = -1;
prev_wave2 = -1;
wave1 = -1;
wave2 = -1;
queue_note_on = -1;
last_filter_type = -1;
}
waveform_family<MONOSYNTH_WAVE_BITS> *monosynth_audio_module::waves;
@@ -352,8 +358,6 @@ void monosynth_audio_module::delayed_note_on()
velocity = queue_vel;
ampctl = 1.0 + (queue_vel - 1.0) * *params[par_vel2amp];
fltctl = 1.0 + (queue_vel - 1.0) * *params[par_vel2filter];
set_frequency();
lookup_waveforms();
bool starting = false;
if (!running)
@@ -408,6 +412,8 @@ void monosynth_audio_module::delayed_note_on()
queue_note_on = -1;
float modsrc[modsrc_count] = { 1, velocity, inertia_pressure.get_last(), modwheel_value, envelope1.value, envelope2.value, 0.5+0.5*lfo1.last, 0.5+0.5*lfo2.last};
calculate_modmatrix(moddest, moddest_count, modsrc);
set_frequency();
lookup_waveforms();
if (queue_note_on_and_off)
{
@@ -467,7 +473,6 @@ void monosynth_audio_module::calculate_step()
if (fabs(*params[par_lfopitch]) > small_value<float>())
lfo_bend = pow(2.0f, *params[par_lfopitch] * lfov1 * (1.f / 1200.0f));
inertia_pitchbend.step();
set_frequency();
envelope1.advance();
envelope2.advance();
float env1 = envelope1.value, env2 = envelope2.value;
@@ -478,6 +483,7 @@ void monosynth_audio_module::calculate_step()
float modsrc[modsrc_count] = { 1, velocity, inertia_pressure.get(), modwheel_value, env1, env2, 0.5+0.5*lfov1, 0.5+0.5*lfov2};
calculate_modmatrix(moddest, moddest_count, modsrc);
set_frequency();
inertia_cutoff.set_inertia(*params[par_cutoff]);
cutoff = inertia_cutoff.get() * pow(2.0f, (lfov1 * *params[par_lfofilter] + env1 * fltctl * *params[par_env1tocutoff] + env2 * fltctl * *params[par_env2tocutoff] + moddest[moddest_cutoff]) * (1.f / 1200.f));
if (*params[par_keyfollow] > 0.01f)

View File

@@ -19,461 +19,18 @@
* Boston, MA 02110-1301 USA
*/
#include <config.h>
#include <calf/ladspa_wrap.h>
#include <calf/lv2wrap.h>
#include <calf/modules.h>
#include <calf/modules_comp.h>
#include <calf/modules_limit.h>
#include <calf/modules_dev.h>
#include <calf/modules_dist.h>
#include <calf/modules_eq.h>
#include <calf/modules_mod.h>
#include <calf/modules_synths.h>
#include <calf/organ.h>
#include <calf/osctlnet.h>
using namespace calf_plugins;
using namespace osctl;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if USE_LADSPA
ladspa_instance::ladspa_instance(audio_module_iface *_module, ladspa_plugin_metadata_set *_ladspa, int sample_rate)
{
module = _module;
metadata = module->get_metadata_iface();
ladspa = _ladspa;
module->get_port_arrays(ins, outs, params);
activate_flag = true;
#if USE_DSSI
feedback_sender = NULL;
#endif
module->set_sample_rate(sample_rate);
module->post_instantiate();
}
float ladspa_instance::get_param_value(int param_no)
{
// XXXKF hack
if (param_no >= ladspa->param_count)
return 0;
return *params[param_no];
}
void ladspa_instance::set_param_value(int param_no, float value)
{
// XXXKF hack
if (param_no >= ladspa->param_count)
return;
*params[param_no] = value;
}
bool ladspa_instance::activate_preset(int bank, int program)
{
return false;
}
/// LADSPA run function - does set sample rate / activate logic when it's run first time after activation
void ladspa_instance::run(unsigned long SampleCount)
{
if (activate_flag)
{
module->activate();
activate_flag = false;
}
module->params_changed();
module->process_slice(0, SampleCount);
}
#if USE_DSSI
void ladspa_instance::run_synth(unsigned long SampleCount, snd_seq_event_t *Events, unsigned long EventCount)
{
if (activate_flag)
{
module->activate();
activate_flag = false;
}
module->params_changed();
uint32_t offset = 0;
for (uint32_t e = 0; e < EventCount; e++)
{
uint32_t timestamp = Events[e].time.tick;
if (timestamp != offset)
module->process_slice(offset, timestamp);
process_dssi_event(Events[e]);
offset = timestamp;
}
if (offset != SampleCount)
module->process_slice(offset, SampleCount);
}
#endif
char *ladspa_instance::configure(const char *key, const char *value)
{
#if USE_DSSI_GUI
if (!strcmp(key, "OSC:FEEDBACK_URI"))
{
const line_graph_iface *lgi = dynamic_cast<const line_graph_iface *>(metadata);
//if (!lgi)
// return NULL;
if (*value)
{
if (feedback_sender) {
delete feedback_sender;
feedback_sender = NULL;
}
feedback_sender = new dssi_feedback_sender(value, lgi);
feedback_sender->add_graphs(metadata->get_param_props(0), metadata->get_param_count());
}
else
{
if (feedback_sender) {
delete feedback_sender;
feedback_sender = NULL;
}
}
return NULL;
}
else
if (!strcmp(key, "OSC:UPDATE"))
{
if (feedback_sender)
feedback_sender->update();
return NULL;
}
else
if (!strcmp(key, "OSC:SEND_STATUS"))
{
if (!feedback_sender)
return NULL;
struct status_gatherer: public send_updates_iface
{
osc_inline_typed_strstream str;
void send_status(const char *key, const char *value)
{
str << key << value;
}
} sg;
int serial = atoi(value);
serial = module->send_status_updates(&sg, serial);
sg.str << (uint32_t)serial;
feedback_sender->client->send("/status_data", sg.str);
return NULL;
}
else
#endif
if (!strcmp(key, "ExecCommand"))
{
if (*value)
{
execute(atoi(value));
}
return NULL;
}
return module->configure(key, value);
}
template<class Module>
ladspa_plugin_metadata_set ladspa_wrapper<Module>::output;
#if USE_DSSI
/// Utility function: handle MIDI event (only handles a subset in this version)
void ladspa_instance::process_dssi_event(snd_seq_event_t &event)
{
switch(event.type) {
case SND_SEQ_EVENT_NOTEON:
module->note_on(event.data.note.channel, event.data.note.note, event.data.note.velocity);
break;
case SND_SEQ_EVENT_NOTEOFF:
module->note_off(event.data.note.channel, event.data.note.note, event.data.note.velocity);
break;
case SND_SEQ_EVENT_PGMCHANGE:
module->program_change(event.data.control.channel, event.data.control.value);
break;
case SND_SEQ_EVENT_CONTROLLER:
module->control_change(event.data.control.channel, event.data.control.param, event.data.control.value);
break;
case SND_SEQ_EVENT_PITCHBEND:
module->pitch_bend(event.data.control.channel, event.data.control.value);
break;
case SND_SEQ_EVENT_CHANPRESS:
module->channel_pressure(event.data.control.channel, event.data.control.value);
break;
}
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// LADSPA callbacks
/// LADSPA activate function (note that at this moment the ports are not set)
static void cb_activate(LADSPA_Handle Instance)
{
((ladspa_instance *)(Instance))->activate_flag = true;
}
/// LADSPA run function - does set sample rate / activate logic when it's run first time after activation
static void cb_run(LADSPA_Handle Instance, unsigned long SampleCount) {
((ladspa_instance *)(Instance))->run(SampleCount);
}
/// LADSPA port connection function
static void cb_connect(LADSPA_Handle Instance, unsigned long port, LADSPA_Data *DataLocation)
{
ladspa_instance *const mod = (ladspa_instance *)Instance;
int first_out = mod->ladspa->input_count;
int first_param = first_out + mod->ladspa->output_count;
int ladspa_port_count = first_param + mod->ladspa->param_count;
if ((int)port < first_out)
mod->ins[port] = DataLocation;
else if ((int)port < first_param)
mod->outs[port - first_out] = DataLocation;
else if ((int)port < ladspa_port_count) {
int i = port - first_param;
mod->params[i] = DataLocation;
*mod->params[i] = mod->metadata->get_param_props(i)->def_value;
}
}
/// LADSPA deactivate function
static void cb_deactivate(LADSPA_Handle Instance) {
((ladspa_instance *)(Instance))->module->deactivate();
}
/// LADSPA cleanup (delete instance) function
static void cb_cleanup(LADSPA_Handle Instance) {
delete ((ladspa_instance *)(Instance));
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DSSI callbacks
#if USE_DSSI
/// DSSI "run synth" function, same as run() except it allows for event delivery
static void cb_run_synth(LADSPA_Handle Instance, unsigned long SampleCount,
snd_seq_event_t *Events, unsigned long EventCount) {
((ladspa_instance *)(Instance))->run_synth(SampleCount, Events, EventCount);
}
/// DSSI configure function (named properties)
static char *cb_configure(LADSPA_Handle Instance,
const char *Key,
const char *Value)
{
return ((ladspa_instance *)(Instance))->configure(Key, Value);
}
/// DSSI get program descriptor function; for 0, it returns the default program (from parameter properties table), for others, it uses global or user preset
static const DSSI_Program_Descriptor *cb_get_program(LADSPA_Handle Instance, unsigned long index)
{
ladspa_plugin_metadata_set *ladspa = ((ladspa_instance *)(Instance))->ladspa;
if (index > ladspa->presets->size())
return NULL;
if (index)
return &(*ladspa->preset_descs)[index - 1];
return &ladspa->dssi_default_program;
}
/// DSSI select program function; for 0, it sets the defaults, for others, it sets global or user preset
static void cb_select_program(LADSPA_Handle Instance, unsigned long Bank, unsigned long Program)
{
ladspa_instance *mod = (ladspa_instance *)Instance;
ladspa_plugin_metadata_set *ladspa = mod->ladspa;
unsigned int no = (Bank << 7) + Program - 1;
// printf("no = %d presets = %p:%d\n", no, presets, presets->size());
if (no == -1U) {
int rpc = ladspa->param_count;
for (int i =0 ; i < rpc; i++)
*mod->params[i] = mod->metadata->get_param_props(i)->def_value;
return;
}
if (no >= ladspa->presets->size())
return;
plugin_preset &p = (*ladspa->presets)[no];
// printf("activating preset %s\n", p.name.c_str());
p.activate(mod);
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
ladspa_plugin_metadata_set::ladspa_plugin_metadata_set()
{
metadata = NULL;
memset(&descriptor, 0, sizeof(descriptor));
#if USE_DSSI
presets = NULL;
preset_descs = NULL;
memset(&descriptor_for_dssi, 0, sizeof(descriptor_for_dssi));
memset(&dssi_descriptor, 0, sizeof(dssi_descriptor));
#endif
}
void ladspa_plugin_metadata_set::prepare(const plugin_metadata_iface *md, LADSPA_Handle (*cb_instantiate)(const struct _LADSPA_Descriptor * Descriptor, unsigned long sample_rate))
{
metadata = md;
input_count = md->get_input_count();
output_count = md->get_output_count();
param_count = md->get_param_count(); // XXXKF ladspa_instance<Module>::real_param_count();
const ladspa_plugin_info &plugin_info = md->get_plugin_info();
descriptor.UniqueID = plugin_info.unique_id;
descriptor.Label = plugin_info.label;
descriptor.Name = strdup((std::string(plugin_info.name) + " LADSPA").c_str());
descriptor.Maker = plugin_info.maker;
descriptor.Copyright = plugin_info.copyright;
descriptor.Properties = md->is_rt_capable() ? LADSPA_PROPERTY_HARD_RT_CAPABLE : 0;
descriptor.PortCount = input_count + output_count + param_count;
descriptor.PortNames = new char *[descriptor.PortCount];
descriptor.PortDescriptors = new LADSPA_PortDescriptor[descriptor.PortCount];
descriptor.PortRangeHints = new LADSPA_PortRangeHint[descriptor.PortCount];
int i;
for (i = 0; i < input_count + output_count; i++)
{
LADSPA_PortRangeHint &prh = ((LADSPA_PortRangeHint *)descriptor.PortRangeHints)[i];
((int *)descriptor.PortDescriptors)[i] = i < input_count ? LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO
: LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
prh.HintDescriptor = 0;
((const char **)descriptor.PortNames)[i] = md->get_port_names()[i];
}
for (; i < input_count + output_count + param_count; i++)
{
LADSPA_PortRangeHint &prh = ((LADSPA_PortRangeHint *)descriptor.PortRangeHints)[i];
const parameter_properties &pp = *md->get_param_props(i - input_count - output_count);
((int *)descriptor.PortDescriptors)[i] =
LADSPA_PORT_CONTROL | (pp.flags & PF_PROP_OUTPUT ? LADSPA_PORT_OUTPUT : LADSPA_PORT_INPUT);
prh.HintDescriptor = LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW;
((const char **)descriptor.PortNames)[i] = pp.name;
prh.LowerBound = pp.min;
prh.UpperBound = pp.max;
switch(pp.flags & PF_TYPEMASK) {
case PF_BOOL:
prh.HintDescriptor |= LADSPA_HINT_TOGGLED;
prh.HintDescriptor &= ~(LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW);
break;
case PF_INT:
case PF_ENUM:
prh.HintDescriptor |= LADSPA_HINT_INTEGER;
break;
default: {
int defpt = (int)(100 * (pp.def_value - pp.min) / (pp.max - pp.min));
if ((pp.flags & PF_SCALEMASK) == PF_SCALE_LOG)
defpt = (int)(100 * log(pp.def_value / pp.min) / log(pp.max / pp.min));
if (defpt < 12)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_MINIMUM;
else if (defpt < 37)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_LOW;
else if (defpt < 63)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_MIDDLE;
else if (defpt < 88)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_HIGH;
else
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_MAXIMUM;
}
}
if (pp.def_value == 0 || pp.def_value == 1 || pp.def_value == 100 || pp.def_value == 440 ) {
prh.HintDescriptor &= ~LADSPA_HINT_DEFAULT_MASK;
if (pp.def_value == 1)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_1;
else if (pp.def_value == 100)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_100;
else if (pp.def_value == 440)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_440;
else
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_0;
}
switch(pp.flags & PF_SCALEMASK) {
case PF_SCALE_LOG:
prh.HintDescriptor |= LADSPA_HINT_LOGARITHMIC;
break;
}
}
descriptor.ImplementationData = this;
descriptor.instantiate = cb_instantiate;
descriptor.connect_port = cb_connect;
descriptor.activate = cb_activate;
descriptor.run = cb_run;
descriptor.run_adding = NULL;
descriptor.set_run_adding_gain = NULL;
descriptor.deactivate = cb_deactivate;
descriptor.cleanup = cb_cleanup;
prepare_dssi();
}
void ladspa_plugin_metadata_set::prepare_dssi()
{
#if USE_DSSI
const ladspa_plugin_info &plugin_info = metadata->get_plugin_info();
memcpy(&descriptor_for_dssi, &descriptor, sizeof(descriptor));
descriptor_for_dssi.Name = strdup((std::string(plugin_info.name) + " DSSI").c_str());
memset(&dssi_descriptor, 0, sizeof(dssi_descriptor));
dssi_descriptor.DSSI_API_Version = 1;
dssi_descriptor.LADSPA_Plugin = &descriptor_for_dssi;
dssi_descriptor.configure = cb_configure;
dssi_descriptor.get_program = cb_get_program;
dssi_descriptor.select_program = cb_select_program;
if (metadata->get_midi())
dssi_descriptor.run_synth = cb_run_synth;
presets = new std::vector<plugin_preset>;
preset_descs = new std::vector<DSSI_Program_Descriptor>;
preset_list plist_tmp, plist;
plist.load_defaults(true);
plist_tmp.load_defaults(false);
plist.presets.insert(plist.presets.end(), plist_tmp.presets.begin(), plist_tmp.presets.end());
// XXXKF this assumes that plugin name in preset is case-insensitive equal to plugin label
// if I forget about this, I'll be in a deep trouble
dssi_default_program.Bank = 0;
dssi_default_program.Program = 0;
dssi_default_program.Name = "default";
int pos = 1;
for (unsigned int i = 0; i < plist.presets.size(); i++)
{
plugin_preset &pp = plist.presets[i];
if (strcasecmp(pp.plugin.c_str(), descriptor.Label))
continue;
DSSI_Program_Descriptor pd;
pd.Bank = pos >> 7;
pd.Program = pos++;
pd.Name = pp.name.c_str();
preset_descs->push_back(pd);
presets->push_back(pp);
}
#endif
}
ladspa_plugin_metadata_set::~ladspa_plugin_metadata_set()
{
delete []descriptor.PortNames;
delete []descriptor.PortDescriptors;
delete []descriptor.PortRangeHints;
#if USE_DSSI
if (presets)
presets->clear();
if (preset_descs)
preset_descs->clear();
delete presets;
delete preset_descs;
#endif
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -481,7 +38,7 @@ ladspa_plugin_metadata_set::~ladspa_plugin_metadata_set()
// instantiate descriptor templates
template<class Module> LV2_Descriptor calf_plugins::lv2_wrapper<Module>::descriptor;
template<class Module> LV2_Calf_Descriptor calf_plugins::lv2_wrapper<Module>::calf_descriptor;
template<class Module> LV2_Persist calf_plugins::lv2_wrapper<Module>::persist;
template<class Module> LV2_State_Interface calf_plugins::lv2_wrapper<Module>::state_iface;
extern "C" {
@@ -496,33 +53,6 @@ const LV2_Descriptor *lv2_descriptor(uint32_t index)
#endif
#if USE_LADSPA
extern "C" {
const LADSPA_Descriptor *ladspa_descriptor(unsigned long Index)
{
#define PER_MODULE_ITEM(name, isSynth, jackname) if (!isSynth && !(Index--)) return &ladspa_wrapper<name##_audio_module>::get().descriptor;
#include <calf/modulelist.h>
return NULL;
}
};
#if USE_DSSI
extern "C" {
const DSSI_Descriptor *dssi_descriptor(unsigned long Index)
{
#define PER_MODULE_ITEM(name, isSynth, jackname) if (!(Index--)) return &calf_plugins::ladspa_wrapper<name##_audio_module>::get().dssi_descriptor;
#include <calf/modulelist.h>
return NULL;
}
};
#endif
#endif
#if USE_JACK
extern "C" {