add support for reverb & chorus in a-fluidsynth
authorRobin Gareus <robin@gareus.org>
Wed, 24 Aug 2016 03:33:18 +0000 (05:33 +0200)
committerRobin Gareus <robin@gareus.org>
Wed, 24 Aug 2016 03:33:18 +0000 (05:33 +0200)
libs/plugins/a-fluidsynth.lv2/a-fluidsynth.c
libs/plugins/a-fluidsynth.lv2/a-fluidsynth.ttl.in

index c6b743ed84d8c0e08b361a967f6cb7187387b89d..11015b5ba14e34dc6ed6cfbd18c0243adbc9fbef 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
+#include <math.h>
 
 #define AFS_URN "urn:ardour:a-fluidsynth"
 
@@ -49,7 +50,20 @@ enum {
        FS_PORT_CONTROL = 0,
        FS_PORT_NOTIFY,
        FS_PORT_OUT_L,
-       FS_PORT_OUT_R
+       FS_PORT_OUT_R,
+       FS_OUT_GAIN,
+       FS_REV_ENABLE,
+       FS_REV_ROOMSIZE,
+       FS_REV_DAMPING,
+       FS_REV_WIDTH,
+       FS_REV_LEVEL,
+       FS_CHR_ENABLE,
+       FS_CHR_N,
+       FS_CHR_SPEED,
+       FS_CHR_DEPTH,
+       FS_CHR_LEVEL,
+       FS_CHR_TYPE,
+       FS_PORT_LAST
 };
 
 enum {
@@ -61,7 +75,9 @@ typedef struct {
        /* ports */
        const LV2_Atom_Sequence* control;
   LV2_Atom_Sequence*       notify;
-       float*                   output[2];
+
+       float* p_ports[FS_PORT_LAST];
+       float  v_ports[FS_PORT_LAST];
 
        /* fluid synth */
        fluid_settings_t* settings;
@@ -104,6 +120,7 @@ typedef struct {
 /* *****************************************************************************
  * helpers
  */
+
 static bool
 load_sf2 (AFluidSynth* self, const char* fn)
 {
@@ -150,7 +167,7 @@ parse_patch_msg (AFluidSynth* self, const LV2_Atom_Object* obj)
                return NULL;
        }
 
-       lv2_atom_object_get(obj, self->patch_value, &file_path, 0);
+       lv2_atom_object_get (obj, self->patch_value, &file_path, 0);
        if (!file_path || file_path->type != self->atom_Path) {
                return NULL;
        }
@@ -158,7 +175,6 @@ parse_patch_msg (AFluidSynth* self, const LV2_Atom_Object* obj)
        return file_path;
 }
 
-
 static void
 inform_ui (AFluidSynth* self)
 {
@@ -168,15 +184,23 @@ inform_ui (AFluidSynth* self)
 
        LV2_Atom_Forge_Frame frame;
        lv2_atom_forge_frame_time (&self->forge, 0);
-       x_forge_object(&self->forge, &frame, 1, self->patch_Set);
+       x_forge_object (&self->forge, &frame, 1, self->patch_Set);
        lv2_atom_forge_property_head (&self->forge, self->patch_property, 0);
        lv2_atom_forge_urid (&self->forge, self->afs_sf2file);
        lv2_atom_forge_property_head (&self->forge, self->patch_value, 0);
-       lv2_atom_forge_path( &self->forge, self->current_sf2_file_path, strlen(self->current_sf2_file_path));
+       lv2_atom_forge_path (&self->forge, self->current_sf2_file_path, strlen (self->current_sf2_file_path));
 
        lv2_atom_forge_pop (&self->forge, &frame);
 }
 
+static float
+db_to_coeff (float db)
+{
+       if (db <= -80) { return 0; }
+       else if (db >=  20) { return 10; }
+       return powf (10.f, .05f * db);
+}
+
 /* *****************************************************************************
  * LV2 Plugin
  */
@@ -294,11 +318,10 @@ connect_port (LV2_Handle instance,
                case FS_PORT_NOTIFY:
                        self->notify = (LV2_Atom_Sequence*)data;
                        break;
-               case FS_PORT_OUT_L:
-                       self->output[0] = (float*)data;
-                       break;
-               case FS_PORT_OUT_R:
-                       self->output[1] = (float*)data;
+               default:
+                       if (port < FS_PORT_LAST) {
+                               self->p_ports[port] = (float*)data;
+                       }
                        break;
        }
 }
@@ -324,14 +347,64 @@ run (LV2_Handle instance, uint32_t n_samples)
        lv2_atom_forge_sequence_head (&self->forge, &self->frame, 0);
 
        if (!self->initialized || self->reinit_in_progress) {
-               memset (self->output[0], 0, n_samples * sizeof (float));
-               memset (self->output[1], 0, n_samples * sizeof (float));
+               memset (self->p_ports[FS_PORT_OUT_L], 0, n_samples * sizeof (float));
+               memset (self->p_ports[FS_PORT_OUT_R], 0, n_samples * sizeof (float));
        } else if (self->panic) {
                fluid_synth_all_notes_off (self->synth, -1);
                fluid_synth_all_sounds_off (self->synth, -1);
+               //fluid_synth_reset_reverb (self->synth);
+               //fluid_synth_reset_chorus (self->synth);
                self->panic = false;
        }
 
+       if (self->initialized && !self->reinit_in_progress) {
+               bool rev_change = false;
+               bool chr_change = false;
+               // TODO clamp values to ranges
+               if (self->v_ports[FS_OUT_GAIN] != *self->p_ports[FS_OUT_GAIN]) {
+                       fluid_synth_set_gain (self->synth, db_to_coeff (*self->p_ports[FS_OUT_GAIN]));
+               }
+               if (self->v_ports[FS_REV_ENABLE] != *self->p_ports[FS_REV_ENABLE]) {
+                       fluid_synth_set_reverb_on (self->synth, *self->p_ports[FS_REV_ENABLE] > 0 ? 1 : 0);
+                       rev_change = true;
+               }
+               if (self->v_ports[FS_CHR_ENABLE] != *self->p_ports[FS_CHR_ENABLE]) {
+                       fluid_synth_set_chorus_on (self->synth, *self->p_ports[FS_CHR_ENABLE] > 0 ? 1 : 0);
+                       chr_change = true;
+               }
+
+               for (uint32_t p = FS_REV_ROOMSIZE; p <= FS_REV_LEVEL && !rev_change; ++p) {
+                       if (self->v_ports[p] != *self->p_ports[p]) {
+                               rev_change = true;
+                       }
+               }
+               for (uint32_t p = FS_CHR_N; p <= FS_CHR_TYPE && !chr_change; ++p) {
+                       if (self->v_ports[p] != *self->p_ports[p]) {
+                               chr_change = true;
+                       }
+               }
+
+               if (rev_change) {
+                       fluid_synth_set_reverb (self->synth,
+                                       *self->p_ports[FS_REV_ROOMSIZE],
+                                       *self->p_ports[FS_REV_DAMPING],
+                                       *self->p_ports[FS_REV_WIDTH],
+                                       *self->p_ports[FS_REV_LEVEL]);
+               }
+
+               if (chr_change) {
+                       fluid_synth_set_chorus (self->synth,
+                                       rintf (*self->p_ports[FS_CHR_N]),
+                                       db_to_coeff (*self->p_ports[FS_CHR_LEVEL]),
+                                       *self->p_ports[FS_CHR_SPEED],
+                                       *self->p_ports[FS_CHR_DEPTH],
+                                       (*self->p_ports[FS_CHR_TYPE] > 0) ? FLUID_CHORUS_MOD_SINE : FLUID_CHORUS_MOD_TRIANGLE);
+               }
+               for (uint32_t p = FS_OUT_GAIN; p < FS_PORT_LAST; ++p) {
+                       self->v_ports[p] = *self->p_ports[p];
+               }
+       }
+
        uint32_t offset = 0;
 
        LV2_ATOM_SEQUENCE_FOREACH (self->control, ev) {
@@ -349,7 +422,7 @@ run (LV2_Handle instance, uint32_t n_samples)
                                        self->queue_sf2_file_path[1023] = '\0';
                                        self->reinit_in_progress = true;
                                        int magic = 0x4711;
-                                       self->schedule->schedule_work (self->schedule->handle, sizeof(int), &magic);
+                                       self->schedule->schedule_work (self->schedule->handle, sizeof (int), &magic);
                                }
                        }
                }
@@ -362,8 +435,8 @@ run (LV2_Handle instance, uint32_t n_samples)
                                fluid_synth_write_float (
                                                self->synth,
                                                ev->time.frames - offset,
-                                               &self->output[0][offset], 0, 1,
-                                               &self->output[1][offset], 0, 1);
+                                               &self->p_ports[FS_PORT_OUT_L][offset], 0, 1,
+                                               &self->p_ports[FS_PORT_OUT_R][offset], 0, 1);
                        }
 
                        offset = ev->time.frames;
@@ -384,7 +457,7 @@ run (LV2_Handle instance, uint32_t n_samples)
        if (self->queue_reinit && !self->reinit_in_progress) {
                self->reinit_in_progress = true;
                int magic = 0x4711;
-               self->schedule->schedule_work (self->schedule->handle, sizeof(int), &magic);
+               self->schedule->schedule_work (self->schedule->handle, sizeof (int), &magic);
        }
 
        /* inform the GUI */
@@ -397,8 +470,8 @@ run (LV2_Handle instance, uint32_t n_samples)
                fluid_synth_write_float (
                                self->synth,
                                n_samples - offset,
-                               &self->output[0][offset], 0, 1,
-                               &self->output[1][offset], 0, 1);
+                               &self->p_ports[FS_PORT_OUT_L][offset], 0, 1,
+                               &self->p_ports[FS_PORT_OUT_R][offset], 0, 1);
        }
 }
 
@@ -424,7 +497,7 @@ work (LV2_Handle                  instance,
 {
        AFluidSynth* self = (AFluidSynth*)instance;
 
-  if (size != sizeof(int)) {
+  if (size != sizeof (int)) {
                return LV2_WORKER_ERR_UNKNOWN;
        }
        int magic = *((const int*)data);
index 3363d18a6dda8b3f8cd6ddd4a5c1d8d2d0d0ac48..ca9b3ca39a44e2cbda134df62760b2277eab6210 100644 (file)
@@ -4,53 +4,54 @@
 @prefix lv2:   <http://lv2plug.in/ns/lv2core#> .
 @prefix midi:  <http://lv2plug.in/ns/ext/midi#> .
 @prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix pprop: <http://lv2plug.in/ns/ext/port-props#> .
 @prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
 @prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .
 @prefix state: <http://lv2plug.in/ns/ext/state#> .
-@prefix unit <http://lv2plug.in/ns/extensions/units#> .
+@prefix units: <http://lv2plug.in/ns/extensions/units#> .
 @prefix urid:  <http://lv2plug.in/ns/ext/urid#> .
 @prefix work:  <http://lv2plug.in/ns/ext/worker#> .
 
 <http://ardour.org/credits.html>
-       a foaf:Person ;
-       foaf:name "Ardour Team" ;
-       foaf:homepage <http://ardour.org/> .
+  a foaf:Person ;
+  foaf:name "Ardour Team" ;
+  foaf:homepage <http://ardour.org/> .
 
 <urn:ardour:a-fluidsynth:sf2file>
-       a lv2:Parameter ;
-       rdfs:label "SF2 File" ;
-       rdfs:range atom:Path .
+  a lv2:Parameter ;
+  rdfs:label "SF2 File" ;
+  rdfs:range atom:Path .
 
 <urn:ardour:a-fluidsynth>
-       a doap:Project, lv2:InstrumentPlugin, lv2:Plugin ;
+  a doap:Project, lv2:InstrumentPlugin, lv2:Plugin ;
 
-       doap:name "a-Fluid Synth" ;
-       rdfs:comment "SF2 Synthesizer using Fluidsynth" ;
+  doap:name "a-Fluid Synth" ;
+  rdfs:comment "SF2 Synthesizer using Fluidsynth" ;
 
-       doap:maintainer <http://ardour.org/credits.html> ;
-       doap:license <http://usefulinc.com/doap/licenses/gpl> ;
+  doap:maintainer <http://ardour.org/credits.html> ;
+  doap:license <http://usefulinc.com/doap/licenses/gpl> ;
 
-       lv2:microVersion 2 ; lv2:minorVersion 0 ;
+  lv2:microVersion 2 ; lv2:minorVersion 0 ;
 
-       lv2:requiredFeature urid:map, work:schedule ;
-       lv2:extensionData work:interface, state:interface ;
-       lv2:optionalFeature lv2:hardRTCapable;
+  lv2:requiredFeature urid:map, work:schedule ;
+  lv2:extensionData work:interface, state:interface ;
+  lv2:optionalFeature lv2:hardRTCapable;
 
-       patch:writable <urn:ardour:a-fluidsynth:sf2file> ;
+  patch:writable <urn:ardour:a-fluidsynth:sf2file> ;
 
     lv2:port [
         a lv2:InputPort, atom:AtomPort ;
         atom:bufferType atom:Sequence ;
-                               atom:supports patch:Message, midi:MidiEvent;
-                               lv2:designation lv2:control ;
+        atom:supports patch:Message, midi:MidiEvent;
+        lv2:designation lv2:control ;
         lv2:index 0 ;
         lv2:symbol "control" ;
         lv2:name "Midi In" ;
     ] , [
         a lv2:OutputPort, atom:AtomPort ;
         atom:bufferType atom:Sequence ;
-                               atom:supports patch:Message;
-                               lv2:designation lv2:control ;
+        atom:supports patch:Message;
+        lv2:designation lv2:control ;
         lv2:index 1 ;
         lv2:symbol "notify" ;
         lv2:name "UI Notifications" ;
         lv2:index 3 ;
         lv2:symbol "outR" ;
         lv2:name "Output Right" ;
+    ] , [
+        a lv2:InputPort, lv2:ControlPort ;
+        lv2:index 4 ;
+        lv2:symbol "level" ;
+        lv2:name "Output Level" ;
+        lv2:default 0.0 ;
+        lv2:minimum -80.0 ;
+        lv2:maximum 20.0;
+        units:unit units:db ;
+    ] , [
+        a lv2:InputPort, lv2:ControlPort ;
+        lv2:index 5 ;
+        lv2:symbol "rev_enable" ;
+        lv2:name "Reverb Enable" ;
+        lv2:default 0 ;
+        lv2:minimum 0 ;
+        lv2:maximum 1 ;
+        lv2:portProperty lv2:integer, lv2:toggled;
+      ] , [
+        a lv2:InputPort, lv2:ControlPort ;
+        lv2:index 6 ;
+        lv2:symbol "rev_roomsize" ;
+        lv2:name "Reverb Roomsize" ;
+        lv2:default 0.5 ;
+        lv2:minimum 0.0 ;
+        lv2:maximum 1.2;
+    ] , [
+        a lv2:InputPort, lv2:ControlPort ;
+        lv2:index 7 ;
+        lv2:symbol "rev_damp" ;
+        lv2:name "Reverb Damping" ;
+        lv2:default 0.5 ;
+        lv2:minimum 0.0 ;
+        lv2:maximum 1.0;
+    ] , [
+        a lv2:InputPort, lv2:ControlPort ;
+        lv2:index 8 ;
+        lv2:symbol "rev_width" ;
+        lv2:name "Reverb Width" ;
+        lv2:default 10.0 ;
+        lv2:minimum 0.0 ;
+        lv2:maximum 100.0;
+    ] , [
+        a lv2:InputPort, lv2:ControlPort ;
+        lv2:index 9 ;
+        lv2:symbol "rev_level" ;
+        lv2:name "Reverb Dry/Wet" ;
+        lv2:default 0.1 ;
+        lv2:minimum 0.0 ;
+        lv2:maximum 1.0;
+    ] , [
+        a lv2:InputPort, lv2:ControlPort ;
+        lv2:index 10 ;
+        lv2:symbol "chorus_enable" ;
+        lv2:name "Chorus Enable" ;
+        lv2:default 0 ;
+        lv2:minimum 0 ;
+        lv2:maximum 1 ;
+        lv2:portProperty lv2:integer, lv2:toggled;
+    ] , [
+        a lv2:InputPort, lv2:ControlPort ;
+        lv2:index 11 ;
+        lv2:symbol "chorus_nr" ;
+        lv2:name "Chorus Voice Count" ;
+        lv2:default 1 ;
+        lv2:minimum 0 ;
+        lv2:maximum 99;
+        lv2:portProperty lv2:integer;
+    ] , [
+        a lv2:InputPort, lv2:ControlPort ;
+        lv2:index 12 ;
+        lv2:symbol "chorus_speed" ;
+        lv2:name "Chorus Speed" ;
+        lv2:default 1.0 ;
+        lv2:minimum 0.29 ;
+        lv2:maximum 5.0;
+        units:unit units:hz ;
+        lv2:portProperty pprop:logarithmic;
+    ] , [
+        a lv2:InputPort, lv2:ControlPort ;
+        lv2:index 13 ;
+        lv2:symbol "chorus_depth" ;
+        lv2:name "Chorus Depth" ;
+        lv2:default 0.0 ;
+        lv2:minimum 0.0 ;
+        lv2:maximum 21.0;
+    ] , [
+        a lv2:InputPort, lv2:ControlPort ;
+        lv2:index 14 ;
+        lv2:symbol "chorus_level" ;
+        lv2:name "Chorus Level" ;
+        lv2:default 0.0 ;
+        lv2:minimum -80.0 ;
+        lv2:maximum 20.0;
+        units:unit units:db ;
+    ] , [
+        a lv2:InputPort, lv2:ControlPort ;
+        lv2:index 15 ;
+        lv2:symbol "chorus_type" ;
+        lv2:name "Chorus Type" ;
+        lv2:default 0.0 ;
+        lv2:minimum 0.0 ;
+        lv2:maximum 1.0;
+        lv2:portProperty lv2:integer, lv2:enumeration;
+        lv2:scalePoint [ rdfs:label  "Sine";  rdf:value 0.0 ; ] ;
+        lv2:scalePoint [ rdfs:label  "Triangle";  rdf:value 1.0 ; ] ;
     ] .