From 94e50601685b77906db84be9431452c4b35aac95 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 3 Jul 2016 03:21:44 +0200 Subject: [PATCH] bundle a-comp --- libs/plugins/a-comp.lv2/a-comp.c | 260 ++++++++++++++++++++++++ libs/plugins/a-comp.lv2/a-comp.ttl.in | 153 ++++++++++++++ libs/plugins/a-comp.lv2/manifest.ttl.in | 32 +++ libs/plugins/a-comp.lv2/presets.ttl.in | 118 +++++++++++ libs/plugins/a-comp.lv2/ui.cc | 119 +++++++++++ libs/plugins/a-comp.lv2/wscript | 60 ++++++ wscript | 1 + 7 files changed, 743 insertions(+) create mode 100644 libs/plugins/a-comp.lv2/a-comp.c create mode 100644 libs/plugins/a-comp.lv2/a-comp.ttl.in create mode 100644 libs/plugins/a-comp.lv2/manifest.ttl.in create mode 100644 libs/plugins/a-comp.lv2/presets.ttl.in create mode 100644 libs/plugins/a-comp.lv2/ui.cc create mode 100644 libs/plugins/a-comp.lv2/wscript diff --git a/libs/plugins/a-comp.lv2/a-comp.c b/libs/plugins/a-comp.lv2/a-comp.c new file mode 100644 index 0000000000..ac67c7f021 --- /dev/null +++ b/libs/plugins/a-comp.lv2/a-comp.c @@ -0,0 +1,260 @@ +/* a-comp + * Copyright (C) 2016 Damien Zammit + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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 General Public License for more details. + */ + +#include +#include + +#include "lv2/lv2plug.in/ns/lv2core/lv2.h" + +#define ACOMP_URI "urn:ardour:a-comp" + +typedef enum { + ACOMP_INPUT0 = 0, + ACOMP_INPUT1, + ACOMP_OUTPUT, + + ACOMP_ATTACK, + ACOMP_RELEASE, + ACOMP_KNEE, + ACOMP_RATIO, + ACOMP_THRESHOLD, + ACOMP_MAKEUP, + + ACOMP_GAINR, + ACOMP_OUTLEVEL, + ACOMP_SIDECHAIN, +} PortIndex; + + +typedef struct { + float* input0; + float* input1; + float* output; + + float* attack; + float* release; + float* knee; + float* ratio; + float* thresdb; + float* makeup; + + float* gainr; + float* outlevel; + float* sidechain; + + float srate; + float old_yl; + float old_y1; + float old_yg; +} AComp; + +static LV2_Handle +instantiate(const LV2_Descriptor* descriptor, + double rate, + const char* bundle_path, + const LV2_Feature* const* features) +{ + AComp* acomp = (AComp*)malloc(sizeof(AComp)); + acomp->srate = rate; + + acomp->old_yl=acomp->old_y1=acomp->old_yg=0.f; + + return (LV2_Handle)acomp; +} + +static void +connect_port(LV2_Handle instance, + uint32_t port, + void* data) +{ + AComp* acomp = (AComp*)instance; + + switch ((PortIndex)port) { + case ACOMP_ATTACK: + acomp->attack = (float*)data; + break; + case ACOMP_RELEASE: + acomp->release = (float*)data; + break; + case ACOMP_KNEE: + acomp->knee = (float*)data; + break; + case ACOMP_RATIO: + acomp->ratio = (float*)data; + break; + case ACOMP_THRESHOLD: + acomp->thresdb = (float*)data; + break; + case ACOMP_MAKEUP: + acomp->makeup = (float*)data; + break; + case ACOMP_GAINR: + acomp->gainr = (float*)data; + break; + case ACOMP_OUTLEVEL: + acomp->outlevel = (float*)data; + break; + case ACOMP_SIDECHAIN: + acomp->sidechain = (float*)data; + break; + case ACOMP_INPUT0: + acomp->input0 = (float*)data; + break; + case ACOMP_INPUT1: + acomp->input1 = (float*)data; + break; + case ACOMP_OUTPUT: + acomp->output = (float*)data; + break; + } +} + +// Force already-denormal float value to zero +static inline float +sanitize_denormal(float value) { + if (!isnormal(value)) { + value = 0.f; + } + return value; +} + +static inline float +from_dB(float gdb) { + return (exp(gdb/20.f*log(10.f))); +} + +static inline float +to_dB(float g) { + return (20.f*log10(g)); +} + +static void +activate(LV2_Handle instance) +{ + AComp* acomp = (AComp*)instance; + + *(acomp->gainr) = 0.0f; + *(acomp->outlevel) = -45.0f; + acomp->old_yl=acomp->old_y1=acomp->old_yg=0.f; +} + +static void +run(LV2_Handle instance, uint32_t n_samples) +{ + AComp* acomp = (AComp*)instance; + + const float* const input0 = acomp->input0; + const float* const input1 = acomp->input1; + float* const output = acomp->output; + + float srate = acomp->srate; + float width = (6.f * *(acomp->knee)) + 0.01; + float cdb=0.f; + float attack_coeff = exp(-1000.f/(*(acomp->attack) * srate)); + float release_coeff = exp(-1000.f/(*(acomp->release) * srate)); + + float max = 0.f; + float lgaininp = 0.f; + float Lgain = 1.f; + float Lxg, Lxl, Lyg, Lyl, Ly1; + int usesidechain = (*(acomp->sidechain) < 0.5) ? 0 : 1; + uint32_t i; + float ingain; + float in0; + float in1; + float ratio = *(acomp->ratio); + float thresdb = *(acomp->thresdb); + + for (i = 0; i < n_samples; i++) { + in0 = input0[i]; + in1 = input1[i]; + ingain = usesidechain ? in1 : in0; + Lyg = 0.f; + Lxg = (ingain==0.f) ? -160.f : to_dB(fabs(ingain)); + Lxg = sanitize_denormal(Lxg); + + Lyg = Lxg + (1.f/ratio-1.f)*(Lxg-thresdb+width/2.f)*(Lxg-thresdb+width/2.f)/(2.f*width); + + if (2.f*(Lxg-thresdb) < -width) { + Lyg = Lxg; + } else { + Lyg = thresdb + (Lxg-thresdb)/ratio; + Lyg = sanitize_denormal(Lyg); + } + + Lxl = Lxg - Lyg; + + acomp->old_y1 = sanitize_denormal(acomp->old_y1); + acomp->old_yl = sanitize_denormal(acomp->old_yl); + Ly1 = fmaxf(Lxl, release_coeff * acomp->old_y1+(1.f-release_coeff)*Lxl); + Lyl = attack_coeff * acomp->old_yl+(1.f-attack_coeff)*Ly1; + Ly1 = sanitize_denormal(Ly1); + Lyl = sanitize_denormal(Lyl); + + cdb = -Lyl; + Lgain = from_dB(cdb); + + *(acomp->gainr) = Lyl; + + lgaininp = in0 * Lgain; + output[i] = lgaininp * from_dB(*(acomp->makeup)); + + max = (fabsf(output[i]) > max) ? fabsf(output[i]) : sanitize_denormal(max); + + acomp->old_yl = Lyl; + acomp->old_y1 = Ly1; + acomp->old_yg = Lyg; + } + *(acomp->outlevel) = (max == 0.f) ? -45.f : to_dB(max); +} + +static void +deactivate(LV2_Handle instance) +{ +} + +static void +cleanup(LV2_Handle instance) +{ + free(instance); +} + +static const void* +extension_data(const char* uri) +{ + return NULL; +} + +static const LV2_Descriptor descriptor = { + ACOMP_URI, + instantiate, + connect_port, + activate, + run, + deactivate, + cleanup, + extension_data +}; + +LV2_SYMBOL_EXPORT +const LV2_Descriptor* +lv2_descriptor(uint32_t index) +{ + switch (index) { + case 0: + return &descriptor; + default: + return NULL; + } +} diff --git a/libs/plugins/a-comp.lv2/a-comp.ttl.in b/libs/plugins/a-comp.lv2/a-comp.ttl.in new file mode 100644 index 0000000000..e64d3365e2 --- /dev/null +++ b/libs/plugins/a-comp.lv2/a-comp.ttl.in @@ -0,0 +1,153 @@ +@prefix doap: . +@prefix foaf: . +@prefix lv2: . +@prefix mod: . +@prefix rdfs: . +@prefix rsz: . +@prefix ui: . +@prefix unit: . + + + a lv2:Plugin , doap:Project; + + lv2:optionalFeature , + ; + + lv2:requiredFeature , + ; + + lv2:port [ + a lv2:InputPort, lv2:AudioPort ; + lv2:index 0 ; + lv2:symbol "lv2_audio_in_1" ; + lv2:name "Audio Input 1" ; + ] , + [ + a lv2:InputPort, lv2:AudioPort ; + lv2:index 1 ; + lv2:symbol "lv2_sidechain_in" ; + lv2:name "Sidechain Input" ; + lv2:portProperty lv2:isSideChain; + ] ; + + lv2:port [ + a lv2:OutputPort, lv2:AudioPort ; + lv2:index 2 ; + lv2:symbol "lv2_audio_out_1" ; + lv2:name "Audio Output 1" ; + ] ; + + lv2:port [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 3 ; + lv2:name "Attack" ; + lv2:symbol "att" ; + lv2:default 10.000000 ; + lv2:minimum 0.100000 ; + lv2:maximum 100.000000 ; + unit:unit unit:ms ; + ] , + [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 4 ; + lv2:name "Release" ; + lv2:symbol "rel" ; + lv2:default 80.000000 ; + lv2:minimum 1.000000 ; + lv2:maximum 500.000000 ; + unit:unit unit:ms ; + ] , + [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 5 ; + lv2:name "Knee" ; + lv2:symbol "kn" ; + lv2:default 0.000000 ; + lv2:minimum 0.000000 ; + lv2:maximum 8.000000 ; + unit:unit unit:db ; + ] , + [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 6 ; + lv2:name "Ratio" ; + lv2:symbol "rat" ; + lv2:default 4.000000 ; + lv2:minimum 1.000000 ; + lv2:maximum 20.000000 ; + unit:unit [ + rdfs:label " " ; + unit:symbol " " ; + unit:render "%f " ; + ] ; + ] , + [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 7 ; + lv2:name "Threshold" ; + lv2:symbol "thr" ; + lv2:default 0.000000 ; + lv2:minimum -80.000000 ; + lv2:maximum 0.000000 ; + unit:unit unit:db ; + ] , + [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 8 ; + lv2:name "Makeup" ; + lv2:symbol "mak" ; + lv2:default 0.000000 ; + lv2:minimum 0.000000 ; + lv2:maximum 30.000000 ; + unit:unit unit:db ; + ] , + [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:index 9 ; + lv2:name "Gain Reduction" ; + lv2:symbol "gr" ; + lv2:default 0.000000 ; + lv2:minimum 0.000000 ; + lv2:maximum 20.000000 ; + unit:unit unit:db ; + ] , + [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:index 10 ; + lv2:name "Output Level" ; + lv2:symbol "outlevel" ; + lv2:default -45.000000 ; + lv2:minimum -45.000000 ; + lv2:maximum 20.000000 ; + unit:unit unit:db ; + ] , + [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 11 ; + lv2:name "Sidechain" ; + lv2:symbol "sidech" ; + lv2:default 0.000000 ; + lv2:minimum 0.000000 ; + lv2:maximum 1.000000 ; + lv2:portProperty lv2:toggled ; + ] ; + + rdfs:comment """ +A powerful mono compressor. +""" ; + + mod:brand "Ardour" ; + mod:label "a-comp" ; + + doap:name "a-comp" ; + doap:license "GPL v2+" ; + +# ui:ui ; + + doap:maintainer [ + foaf:name "Damien Zammit" ; + foaf:homepage ; + ] ; + + lv2:microVersion 0 ; + lv2:minorVersion 1 . diff --git a/libs/plugins/a-comp.lv2/manifest.ttl.in b/libs/plugins/a-comp.lv2/manifest.ttl.in new file mode 100644 index 0000000000..73cacd4dbf --- /dev/null +++ b/libs/plugins/a-comp.lv2/manifest.ttl.in @@ -0,0 +1,32 @@ +@prefix lv2: . +@prefix rdfs: . +@prefix pset: . +@prefix ui: . + + + a lv2:Plugin ; + lv2:binary ; + rdfs:seeAlso . + +# +# a ui:GtkUI ; +# ui:binary ; +# rdfs:seeAlso . + + + a pset:Preset ; + lv2:appliesTo ; + rdfs:label "Zero" ; + rdfs:seeAlso . + + + a pset:Preset ; + lv2:appliesTo ; + rdfs:label "PoppySnare" ; + rdfs:seeAlso . + + + a pset:Preset ; + lv2:appliesTo ; + rdfs:label "VocalLeveller" ; + rdfs:seeAlso . diff --git a/libs/plugins/a-comp.lv2/presets.ttl.in b/libs/plugins/a-comp.lv2/presets.ttl.in new file mode 100644 index 0000000000..9d9b10bd4d --- /dev/null +++ b/libs/plugins/a-comp.lv2/presets.ttl.in @@ -0,0 +1,118 @@ +@prefix lv2: . +@prefix pset: . + + + lv2:port [ + lv2:symbol "att" ; + pset:value 10.000000 ; + ] , + [ + lv2:symbol "rel" ; + pset:value 80.000000 ; + ] , + [ + lv2:symbol "kn" ; + pset:value 0.000000 ; + ] , + [ + lv2:symbol "rat" ; + pset:value 4.000000 ; + ] , + [ + lv2:symbol "thr" ; + pset:value 0.000000 ; + ] , + [ + lv2:symbol "mak" ; + pset:value 0.000000 ; + ] , + [ + lv2:symbol "gr" ; + pset:value 0.000000 ; + ] , + [ + lv2:symbol "outlevel" ; + pset:value -45.000000 ; + ] , + [ + lv2:symbol "sidech" ; + pset:value 0.000000 ; + ] . + + + lv2:port [ + lv2:symbol "att" ; + pset:value 10.000000 ; + ] , + [ + lv2:symbol "rel" ; + pset:value 10.000000 ; + ] , + [ + lv2:symbol "kn" ; + pset:value 1.000000 ; + ] , + [ + lv2:symbol "rat" ; + pset:value 5.000000 ; + ] , + [ + lv2:symbol "thr" ; + pset:value -18.000000 ; + ] , + [ + lv2:symbol "mak" ; + pset:value 6.000000 ; + ] , + [ + lv2:symbol "gr" ; + pset:value 0.000000 ; + ] , + [ + lv2:symbol "outlevel" ; + pset:value -45.000000 ; + ] , + [ + lv2:symbol "sidech" ; + pset:value 0.000000 ; + ] . + + + lv2:port [ + lv2:symbol "att" ; + pset:value 50.000000 ; + ] , + [ + lv2:symbol "rel" ; + pset:value 400.000000 ; + ] , + [ + lv2:symbol "kn" ; + pset:value 8.000000 ; + ] , + [ + lv2:symbol "rat" ; + pset:value 5.000000 ; + ] , + [ + lv2:symbol "thr" ; + pset:value -16.000000 ; + ] , + [ + lv2:symbol "mak" ; + pset:value 9.000000 ; + ] , + [ + lv2:symbol "gr" ; + pset:value 0.000000 ; + ] , + [ + lv2:symbol "outlevel" ; + pset:value -45.000000 ; + ] , + [ + lv2:symbol "sidech" ; + pset:value 0.000000 ; + ] . + + diff --git a/libs/plugins/a-comp.lv2/ui.cc b/libs/plugins/a-comp.lv2/ui.cc new file mode 100644 index 0000000000..5d257aec2e --- /dev/null +++ b/libs/plugins/a-comp.lv2/ui.cc @@ -0,0 +1,119 @@ +/* a-comp UI -- test/example + * + * Copyright (C) 2016 Robin Gareus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define ACOMP_URI "urn:ardour:a-comp" + +#include + +#include + +#include "lv2/lv2plug.in/ns/extensions/ui/ui.h" + +using namespace Gtk; + +typedef struct { + LV2UI_Write_Function write; + LV2UI_Controller controller; + + Box* box; + Label* label; +} ACompUI; + + +/****************************************************************************** + * GUI + */ + +static void* setup_ui (ACompUI* ui) { + ui->box = manage (new HBox); + + ui->label = manage (new Label ("Hello World")); + ui->box->pack_start (*ui->label, false, false, 4); + + return ui->box->gobj (); +} + + +/****************************************************************************** + * LV2 callbacks + */ + +static LV2UI_Handle +instantiate (const LV2UI_Descriptor* descriptor, + const char* plugin_uri, + const char* bundle_path, + LV2UI_Write_Function write_function, + LV2UI_Controller controller, + LV2UI_Widget* widget, + const LV2_Feature* const* features) +{ + ACompUI* ui = (ACompUI*)calloc (1, sizeof (ACompUI)); + ui->write = write_function; + ui->controller = controller; + ui->box = NULL; + + *widget = setup_ui (ui); + return ui; +} + +static void +cleanup (LV2UI_Handle handle) +{ + ACompUI* ui = (ACompUI*)handle; + free (ui); +} + +static void +port_event (LV2UI_Handle handle, + uint32_t port_index, + uint32_t buffer_size, + uint32_t format, + const void* buffer) +{ + ACompUI* ui = (ACompUI*)handle; +} + +/****************************************************************************** + * LV2 setup + */ + +static const void* +extension_data (const char* uri) +{ + return NULL; +} + +static const LV2UI_Descriptor descriptor = { + ACOMP_URI "#ui", + instantiate, + cleanup, + port_event, + extension_data +}; + +LV2_SYMBOL_EXPORT +const LV2UI_Descriptor* +lv2ui_descriptor (uint32_t index) +{ + switch (index) { + case 0: + return &descriptor; + default: + return NULL; + } +} diff --git a/libs/plugins/a-comp.lv2/wscript b/libs/plugins/a-comp.lv2/wscript new file mode 100644 index 0000000000..7feeee73ee --- /dev/null +++ b/libs/plugins/a-comp.lv2/wscript @@ -0,0 +1,60 @@ +#!/usr/bin/env python +import os +import re +import shutil +import waflib.extras.autowaf as autowaf +import waflib.Options as Options, waflib.Utils as Utils + +# Mandatory variables +top = '.' +out = 'build' + +def options(opt): + autowaf.set_options(opt) + +def configure(conf): + conf.load('compiler_c') + autowaf.configure(conf) + if Options.options.lv2: + autowaf.check_pkg(conf, 'lv2', atleast_version='1.0.0', + uselib_store='LV2_1_0_0') + +def build(bld): + bundle = 'a-comp.lv2' + module_pat = re.sub('^lib', '', bld.env.cshlib_PATTERN) + module_ext = module_pat[module_pat.rfind('.'):] + + if bld.is_defined ('HAVE_LV2'): + # Build RDF files + for i in ['manifest.ttl', 'a-comp.ttl', 'presets.ttl']: + bld(features = 'subst', + source = i + '.in', + target = '../../LV2/%s/%s' % (bundle, i), + install_path = '${LV2DIR}/%s' % bundle, + chmod = Utils.O644, + LIB_EXT = module_ext) + + # Build plugin library + obj = bld(features = 'c cshlib', + source = 'a-comp.c', + name = 'a-comp', + cflags = [ '-fPIC' ], + target = '../../LV2/%s/a-comp' % bundle, + install_path = '${LV2DIR}/%s' % bundle, + use = 'LV2_1_0_0' + ) + obj.env.cshlib_PATTERN = module_pat + + # Build plugin GUI + obj = bld(features = 'cxx cxxshlib', + source = 'ui.cc', + name = 'a-comp-ui', + cxxflags = [ '-fPIC' ], + target = '../../LV2/%s/a-comp-ui' % bundle, + install_path = '${LV2DIR}/%s' % bundle, + uselib = 'GTKMM GTK', + use = 'LV2_1_0_0' + ) + obj.env.cxxshlib_PATTERN = module_pat + +# vi:set ts=4 sw=4 et: diff --git a/wscript b/wscript index f23a987b3c..066ad3b0d3 100644 --- a/wscript +++ b/wscript @@ -219,6 +219,7 @@ children = [ 'libs/audiographer', 'libs/canvas', 'libs/plugins/reasonablesynth.lv2', + 'libs/plugins/a-comp.lv2', 'gtk2_ardour', 'export', 'midi_maps',