aboutsummaryrefslogtreecommitdiffstats
path: root/lvz
diff options
context:
space:
mode:
Diffstat (limited to 'lvz')
l---------lvz/AudioEffect.hpp1
-rw-r--r--lvz/audioeffectx.h151
-rw-r--r--lvz/gendata.cpp251
-rw-r--r--lvz/wrapper.cpp216
4 files changed, 619 insertions, 0 deletions
diff --git a/lvz/AudioEffect.hpp b/lvz/AudioEffect.hpp
new file mode 120000
index 0000000..df90333
--- /dev/null
+++ b/lvz/AudioEffect.hpp
@@ -0,0 +1 @@
+audioeffectx.h \ No newline at end of file
diff --git a/lvz/audioeffectx.h b/lvz/audioeffectx.h
new file mode 100644
index 0000000..090bb60
--- /dev/null
+++ b/lvz/audioeffectx.h
@@ -0,0 +1,151 @@
+/*
+ LVZ - An ugly C++ interface for writing LV2 plugins.
+ Copyright 2008-2012 David Robillard <http://drobilla.net>
+
+ This 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 3 of the License,
+ or (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef LVZ_AUDIOEFFECTX_H
+#define LVZ_AUDIOEFFECTX_H
+
+#include <stdint.h>
+#include <string.h>
+
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+
+class AudioEffect;
+
+typedef int (*audioMasterCallback)(int, int ver, int, int, int, int);
+
+AudioEffect* createEffectInstance(audioMasterCallback audioMaster);
+
+enum LvzPinFlags {
+ kLvzPinIsActive = 1<<0,
+ kLvzPinIsStereo = 1<<1
+};
+
+struct LvzPinProperties {
+ LvzPinProperties() : label(NULL), flags(0) {}
+ char* label;
+ int flags;
+};
+
+enum LvzEventTypes {
+ kLvzMidiType = 0
+};
+
+struct LvzEvent {
+ int type;
+};
+
+struct LvzMidiEvent : public LvzEvent {
+ char* midiData;
+ int32_t deltaFrames;
+};
+
+struct LvzEvents {
+ int32_t numEvents;
+ LvzEvent** events;
+};
+
+#define DECLARE_LVZ_DEPRECATED(x) x
+
+class AudioEffect {
+public:
+ AudioEffect() {}
+ virtual ~AudioEffect() {}
+
+ virtual void setParameter(int32_t index, float value) = 0;
+ virtual void setParameterAutomated(int32_t index, float value) {}
+ virtual float getParameter(int32_t index) = 0;
+
+ virtual void masterIdle() {}
+};
+
+class AudioEffectX : public AudioEffect {
+public:
+ AudioEffectX(audioMasterCallback audioMaster, int32_t progs, int32_t params)
+ : URI("NIL")
+ , uniqueID("NIL")
+ , eventInput(NULL)
+ , sampleRate(44100)
+ , curProgram(0)
+ , numInputs(0)
+ , numOutputs(0)
+ , numParams(params)
+ , numPrograms(progs)
+ {
+ }
+
+ virtual void process (float **inputs, float **outputs, int32_t nframes) {}
+ virtual void processReplacing(float **inputs, float **outputs, int32_t nframes) = 0;
+
+ void setMidiEventType(LV2_URID urid) { midiEventType = urid; }
+ void setEventInput(const LV2_Atom_Sequence* seq) { eventInput = seq; }
+
+ virtual int32_t processEvents(LvzEvents* ev) { return 0; }
+
+ virtual const char* getURI() { return URI; }
+ virtual const char* getUniqueID() { return uniqueID; }
+ virtual float getSampleRate() { return sampleRate; }
+ virtual int32_t getNumInputs() { return numInputs; }
+ virtual int32_t getNumOutputs() { return numOutputs; }
+ virtual int32_t getNumParameters() { return numParams; }
+ virtual int32_t getNumPrograms() { return numPrograms; }
+
+ virtual void getParameterName(int32_t index, char *label) = 0;
+ virtual bool getProductString(char* text) = 0;
+ virtual void getProgramName(char *name) { name[0] = '\0'; }
+
+ virtual int32_t canDo(const char* text) { return false; }
+ virtual bool canHostDo(const char* act) { return false; }
+ virtual void canMono() {}
+ virtual void canProcessReplacing() {}
+ virtual void isSynth() {}
+ virtual void wantEvents() {}
+
+ virtual void setBlockSize(int32_t size) {}
+ virtual void setNumInputs(int32_t num) { numInputs = num; }
+ virtual void setNumOutputs(int32_t num) { numOutputs = num; }
+ virtual void setSampleRate(float rate) { sampleRate = rate; }
+ virtual void setProgram(int32_t prog) { curProgram = prog; }
+ virtual void setURI(const char* uri) { URI = uri; }
+ virtual void setUniqueID(const char* id) { uniqueID = id; }
+ virtual void suspend() {}
+ virtual void beginEdit(int32_t index) {}
+ virtual void endEdit(int32_t index) {}
+
+ virtual long dispatcher(long opCode, long index, long value, void *ptr, float opt) {
+ return 0;
+ }
+
+protected:
+ const char* URI;
+ const char* uniqueID;
+ const LV2_Atom_Sequence* eventInput;
+ LV2_URID midiEventType;
+ float sampleRate;
+ int32_t curProgram;
+ int32_t numInputs;
+ int32_t numOutputs;
+ int32_t numParams;
+ int32_t numPrograms;
+};
+
+extern "C" {
+AudioEffectX* lvz_new_audioeffectx();
+}
+
+#endif // LVZ_AUDIOEFFECTX_H
diff --git a/lvz/gendata.cpp b/lvz/gendata.cpp
new file mode 100644
index 0000000..2bddf9d
--- /dev/null
+++ b/lvz/gendata.cpp
@@ -0,0 +1,251 @@
+/*
+ LVZ - An ugly C++ interface for writing LV2 plugins.
+ Copyright 2008-2012 David Robillard <http://drobilla.net>
+
+ This 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 3 of the License,
+ or (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <cassert>
+#include <fstream>
+#include <iostream>
+#include <list>
+#include <map>
+#include <string>
+
+#include <dlfcn.h>
+
+#include "audioeffectx.h"
+
+using namespace std;
+
+// VST is so incredibly awful. Just.. wow.
+#define MAX_NAME_LENGTH 1024
+char name_buf[MAX_NAME_LENGTH];
+
+struct Record {
+ Record(const string& n) : base_name(n) {}
+ string base_name;
+ typedef list<string> UIs;
+ UIs uis;
+};
+
+typedef std::map<string, Record> Manifest;
+Manifest manifest;
+
+string
+symbolify(const char* name, char space_char='_')
+{
+ string str(name);
+
+ // Like This -> Like_This
+ for (size_t i=0; i < str.length(); ++i)
+ if (str[i] == ' ')
+ str[i] = space_char;
+
+ str[0] = std::tolower(str[0]);
+
+ // LikeThis -> like_this
+ for (size_t i=1; i < str.length(); ++i)
+ if (str[i] >= 'A' && str[i] <= 'Z'
+ && str[i-1] >= 'a' && str[i-1] <= 'z'
+ && ((i == str.length() - 1) || (str[i+1] <= 'a' && str[i+1] >= 'Z'))
+ && (!(str[i-1] == 'd' && str[i] == 'B'))
+ && (!(str[i-1] == 'F' && str[i] == 'X'))
+ && (!(str[i-1] == 'D' && str[i] == 'C')))
+ str = str.substr(0, i) + space_char + str.substr(i);
+
+ // To lowercase, and skip invalids
+ for (size_t i=1; i < str.length(); ) {
+ if (std::isalpha(str[i]) || std::isdigit(str[i])) {
+ str[i] = std::tolower(str[i]);
+ ++i;
+ } else if (str[i-1] != space_char) {
+ str[i] = space_char;
+ ++i;
+ } else {
+ str = str.substr(0, i) + str.substr(i+1);
+ }
+ }
+
+ return str;
+}
+
+void
+write_plugin(AudioEffectX* effect, const string& lib_file_name)
+{
+ const string base_name = lib_file_name.substr(0, lib_file_name.find_last_of("."));
+ const string data_file_name = base_name + ".ttl";
+
+ fstream os(data_file_name.c_str(), ios::out);
+ effect->getProductString(name_buf);
+
+ os << "@prefix lv2: <http://lv2plug.in/ns/lv2core#> ." << endl;
+ os << "@prefix doap: <http://usefulinc.com/ns/doap#> ." << endl << endl;
+ os << "<" << effect->getURI() << ">" << endl;
+ os << "\tlv2:symbol \"" << effect->getUniqueID() << "\" ;" << endl;
+ os << "\tdoap:name \"" << name_buf << "\" ;" << endl;
+ os << "\tdoap:license <http://usefulinc.com/doap/licenses/gpl> ;" << endl;
+ os << "\tlv2:pluginProperty lv2:hardRTCapable";
+
+ uint32_t num_params = effect->getNumParameters();
+ uint32_t num_audio_ins = effect->getNumInputs();
+ uint32_t num_audio_outs = effect->getNumOutputs();
+ uint32_t num_ports = num_params + num_audio_ins + num_audio_outs;
+
+ if (num_ports > 0)
+ os << " ;" << endl << "\tlv2:port [" << endl;
+ else
+ os << " ." << endl;
+
+ uint32_t idx = 0;
+
+ for (uint32_t i = idx; i < num_params; ++i, ++idx) {
+ effect->getParameterName(i, name_buf);
+ os << "\t\ta lv2:InputPort, lv2:ControlPort ;" << endl;
+ os << "\t\tlv2:index" << " " << idx << " ;" << endl;
+ os << "\t\tlv2:name \"" << name_buf << "\" ;" << endl;
+ os << "\t\tlv2:symbol \"" << symbolify(name_buf) << "\" ;" << endl;
+ os << "\t\tlv2:default " << effect->getParameter(i) << " ;" << endl;
+ os << "\t\tlv2:minimum 0.0 ;" << endl;
+ os << "\t\tlv2:maximum 1.0 ;" << endl;
+ os << ((idx == num_ports - 1) ? "\t] ." : "\t] , [") << endl;
+ }
+
+ for (uint32_t i = 0; i < num_audio_ins; ++i, ++idx) {
+ os << "\t\ta lv2:InputPort, lv2:AudioPort ;" << endl;
+ os << "\t\tlv2:index" << " " << idx << " ;" << endl;
+ os << "\t\tlv2:symbol \"in" << i+1 << "\" ;" << endl;
+ os << "\t\tlv2:name \"Input " << i+1 << "\" ;" << endl;
+ os << ((idx == num_ports - 1) ? "\t] ." : "\t] , [") << endl;
+ }
+
+ for (uint32_t i = 0; i < num_audio_outs; ++i, ++idx) {
+ os << "\t\ta lv2:OutputPort, lv2:AudioPort ;" << endl;
+ os << "\t\tlv2:index " << idx << " ;" << endl;
+ os << "\t\tlv2:symbol \"out" << i+1 << "\" ;" << endl;
+ os << "\t\tlv2:name \"Output " << i+1 << "\" ;" << endl;
+ os << ((idx == num_ports - 1) ? "\t] ." : "\t] , [") << endl;
+ }
+
+ os.close();
+
+ Manifest::iterator i = manifest.find(effect->getURI());
+ if (i != manifest.end()) {
+ i->second.base_name = base_name;
+ } else {
+ manifest.insert(std::make_pair(effect->getURI(), Record(base_name)));
+ }
+
+ if (effect->getNumPrograms() > 1) {
+ std::string preset_file = base_name + "-presets.ttl";
+
+ fstream pos(preset_file.c_str(), ios::out);
+ pos << "@prefix lv2: <http://lv2plug.in/ns/lv2core#> ." << endl;
+ pos << "@prefix pset: <http://lv2plug.in/ns/ext/presets#> ." << endl;
+ pos << "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> ." << endl << endl;
+ for (int32_t i = 0; i < effect->getNumPrograms(); ++i) {
+ effect->setProgram(i);
+ effect->getProgramName(name_buf);
+
+ std::string preset_uri = string(effect->getURI())
+ + "#pset-" + symbolify(name_buf, '-');
+
+ // Write manifest entry
+ std::cout << "<" << preset_uri << ">"
+ << "\n\ta pset:Preset ;\n\tlv2:appliesTo <"
+ << effect->getURI() << "> ;\n\t"
+ << "rdfs:seeAlso <" << preset_file << "> .\n" << std::endl;
+
+ // Write preset file
+ pos << "<" << preset_uri << ">"
+ << "\n\ta pset:Preset ;\n\tlv2:appliesTo <"
+ << effect->getURI() << "> ;\n\t"
+ << "rdfs:label \"" << name_buf << "\"";
+ for (uint32_t i = 0; i < num_params; ++i) {
+ effect->getParameterName(i, name_buf);
+ pos << " ;\n\tlv2:port [" << endl;
+ pos << "\t\tlv2:symbol \"" << symbolify(name_buf) << "\" ;" << endl;
+ pos << "\t\tpset:value " << effect->getParameter(i) << " ;" << endl;
+ pos << "\t]";
+ }
+ pos << " .\n" << endl;
+ }
+ pos.close();
+ }
+}
+
+void
+write_manifest(ostream& os)
+{
+ os << "@prefix lv2: <http://lv2plug.in/ns/lv2core#> ." << endl;
+ os << "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> ." << endl;
+ os << "@prefix uiext: <http://lv2plug.in/ns/extensions/ui#> ." << endl << endl;
+ for (Manifest::iterator i = manifest.begin(); i != manifest.end(); ++i) {
+ Record& r = i->second;
+ os << "<" << i->first << ">\n\ta lv2:Plugin ;" << endl;
+ os << "\trdfs:seeAlso <" << r.base_name << ".ttl> ;" << endl;
+ os << "\tlv2:binary <" << r.base_name << ".so> ";
+ for (Record::UIs::iterator j = r.uis.begin(); j != r.uis.end(); ++j)
+ os << ";" << endl << "\tuiext:ui <" << *j << "> ";
+ os << "." << endl << endl;
+ }
+}
+
+int
+main(int argc, char** argv)
+{
+ if (argc == 0) {
+ cout << "Usage: gendata [PLUGINLIB1] [PLUGINLIB2]..." << endl;
+ cout << "Each argument is a path to a LVZ plugin library." << endl;
+ cout << "For each library an LV2 data file with the same name" << endl;
+ cout << "will be output containing the data for that plugin." << endl;
+ cout << "A manifest of the plugins found is written to stdout" << endl;
+ return 1;
+ }
+
+ typedef AudioEffectX* (*new_effect_func)();
+ typedef AudioEffectX* (*plugin_uri_func)();
+
+ new_effect_func constructor = NULL;
+ AudioEffectX* effect = NULL;
+
+ for (int i = 1; i < argc; ++i) {
+ void* handle = dlopen(argv[i], RTLD_LAZY);
+ if (handle == NULL) {
+ cerr << "ERROR: " << argv[i] << ": " << dlerror() << " (ignoring)" << endl;
+ continue;
+ }
+
+ string lib_path = argv[i];
+ size_t last_slash = lib_path.find_last_of("/");
+ if (last_slash != string::npos)
+ lib_path = lib_path.substr(last_slash + 1);
+
+ constructor = (new_effect_func)dlsym(handle, "lvz_new_audioeffectx");
+ if (constructor != NULL) {
+ effect = constructor();
+ write_plugin(effect, lib_path);
+ }
+
+ if (constructor == NULL) {
+ cerr << "ERROR: " << argv[i] << ": not an LVZ plugin library, ignoring" << endl;
+ }
+
+ dlclose(handle);
+ }
+
+ write_manifest(cout);
+
+ return 0;
+}
diff --git a/lvz/wrapper.cpp b/lvz/wrapper.cpp
new file mode 100644
index 0000000..6d265a7
--- /dev/null
+++ b/lvz/wrapper.cpp
@@ -0,0 +1,216 @@
+/*
+ LVZ - An ugly C++ interface for writing LV2 plugins.
+ Copyright 2008-2012 David Robillard <http://drobilla.net>
+
+ This 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 3 of the License,
+ or (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PLUGIN_CLASS
+#error "This file requires PLUGIN_CLASS to be defined"
+#endif
+#ifndef URI_PREFIX
+#error "This file requires URI_PREFIX to be defined"
+#endif
+#ifndef PLUGIN_URI_SUFFIX
+#error "This file requires PLUGIN_URI_SUFFIX to be defined"
+#endif
+#ifndef PLUGIN_HEADER
+#error "This file requires PLUGIN_HEADER to be defined"
+#endif
+
+#include <stdlib.h>
+#include "audioeffectx.h"
+#include "lv2.h"
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+#include PLUGIN_HEADER
+
+extern "C" {
+
+/* Plugin */
+
+typedef struct {
+ PLUGIN_CLASS* effect;
+ float* controls;
+ float** control_buffers;
+ float** inputs;
+ float** outputs;
+} LVZPlugin;
+
+static void
+lvz_cleanup(LV2_Handle instance)
+{
+ LVZPlugin* plugin = (LVZPlugin*)instance;
+ free(plugin->controls);
+ free(plugin->control_buffers);
+ free(plugin->inputs);
+ free(plugin->outputs);
+ delete plugin->effect;
+ free(plugin);
+}
+
+static void
+lvz_connect_port(LV2_Handle instance, uint32_t port, void* data)
+{
+ LVZPlugin* plugin = (LVZPlugin*)instance;
+ const uint32_t num_params = plugin->effect->getNumParameters();
+ const uint32_t num_inputs = plugin->effect->getNumInputs();
+ const uint32_t num_outputs = plugin->effect->getNumOutputs();
+
+ if (port < num_params) {
+ plugin->control_buffers[port] = (float*)data;
+ } else if (port < num_params + num_inputs) {
+ plugin->inputs[port - num_params] = (float*)data;
+ } else if (port < num_params + num_inputs + num_outputs) {
+ plugin->outputs[port - num_params - num_inputs] = (float*)data;
+ } else if (port == num_params + num_inputs + num_outputs) {
+ plugin->effect->setEventInput((LV2_Atom_Sequence*)data);
+ }
+}
+
+static int
+master_callback(int, int ver, int, int, int, int)
+{
+ return 0;
+}
+
+static LV2_Handle
+lvz_instantiate(const LV2_Descriptor* descriptor,
+ double rate,
+ const char* bundle_path,
+ const LV2_Feature*const* features)
+{
+ PLUGIN_CLASS* effect = new PLUGIN_CLASS(master_callback);
+ effect->setURI(URI_PREFIX PLUGIN_URI_SUFFIX);
+ effect->setSampleRate(rate);
+
+ uint32_t num_params = effect->getNumParameters();
+ uint32_t num_inputs = effect->getNumInputs();
+ uint32_t num_outputs = effect->getNumOutputs();
+
+ LVZPlugin* plugin = (LVZPlugin*)malloc(sizeof(LVZPlugin));
+ plugin->effect = effect;
+
+ for (int i = 0; features[i]; ++i) {
+ if (!strcmp(features[i]->URI, LV2_URID__map)) {
+ LV2_URID_Map* map = (LV2_URID_Map*)features[i]->data;
+ plugin->effect->setMidiEventType(
+ map->map(map->handle, LV2_MIDI__MidiEvent));
+ break;
+ }
+ }
+
+ if (num_params > 0) {
+ plugin->controls = (float*)malloc(sizeof(float) * num_params);
+ plugin->control_buffers = (float**)malloc(sizeof(float*) * num_params);
+ for (uint32_t i = 0; i < num_params; ++i) {
+ plugin->controls[i] = effect->getParameter(i);
+ plugin->control_buffers[i] = NULL;
+ }
+ } else {
+ plugin->controls = NULL;
+ plugin->control_buffers = NULL;
+ }
+
+ if (num_inputs > 0) {
+ plugin->inputs = (float**)malloc(sizeof(float*) * num_inputs);
+ for (uint32_t i = 0; i < num_inputs; ++i) {
+ plugin->inputs[i] = NULL;
+ }
+ } else {
+ plugin->inputs = NULL;
+ }
+
+ if (num_outputs > 0) {
+ plugin->outputs = (float**)malloc(sizeof(float*) * num_outputs);
+ for (uint32_t i = 0; i < num_outputs; ++i) {
+ plugin->outputs[i] = NULL;
+ }
+ } else {
+ plugin->outputs = NULL;
+ }
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+lvz_run(LV2_Handle instance, uint32_t sample_count)
+{
+ LVZPlugin* plugin = (LVZPlugin*)instance;
+
+ for (int32_t i = 0; i < plugin->effect->getNumParameters(); ++i) {
+ float val = plugin->control_buffers[i][0];
+ if (val != plugin->controls[i]) {
+ plugin->effect->setParameter(i, val);
+ plugin->controls[i] = val;
+ }
+ }
+
+ plugin->effect->processReplacing(plugin->inputs, plugin->outputs, sample_count);
+}
+
+static const void*
+lvz_extension_data(const char* uri)
+{
+ return NULL;
+}
+
+static void
+lvz_deactivate(LV2_Handle instance)
+{
+ LVZPlugin* plugin = (LVZPlugin*)instance;
+ plugin->effect->suspend();
+}
+
+/* Library */
+
+static LV2_Descriptor descriptor;
+static bool initialised = false;
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ if (!initialised) {
+ descriptor.URI = URI_PREFIX PLUGIN_URI_SUFFIX;
+ descriptor.instantiate = lvz_instantiate;
+ descriptor.connect_port = lvz_connect_port;
+ descriptor.activate = NULL;
+ descriptor.run = lvz_run;
+ descriptor.deactivate = lvz_deactivate;
+ descriptor.cleanup = lvz_cleanup;
+ descriptor.extension_data = lvz_extension_data;
+ initialised = true;
+ }
+
+ switch (index) {
+ case 0:
+ return &descriptor;
+ default:
+ return NULL;
+ }
+}
+
+/** Entry point for LVZ gendata */
+LV2_SYMBOL_EXPORT
+AudioEffectX*
+lvz_new_audioeffectx()
+{
+ PLUGIN_CLASS* effect = new PLUGIN_CLASS(master_callback);
+ effect->setURI(URI_PREFIX PLUGIN_URI_SUFFIX);
+ return effect;
+}
+
+} // extern "C"