summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Gareus <robin@gareus.org>2016-08-24 03:18:14 +0200
committerRobin Gareus <robin@gareus.org>2016-08-24 03:18:14 +0200
commit870cb197373cb70b28233b3a30e90a70e737dba5 (patch)
tree5d00c9789910e20cc68f68788d56856bc35701cc
parent30afda553675a91bfffd94ee95038d6ba0c89bf8 (diff)
a-fluidsynth
-rw-r--r--libs/plugins/a-fluidsynth.lv2/a-fluidsynth.c560
-rw-r--r--libs/plugins/a-fluidsynth.lv2/a-fluidsynth.ttl.in67
-rw-r--r--libs/plugins/a-fluidsynth.lv2/manifest.ttl.in7
-rw-r--r--libs/plugins/a-fluidsynth.lv2/wscript57
-rw-r--r--wscript1
5 files changed, 692 insertions, 0 deletions
diff --git a/libs/plugins/a-fluidsynth.lv2/a-fluidsynth.c b/libs/plugins/a-fluidsynth.lv2/a-fluidsynth.c
new file mode 100644
index 0000000000..c6b743ed84
--- /dev/null
+++ b/libs/plugins/a-fluidsynth.lv2/a-fluidsynth.c
@@ -0,0 +1,560 @@
+/* a-fluidsynth -- simple & robust x-platform fluidsynth LV2
+ *
+ * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define AFS_URN "urn:ardour:a-fluidsynth"
+
+#ifdef HAVE_LV2_1_10_0
+#define x_forge_object lv2_atom_forge_object
+#else
+#define x_forge_object lv2_atom_forge_blank
+#endif
+
+#include "fluidsynth.h"
+
+#include <lv2/lv2plug.in/ns/lv2core/lv2.h>
+#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
+#include <lv2/lv2plug.in/ns/ext/atom/forge.h>
+#include <lv2/lv2plug.in/ns/ext/atom/util.h>
+#include <lv2/lv2plug.in/ns/ext/log/logger.h>
+#include <lv2/lv2plug.in/ns/ext/midi/midi.h>
+#include <lv2/lv2plug.in/ns/ext/patch/patch.h>
+#include <lv2/lv2plug.in/ns/ext/state/state.h>
+#include <lv2/lv2plug.in/ns/ext/urid/urid.h>
+#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
+
+enum {
+ FS_PORT_CONTROL = 0,
+ FS_PORT_NOTIFY,
+ FS_PORT_OUT_L,
+ FS_PORT_OUT_R
+};
+
+enum {
+ CMD_APPLY = 0,
+ CMD_FREE = 1,
+};
+
+typedef struct {
+ /* ports */
+ const LV2_Atom_Sequence* control;
+ LV2_Atom_Sequence* notify;
+ float* output[2];
+
+ /* fluid synth */
+ fluid_settings_t* settings;
+ fluid_synth_t* synth;
+ int synthId;
+
+ /* lv2 URIDs */
+ LV2_URID atom_Blank;
+ LV2_URID atom_Object;
+ LV2_URID atom_URID;
+ LV2_URID atom_Path;
+ LV2_URID midi_MidiEvent;
+ LV2_URID patch_Get;
+ LV2_URID patch_Set;
+ LV2_URID patch_property;
+ LV2_URID patch_value;
+ LV2_URID afs_sf2file;
+
+ /* lv2 extensions */
+ LV2_Log_Log* log;
+ LV2_Log_Logger logger;
+ LV2_Worker_Schedule* schedule;
+ LV2_Atom_Forge forge;
+ LV2_Atom_Forge_Frame frame;
+
+ /* state */
+ bool panic;
+ bool initialized;
+ bool inform_ui;
+
+ char current_sf2_file_path[1024];
+ char queue_sf2_file_path[1024];
+ bool reinit_in_progress; // set in run, cleared in work_response
+ bool queue_reinit; // set in restore, cleared in work_response
+
+ fluid_midi_event_t* fmidi_event;
+
+} AFluidSynth;
+
+/* *****************************************************************************
+ * helpers
+ */
+static bool
+load_sf2 (AFluidSynth* self, const char* fn)
+{
+ const int synth_id = fluid_synth_sfload (self->synth, fn, 1);
+
+ if (synth_id == FLUID_FAILED) {
+ return false;
+ }
+
+ fluid_sfont_t* const sfont = fluid_synth_get_sfont_by_id (self->synth, synth_id);
+ if (!sfont) {
+ return false;
+ }
+
+ int chn;
+ fluid_preset_t preset;
+ sfont->iteration_start (sfont);
+ for (chn = 0; sfont->iteration_next (sfont, &preset) && chn < 15; ++chn) {
+ fluid_synth_program_select (self->synth, chn, synth_id,
+ preset.get_banknum (&preset), preset.get_num (&preset));
+ }
+
+ if (chn == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static const LV2_Atom*
+parse_patch_msg (AFluidSynth* self, const LV2_Atom_Object* obj)
+{
+ const LV2_Atom* property = NULL;
+ const LV2_Atom* file_path = NULL;
+
+ if (obj->body.otype != self->patch_Set) {
+ return NULL;
+ }
+
+ lv2_atom_object_get (obj, self->patch_property, &property, 0);
+ if (!property || property->type != self->atom_URID) {
+ return NULL;
+ } else if (((const LV2_Atom_URID*)property)->body != self->afs_sf2file) {
+ return NULL;
+ }
+
+ lv2_atom_object_get(obj, self->patch_value, &file_path, 0);
+ if (!file_path || file_path->type != self->atom_Path) {
+ return NULL;
+ }
+
+ return file_path;
+}
+
+
+static void
+inform_ui (AFluidSynth* self)
+{
+ if (strlen (self->current_sf2_file_path) == 0) {
+ return;
+ }
+
+ LV2_Atom_Forge_Frame frame;
+ lv2_atom_forge_frame_time (&self->forge, 0);
+ 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_pop (&self->forge, &frame);
+}
+
+/* *****************************************************************************
+ * LV2 Plugin
+ */
+
+static LV2_Handle
+instantiate (const LV2_Descriptor* descriptor,
+ double rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ AFluidSynth* self = (AFluidSynth*)calloc (1, sizeof (AFluidSynth));
+
+ if (!self) {
+ return NULL;
+ }
+
+ LV2_URID_Map* map = NULL;
+
+ for (int i=0; features[i] != NULL; ++i) {
+ if (!strcmp (features[i]->URI, LV2_URID__map)) {
+ map = (LV2_URID_Map*)features[i]->data;
+ } else if (!strcmp (features[i]->URI, LV2_LOG__log)) {
+ self->log = (LV2_Log_Log*)features[i]->data;
+ } else if (!strcmp (features[i]->URI, LV2_WORKER__schedule)) {
+ self->schedule = (LV2_Worker_Schedule*)features[i]->data;
+ }
+ }
+
+ lv2_log_logger_init (&self->logger, map, self->log);
+
+ if (!map) {
+ lv2_log_error (&self->logger, "a-fluidsynth.lv2: Host does not support urid:map\n");
+ free (self);
+ return NULL;
+ }
+
+ if (!self->schedule) {
+ lv2_log_error (&self->logger, "a-fluidsynth.lv2: Host does not support worker:schedule\n");
+ free (self);
+ return NULL;
+ }
+
+ /* initialize fluid synth */
+ self->settings = new_fluid_settings ();
+
+ if (!self->settings) {
+ lv2_log_error (&self->logger, "a-fluidsynth.lv2: cannot allocate Fluid Settings\n");
+ free (self);
+ return NULL;
+ }
+
+ fluid_settings_setnum (self->settings, "synth.sample-rate", rate);
+ fluid_settings_setint (self->settings, "synth.parallel-render", 1);
+ fluid_settings_setint (self->settings, "synth.threadsafe-api", 0);
+
+ self->synth = new_fluid_synth (self->settings);
+
+ if (!self->synth) {
+ lv2_log_error (&self->logger, "a-fluidsynth.lv2: cannot allocate Fluid Synth\n");
+ delete_fluid_settings (self->settings);
+ free (self);
+ return NULL;
+ }
+
+ fluid_synth_set_gain (self->synth, 1.0f);
+ fluid_synth_set_polyphony (self->synth, 32);
+ fluid_synth_set_sample_rate (self->synth, (float)rate);
+
+ self->fmidi_event = new_fluid_midi_event ();
+
+ if (!self->fmidi_event) {
+ lv2_log_error (&self->logger, "a-fluidsynth.lv2: cannot allocate Fluid Event\n");
+ delete_fluid_synth (self->synth);
+ delete_fluid_settings (self->settings);
+ free (self);
+ return NULL;
+ }
+
+ /* initialize plugin state */
+
+ self->panic = true;
+ self->inform_ui = false;
+ self->initialized = false;
+ self->reinit_in_progress = false;
+ self->queue_reinit = false;
+
+ lv2_atom_forge_init (&self->forge, map);
+
+ /* map URIDs */
+ self->atom_Blank = map->map (map->handle, LV2_ATOM__Blank);
+ self->atom_Object = map->map (map->handle, LV2_ATOM__Object);
+ self->atom_Path = map->map (map->handle, LV2_ATOM__Path);
+ self->atom_URID = map->map (map->handle, LV2_ATOM__URID);
+ self->midi_MidiEvent = map->map (map->handle, LV2_MIDI__MidiEvent);
+ self->patch_Get = map->map (map->handle, LV2_PATCH__Get);
+ self->patch_Set = map->map (map->handle, LV2_PATCH__Set);
+ self->patch_property = map->map (map->handle, LV2_PATCH__property);
+ self->patch_value = map->map (map->handle, LV2_PATCH__value);
+ self->afs_sf2file = map->map (map->handle, AFS_URN ":sf2file");
+
+ return (LV2_Handle)self;
+}
+
+static void
+connect_port (LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ AFluidSynth* self = (AFluidSynth*)instance;
+
+ switch (port) {
+ case FS_PORT_CONTROL:
+ self->control = (const LV2_Atom_Sequence*)data;
+ break;
+ 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;
+ break;
+ }
+}
+
+static void
+activate (LV2_Handle instance)
+{
+ AFluidSynth* self = (AFluidSynth*)instance;
+ self->panic = true;
+}
+
+static void
+run (LV2_Handle instance, uint32_t n_samples)
+{
+ AFluidSynth* self = (AFluidSynth*)instance;
+
+ if (!self->control || !self->notify) {
+ return;
+ }
+
+ const uint32_t capacity = self->notify->atom.size;
+ lv2_atom_forge_set_buffer (&self->forge, (uint8_t*)self->notify, capacity);
+ 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));
+ } else if (self->panic) {
+ fluid_synth_all_notes_off (self->synth, -1);
+ fluid_synth_all_sounds_off (self->synth, -1);
+ self->panic = false;
+ }
+
+ uint32_t offset = 0;
+
+ LV2_ATOM_SEQUENCE_FOREACH (self->control, ev) {
+ const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body;
+ if (ev->body.type == self->atom_Blank || ev->body.type == self->atom_Object) {
+ if (obj->body.otype == self->patch_Get) {
+ self->inform_ui = false;
+ inform_ui (self);
+ }
+ else if (obj->body.otype == self->patch_Set) {
+ const LV2_Atom* file_path = parse_patch_msg (self, obj);
+ if (file_path && !self->reinit_in_progress && !self->queue_reinit) {
+ const char *fn = (const char*)(file_path+1);
+ strncpy (self->queue_sf2_file_path, fn, 1023);
+ 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);
+ }
+ }
+ }
+ else if (ev->body.type == self->midi_MidiEvent && self->initialized && !self->reinit_in_progress) {
+ if (ev->body.size > 3 || ev->time.frames >= n_samples) {
+ continue;
+ }
+
+ if (ev->time.frames > offset) {
+ fluid_synth_write_float (
+ self->synth,
+ ev->time.frames - offset,
+ &self->output[0][offset], 0, 1,
+ &self->output[1][offset], 0, 1);
+ }
+
+ offset = ev->time.frames;
+
+ const uint8_t* const data = (const uint8_t*)(ev + 1);
+ fluid_midi_event_set_type (self->fmidi_event, data[0] & 0xf0);
+ fluid_midi_event_set_channel (self->fmidi_event, data[0] & 0x0f);
+ if (ev->body.size > 1) {
+ fluid_midi_event_set_key (self->fmidi_event, data[1]);
+ }
+ if (ev->body.size > 2) {
+ fluid_midi_event_set_value (self->fmidi_event, data[2]);
+ }
+ fluid_synth_handle_midi_event (self->synth, self->fmidi_event);
+ }
+ }
+
+ 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);
+ }
+
+ /* inform the GUI */
+ if (self->inform_ui) {
+ self->inform_ui = false;
+ inform_ui (self);
+ }
+
+ if (n_samples > offset && self->initialized && !self->reinit_in_progress) {
+ fluid_synth_write_float (
+ self->synth,
+ n_samples - offset,
+ &self->output[0][offset], 0, 1,
+ &self->output[1][offset], 0, 1);
+ }
+}
+
+static void cleanup (LV2_Handle instance)
+{
+ AFluidSynth* self = (AFluidSynth*)instance;
+ delete_fluid_synth (self->synth);
+ delete_fluid_settings (self->settings);
+ delete_fluid_midi_event (self->fmidi_event);
+ free (self);
+}
+
+/* *****************************************************************************
+ * LV2 Extensions
+ */
+
+static LV2_Worker_Status
+work (LV2_Handle instance,
+ LV2_Worker_Respond_Function respond,
+ LV2_Worker_Respond_Handle handle,
+ uint32_t size,
+ const void* data)
+{
+ AFluidSynth* self = (AFluidSynth*)instance;
+
+ if (size != sizeof(int)) {
+ return LV2_WORKER_ERR_UNKNOWN;
+ }
+ int magic = *((const int*)data);
+ if (magic != 0x4711) {
+ return LV2_WORKER_ERR_UNKNOWN;
+ }
+
+ self->initialized = load_sf2 (self, self->queue_sf2_file_path);
+ respond (handle, 1, "");
+ return LV2_WORKER_SUCCESS;
+}
+
+static LV2_Worker_Status
+work_response (LV2_Handle instance,
+ uint32_t size,
+ const void* data)
+{
+ AFluidSynth* self = (AFluidSynth*)instance;
+ self->reinit_in_progress = false;
+ self->queue_reinit = false;
+ self->inform_ui = true;
+ self->panic = true;
+
+ if (self->initialized) {
+ strcpy (self->current_sf2_file_path, self->queue_sf2_file_path);
+ } else {
+ self->current_sf2_file_path[0] = 0;
+ }
+ return LV2_WORKER_SUCCESS;
+}
+
+static LV2_State_Status
+save (LV2_Handle instance,
+ LV2_State_Store_Function store,
+ LV2_State_Handle handle,
+ uint32_t flags,
+ const LV2_Feature* const* features)
+{
+ AFluidSynth* self = (AFluidSynth*)instance;
+
+ if (strlen (self->current_sf2_file_path) == 0) {
+ return LV2_STATE_ERR_NO_PROPERTY;
+ }
+
+ LV2_State_Map_Path* map_path = NULL;
+
+ for (int i = 0; features[i]; ++i) {
+ if (!strcmp (features[i]->URI, LV2_STATE__mapPath)) {
+ map_path = (LV2_State_Map_Path*) features[i]->data;
+ }
+ }
+
+ if (!map_path) {
+ return LV2_STATE_ERR_NO_FEATURE;
+ }
+
+ char* apath = map_path->abstract_path (map_path->handle, self->current_sf2_file_path);
+ store (handle, self->afs_sf2file,
+ apath, strlen (apath) + 1,
+ self->atom_Path,
+ LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
+
+ return LV2_STATE_SUCCESS;
+}
+
+static LV2_State_Status
+restore (LV2_Handle instance,
+ LV2_State_Retrieve_Function retrieve,
+ LV2_State_Handle handle,
+ uint32_t flags,
+ const LV2_Feature* const* features)
+{
+ AFluidSynth* self = (AFluidSynth*)instance;
+ if (self->reinit_in_progress || self->queue_reinit) {
+ lv2_log_warning (&self->logger, "a-fluidsynth.lv2: sf2 load already queued.\n");
+ return LV2_STATE_ERR_UNKNOWN;
+ }
+
+ size_t size;
+ uint32_t type;
+ uint32_t valflags;
+
+ const void* value = retrieve (handle, self->afs_sf2file, &size, &type, &valflags);
+ if (value) {
+ strncpy (self->queue_sf2_file_path, value, 1023);
+ self->queue_sf2_file_path[1023] = '\0';
+ self->queue_reinit = true;
+ }
+ return LV2_STATE_SUCCESS;
+}
+
+static const void*
+extension_data (const char* uri)
+{
+ static const LV2_Worker_Interface worker = { work, work_response, NULL };
+ static const LV2_State_Interface state = { save, restore };
+ if (!strcmp (uri, LV2_WORKER__interface)) {
+ return &worker;
+ }
+ else if (!strcmp (uri, LV2_STATE__interface)) {
+ return &state;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ AFS_URN,
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data
+};
+
+#undef LV2_SYMBOL_EXPORT
+#ifdef _WIN32
+# define LV2_SYMBOL_EXPORT __declspec(dllexport)
+#else
+# define LV2_SYMBOL_EXPORT __attribute__ ((visibility ("default")))
+#endif
+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-fluidsynth.lv2/a-fluidsynth.ttl.in b/libs/plugins/a-fluidsynth.lv2/a-fluidsynth.ttl.in
new file mode 100644
index 0000000000..3363d18a6d
--- /dev/null
+++ b/libs/plugins/a-fluidsynth.lv2/a-fluidsynth.ttl.in
@@ -0,0 +1,67 @@
+@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix midi: <http://lv2plug.in/ns/ext/midi#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@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 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/> .
+
+<urn:ardour:a-fluidsynth:sf2file>
+ a lv2:Parameter ;
+ rdfs:label "SF2 File" ;
+ rdfs:range atom:Path .
+
+<urn:ardour:a-fluidsynth>
+ a doap:Project, lv2:InstrumentPlugin, lv2:Plugin ;
+
+ 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> ;
+
+ lv2:microVersion 2 ; lv2:minorVersion 0 ;
+
+ lv2:requiredFeature urid:map, work:schedule ;
+ lv2:extensionData work:interface, state:interface ;
+ lv2:optionalFeature lv2:hardRTCapable;
+
+ 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 ;
+ 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 ;
+ lv2:index 1 ;
+ lv2:symbol "notify" ;
+ lv2:name "UI Notifications" ;
+ ] , [
+ a lv2:OutputPort, lv2:AudioPort ;
+ lv2:index 2 ;
+ lv2:symbol "outL" ;
+ lv2:name "Out Left" ;
+ ] , [
+ a lv2:OutputPort, lv2:AudioPort ;
+ lv2:index 3 ;
+ lv2:symbol "outR" ;
+ lv2:name "Output Right" ;
+ ] .
diff --git a/libs/plugins/a-fluidsynth.lv2/manifest.ttl.in b/libs/plugins/a-fluidsynth.lv2/manifest.ttl.in
new file mode 100644
index 0000000000..39576ff5ad
--- /dev/null
+++ b/libs/plugins/a-fluidsynth.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<urn:ardour:a-fluidsynth>
+ a lv2:Plugin ;
+ lv2:binary <a-fluidsynth@LIB_EXT@> ;
+ rdfs:seeAlso <a-fluidsynth.ttl> .
diff --git a/libs/plugins/a-fluidsynth.lv2/wscript b/libs/plugins/a-fluidsynth.lv2/wscript
new file mode 100644
index 0000000000..50f727e1cc
--- /dev/null
+++ b/libs/plugins/a-fluidsynth.lv2/wscript
@@ -0,0 +1,57 @@
+#!/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-fluidsynth.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-fluidsynth.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-fluidsynth.c',
+ name = 'a-fluidsynth',
+ cflags = [ '-fPIC', bld.env['compiler_flags_dict']['c99'] ],
+ includes = [ '../../ardour' ],
+ target = '../../LV2/%s/a-fluidsynth' % bundle,
+ install_path = '${LV2DIR}/%s' % bundle,
+ uselib = ['LIBFLUIDSYNTH'],
+ use = ['LV2_1_0_0']
+ )
+
+ if bld.is_defined('USE_EXTERNAL_LIBS'):
+ obj.uselib.extend(['LIBFLUIDSYNTH'])
+ else:
+ obj.use.extend(['libfluidsynth_includes', 'libfluidsynth'])
+
+ obj.env.cshlib_PATTERN = module_pat
+ obj.env.cxxshlib_PATTERN = module_pat
+
+# vi:set ts=4 sw=4 et:
diff --git a/wscript b/wscript
index a4ee4ab590..8edd38d176 100644
--- a/wscript
+++ b/wscript
@@ -224,6 +224,7 @@ children = [
'libs/plugins/a-delay.lv2',
'libs/plugins/a-eq.lv2',
'libs/plugins/a-reverb.lv2',
+ 'libs/plugins/a-fluidsynth.lv2',
'gtk2_ardour',
'export',
'midi_maps',