13
0

add support for reverb & chorus in a-fluidsynth

This commit is contained in:
Robin Gareus 2016-08-24 05:33:18 +02:00
parent 8da58c3da0
commit aa349d3f8c
2 changed files with 221 additions and 41 deletions

View 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);

View 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" ;
@ -64,4 +65,110 @@
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 ; ] ;
] .