From 9bee2ff784728453511d6d749ae8410b992059b8 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 21 Jun 2019 17:13:42 +0200 Subject: [PATCH] Export internal audio and midi players as LV2 plugins Signed-off-by: falkTX --- source/plugin/carla-base.cpp | 6 ++- source/plugin/carla-lv2-export.cpp | 40 +++++++++++----- source/plugin/carla-lv2.cpp | 74 ++++++++++++++++++++++++++++-- source/utils/CarlaLv2Utils.hpp | 21 +++++++++ 4 files changed, 124 insertions(+), 17 deletions(-) diff --git a/source/plugin/carla-base.cpp b/source/plugin/carla-base.cpp index ff5cdad7a9..da203a6241 100644 --- a/source/plugin/carla-base.cpp +++ b/source/plugin/carla-base.cpp @@ -59,8 +59,10 @@ struct PluginListManager { std::strcmp(desc->label, "carlapatchbay3s" ) == 0 || std::strcmp(desc->label, "carlapatchbay16" ) == 0 || std::strcmp(desc->label, "carlapatchbay32" ) == 0 || - std::strcmp(desc->label, "bigmeter" ) == 0 /*|| - std::strcmp(desc->label, "notes" ) == 0*/) + std::strcmp(desc->label, "bigmeter" ) == 0 || + /*std::strcmp(desc->label, "notes" ) == 0*/ + std::strcmp(desc->label, "audiofile" ) == 0 || + std::strcmp(desc->label, "midifile" ) == 0) { descs.append(desc); } diff --git a/source/plugin/carla-lv2-export.cpp b/source/plugin/carla-lv2-export.cpp index e6de2eb26e..621eabf0c8 100644 --- a/source/plugin/carla-lv2-export.cpp +++ b/source/plugin/carla-lv2-export.cpp @@ -24,6 +24,7 @@ #include "lv2/midi.h" #include "lv2/options.h" #include "lv2/parameters.h" +#include "lv2/patch.h" #include "lv2/port-props.h" #include "lv2/state.h" #include "lv2/time.h" @@ -111,6 +112,7 @@ static void writeManifestFile(PluginListManager& plm) // ------------------------------------------------------------------- // Header + text += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; text += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; text += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; text += "@prefix rdfs: .\n"; @@ -158,6 +160,15 @@ static void writeManifestFile(PluginListManager& plm) # endif #endif + // ------------------------------------------------------------------- + // File handling + + text += "\n"; + text += " a lv2:Parameter ;\n"; + text += " rdfs:label \"file\" ;\n"; + text += " rdfs:range atom:Path .\n"; + text += "\n"; + // ------------------------------------------------------------------- // Write file now @@ -216,15 +227,16 @@ static void writePluginFile(const NativePluginDescriptor* const pluginDesc) // ------------------------------------------------------------------- // Header - text += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; - text += "@prefix doap: .\n"; - text += "@prefix foaf: .\n"; - text += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; - text += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; - text += "@prefix rdf: .\n"; - text += "@prefix rdfs: .\n"; - text += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; - text += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n"; + text += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; + text += "@prefix doap: .\n"; + text += "@prefix foaf: .\n"; + text += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; + text += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; + text += "@prefix patch: <" LV2_PATCH_PREFIX "> .\n"; + text += "@prefix rdf: .\n"; + text += "@prefix rdfs: .\n"; + text += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; + text += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n"; text += "\n"; // ------------------------------------------------------------------- @@ -287,7 +299,7 @@ static void writePluginFile(const NativePluginDescriptor* const pluginDesc) text += " lv2:extensionData <" LV2_OPTIONS__interface "> ;\n"; - if (pluginDesc->hints & NATIVE_PLUGIN_USES_STATE) + if ((pluginDesc->hints & NATIVE_PLUGIN_USES_STATE) || (pluginDesc->hints & NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE)) text += " lv2:extensionData <" LV2_STATE__interface "> ;\n"; if (pluginDesc->category != NATIVE_PLUGIN_CATEGORY_SYNTH) @@ -310,7 +322,7 @@ static void writePluginFile(const NativePluginDescriptor* const pluginDesc) // UIs #ifdef HAVE_PYQT - if (pluginDesc->hints & NATIVE_PLUGIN_HAS_UI) + if ((pluginDesc->hints & NATIVE_PLUGIN_HAS_UI) != 0 && (pluginDesc->hints & NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE) == 0) { text += " ui:ui ;\n"; text += "\n"; @@ -320,7 +332,8 @@ static void writePluginFile(const NativePluginDescriptor* const pluginDesc) // ------------------------------------------------------------------- // First input MIDI/Time/UI port - const bool hasEventInPort = (pluginDesc->hints & NATIVE_PLUGIN_USES_TIME) != 0 || (pluginDesc->hints & NATIVE_PLUGIN_HAS_UI) != 0; + const bool hasEventInPort = (pluginDesc->hints & NATIVE_PLUGIN_USES_TIME) != 0 + || (pluginDesc->hints & NATIVE_PLUGIN_HAS_UI) != 0; if (pluginDesc->midiIns > 0 || hasEventInPort) { @@ -375,6 +388,9 @@ static void writePluginFile(const NativePluginDescriptor* const pluginDesc) text += " ] ;\n\n"; } + if (pluginDesc->hints & NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE) + text += " patch:writable ;\n\n"; + // ------------------------------------------------------------------- // MIDI inputs diff --git a/source/plugin/carla-lv2.cpp b/source/plugin/carla-lv2.cpp index 65fec79ba6..b4d10ce559 100644 --- a/source/plugin/carla-lv2.cpp +++ b/source/plugin/carla-lv2.cpp @@ -71,6 +71,7 @@ class NativePlugin : public Lv2PluginBaseClass #ifdef USING_JUCE fJuceInitialiser(), #endif + fLoadedFile(), fWorkerUISignal(0) { carla_zeroStruct(fHost); @@ -244,6 +245,8 @@ class NativePlugin : public Lv2PluginBaseClass if (event->body.type == fURIs.uiEvents && fWorkerUISignal != -1) { + CARLA_SAFE_ASSERT_CONTINUE((fDescriptor->hints & NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE) == 0); + if (fWorker != nullptr) { // worker is supported by the host, we can continue @@ -260,6 +263,34 @@ class NativePlugin : public Lv2PluginBaseClass continue; } + if (event->body.type == fURIs.atomObject) + { + const LV2_Atom_Object* const obj = (const LV2_Atom_Object*)(&event->body); + + if (obj->body.otype == fURIs.patchSet) { + // Get property URI. + const LV2_Atom* property = NULL; + lv2_atom_object_get(obj, fURIs.patchProperty, &property, 0); + CARLA_SAFE_ASSERT_CONTINUE(property != nullptr); + CARLA_SAFE_ASSERT_CONTINUE(property->type == fURIs.atomURID); + CARLA_SAFE_ASSERT_CONTINUE(((const LV2_Atom_URID*)property)->body == fURIs.carlaFile); + + // Get value. + const LV2_Atom* fileobj = NULL; + lv2_atom_object_get(obj, fURIs.patchValue, &fileobj, 0); + CARLA_SAFE_ASSERT_CONTINUE(fileobj != nullptr); + CARLA_SAFE_ASSERT_CONTINUE(fileobj->type == fURIs.atomPath); + + const char* const filepath((const char*)(fileobj + 1)); + + fWorker->schedule_work(fWorker->handle, + static_cast(std::strlen(filepath) + 1U), + filepath); + } + + continue; + } + if (event->body.type != fURIs.midiEvent) continue; if (event->body.size > 4) @@ -366,6 +397,17 @@ class NativePlugin : public Lv2PluginBaseClass LV2_State_Status lv2_save(const LV2_State_Store_Function store, const LV2_State_Handle handle, const uint32_t /*flags*/, const LV2_Feature* const* const /*features*/) const { + if (fDescriptor->hints & NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE) + { + store(handle, + fUridMap->map(fUridMap->handle, "http://kxstudio.sf.net/ns/carla/file"), + fLoadedFile.buffer(), + fLoadedFile.length()+1, + fURIs.atomPath, + LV2_STATE_IS_POD); + return LV2_STATE_SUCCESS; + } + if ((fDescriptor->hints & NATIVE_PLUGIN_USES_STATE) == 0 || fDescriptor->get_state == nullptr) return LV2_STATE_ERR_NO_FEATURE; @@ -380,13 +422,31 @@ class NativePlugin : public Lv2PluginBaseClass } LV2_State_Status lv2_restore(const LV2_State_Retrieve_Function retrieve, const LV2_State_Handle handle, - uint32_t flags, const LV2_Feature* const* const /*features*/) const + uint32_t flags, const LV2_Feature* const* const /*features*/) { + size_t size = 0; + uint32_t type = 0; + + if (fDescriptor->hints & NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE) + { + size = type = 0; + const void* const data = retrieve(handle, + fUridMap->map(fUridMap->handle, "http://kxstudio.sf.net/ns/carla/file"), + &size, &type, &flags); + + CARLA_SAFE_ASSERT_RETURN(type == fURIs.atomPath, LV2_STATE_ERR_UNKNOWN); + + const char* const filename = (const char*)data; + + fLoadedFile = filename; + fDescriptor->set_custom_data(fHandle, "file", filename); + return LV2_STATE_SUCCESS; + } + if ((fDescriptor->hints & NATIVE_PLUGIN_USES_STATE) == 0 || fDescriptor->set_state == nullptr) return LV2_STATE_ERR_NO_FEATURE; - size_t size = 0; - uint32_t type = 0; + size = type = 0; const void* const data = retrieve(handle, fUridMap->map(fUridMap->handle, "http://kxstudio.sf.net/ns/carla/chunk"), &size, &type, &flags); if (size == 0) @@ -409,6 +469,13 @@ class NativePlugin : public Lv2PluginBaseClass { const char* const msg = (const char*)data; + if (fDescriptor->hints & NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE) + { + fLoadedFile = msg; + fDescriptor->set_custom_data(fHandle, "file", msg); + return LV2_WORKER_SUCCESS; + } + /**/ if (std::strncmp(msg, "control ", 8) == 0) { if (fDescriptor->ui_set_parameter_value == nullptr) @@ -767,6 +834,7 @@ class NativePlugin : public Lv2PluginBaseClass juce::SharedResourcePointer fJuceInitialiser; #endif + CarlaString fLoadedFile; int fWorkerUISignal; // ------------------------------------------------------------------- diff --git a/source/utils/CarlaLv2Utils.hpp b/source/utils/CarlaLv2Utils.hpp index d46272515d..2238b4fa70 100644 --- a/source/utils/CarlaLv2Utils.hpp +++ b/source/utils/CarlaLv2Utils.hpp @@ -569,6 +569,7 @@ class Lv2PluginBaseClass : public LV2_External_UI_Widget_Compat fBufferSize(0), fSampleRate(sampleRate), fUridMap(nullptr), + fUridUnmap(nullptr), fWorker(nullptr), fTimeInfo(), fLastPositionData(), @@ -660,6 +661,7 @@ class Lv2PluginBaseClass : public LV2_External_UI_Widget_Compat fUridMap = uridMap; fURIs.map(uridMap); + fUridUnmap = uridUnmap; fWorker = worker; clearTimeData(); @@ -1175,6 +1177,7 @@ class Lv2PluginBaseClass : public LV2_External_UI_Widget_Compat // LV2 host features const LV2_URID_Map* fUridMap; + const LV2_URID_Unmap* fUridUnmap; const LV2_Worker_Schedule* fWorker; // Time info stuff @@ -1475,9 +1478,15 @@ class Lv2PluginBaseClass : public LV2_External_UI_Widget_Compat LV2_URID atomFloat; LV2_URID atomInt; LV2_URID atomLong; + LV2_URID atomPath; LV2_URID atomSequence; LV2_URID atomString; + LV2_URID atomURID; + LV2_URID carlaFile; LV2_URID midiEvent; + LV2_URID patchProperty; + LV2_URID patchSet; + LV2_URID patchValue; LV2_URID timePos; LV2_URID timeBar; LV2_URID timeBarBeat; @@ -1496,9 +1505,15 @@ class Lv2PluginBaseClass : public LV2_External_UI_Widget_Compat atomFloat(0), atomInt(0), atomLong(0), + atomPath(0), atomSequence(0), atomString(0), + atomURID(0), + carlaFile(0), midiEvent(0), + patchProperty(0), + patchSet(0), + patchValue(0), timePos(0), timeBar(0), timeBarBeat(0), @@ -1518,9 +1533,15 @@ class Lv2PluginBaseClass : public LV2_External_UI_Widget_Compat atomFloat = uridMap->map(uridMap->handle, LV2_ATOM__Float); atomInt = uridMap->map(uridMap->handle, LV2_ATOM__Int); atomLong = uridMap->map(uridMap->handle, LV2_ATOM__Long); + atomPath = uridMap->map(uridMap->handle, LV2_ATOM__Path); atomSequence = uridMap->map(uridMap->handle, LV2_ATOM__Sequence); atomString = uridMap->map(uridMap->handle, LV2_ATOM__String); + atomURID = uridMap->map(uridMap->handle, LV2_ATOM__URID); + carlaFile = uridMap->map(uridMap->handle, "http://kxstudio.sf.net/carla/file"); midiEvent = uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent); + patchProperty = uridMap->map(uridMap->handle, LV2_PATCH__property); + patchSet = uridMap->map(uridMap->handle, LV2_PATCH__Set); + patchValue = uridMap->map(uridMap->handle, LV2_PATCH__value); timePos = uridMap->map(uridMap->handle, LV2_TIME__Position); timeBar = uridMap->map(uridMap->handle, LV2_TIME__bar); timeBarBeat = uridMap->map(uridMap->handle, LV2_TIME__barBeat);