Browse Source

exported lv2s can now show custom gui

tags/1.9.8
falkTX 8 years ago
parent
commit
bd963b6b5d
6 changed files with 341 additions and 30 deletions
  1. +9
    -5
      source/backend/engine/CarlaEngineThread.cpp
  2. +29
    -3
      source/backend/plugin/CarlaPlugin.cpp
  3. +1
    -1
      source/backend/plugin/CarlaPluginJack.cpp
  4. +296
    -11
      source/plugin/carla-lv2-single.cpp
  5. +5
    -9
      source/plugin/carla-lv2.cpp
  6. +1
    -1
      source/widgets/racklistwidget.py

+ 9
- 5
source/backend/engine/CarlaEngineThread.cpp View File

@@ -40,21 +40,23 @@ CarlaEngineThread::~CarlaEngineThread() noexcept

void CarlaEngineThread::run() noexcept
{
// FIXME carla-lv2-single using build-bridge!
CARLA_SAFE_ASSERT_RETURN(kEngine != nullptr,);
#ifndef BUILD_BRIDGE
CARLA_SAFE_ASSERT(kEngine->isRunning());
#endif
carla_debug("CarlaEngineThread::run()");

#ifdef HAVE_LIBLO
const bool isPlugin(kEngine->getType() == kEngineTypePlugin);
#endif
float value;

#ifndef BUILD_BRIDGE
CARLA_SAFE_ASSERT(kEngine->isRunning() || isPlugin);
#endif
carla_debug("CarlaEngineThread::run()");

#ifdef BUILD_BRIDGE
for (; /*kEngine->isRunning() &&*/ ! shouldThreadExit();)
#else
for (; kEngine->isRunning() && ! shouldThreadExit();)
for (; (kEngine->isRunning() || isPlugin) && ! shouldThreadExit();)
#endif
{
#if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE)
@@ -129,6 +131,8 @@ void CarlaEngineThread::run() noexcept

carla_msleep(25);
}

carla_stdout("CarlaEngineThread closed");
}

// -----------------------------------------------------------------------


+ 29
- 3
source/backend/plugin/CarlaPlugin.cpp View File

@@ -1,6 +1,6 @@
/*
* Carla Plugin
* Copyright (C) 2011-2014 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2011-2017 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -966,11 +966,20 @@ bool CarlaPlugin::exportAsLV2(const char* const lv2path)

manifestStream << "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n";
manifestStream << "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
manifestStream << "@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .\n";
manifestStream << "\n";
manifestStream << "<" << symbol.buffer() << ".ttl>\n";
manifestStream << " a lv2:Plugin ;\n";
manifestStream << " lv2:binary <" << symbol.buffer() << ".so> ;\n";
manifestStream << " rdfs:seeAlso <" << symbol.buffer() << ".ttl> .\n";
manifestStream << "\n";
manifestStream << "<ext-ui>\n";
manifestStream << " a <http://kxstudio.sf.net/ns/lv2ext/external-ui#Widget> ;\n";
manifestStream << " ui:binary <" << symbol.buffer() << ".so> ;\n";
manifestStream << " lv2:extensionData <http://lv2plug.in/ns/extensions/ui#idleInterface> ,\n";
manifestStream << " <http://lv2plug.in/ns/extensions/ui#showInterface> ;\n";
manifestStream << " lv2:requiredFeature <http://lv2plug.in/ns/ext/instance-access> .\n";
manifestStream << "\n";

const CarlaString manifestFilename(bundlepath + CARLA_OS_SEP_STR "manifest.ttl");
const File manifestFile(manifestFilename.buffer());
@@ -988,6 +997,7 @@ bool CarlaPlugin::exportAsLV2(const char* const lv2path)
mainStream << "@prefix doap: <http://usefulinc.com/ns/doap#> .\n";
mainStream << "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n";
mainStream << "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
mainStream << "@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .\n";
mainStream << "\n";
mainStream << "<>\n";
mainStream << " a lv2:Plugin ;\n";
@@ -997,6 +1007,12 @@ bool CarlaPlugin::exportAsLV2(const char* const lv2path)
mainStream << " <http://lv2plug.in/ns/ext/urid#map> ;\n";
mainStream << "\n";

if (pData->hints & PLUGIN_HAS_CUSTOM_UI)
{
mainStream << " ui:ui <ext-ui> ;\n";
mainStream << "\n";
}

int portIndex = 0;

for (uint32_t i=0; i<pData->audioIn.count; ++i)
@@ -1033,8 +1049,9 @@ bool CarlaPlugin::exportAsLV2(const char* const lv2path)
mainStream << " lv2:default 0 ;\n";
mainStream << " lv2:minimum 0 ;\n";
mainStream << " lv2:maximum 1 ;\n";
mainStream << " lv2:portProperty lv2:toggled , lv2:integer;\n";
// TODO designation, hidegui
mainStream << " lv2:designation lv2:freeWheeling ;\n";
mainStream << " lv2:portProperty lv2:toggled , lv2:integer ;\n";
mainStream << " lv2:portProperty <http://lv2plug.in/ns/ext/port-props#notOnGUI> ;\n";
mainStream << " ] ;\n";

for (uint32_t i=0; i<pData->param.count; ++i)
@@ -1069,6 +1086,15 @@ bool CarlaPlugin::exportAsLV2(const char* const lv2path)
CarlaString s(strBufName);
s.toBasic();
std::memcpy(strBufSymbol, s.buffer(), s.length()+1);

// FIXME - must be unique
if (strBufSymbol[0] >= '0' && strBufSymbol[0] <= '9')
{
const size_t len(std::strlen(strBufSymbol));
std::memmove(strBufSymbol+1, strBufSymbol, len);
strBufSymbol[0] = '_';
strBufSymbol[len+1] = '\0';
}
}

mainStream << " lv2:index " << portIndexNum << " ;\n";


+ 1
- 1
source/backend/plugin/CarlaPluginJack.cpp View File

@@ -220,7 +220,7 @@ public:
fShmNonRtServerControl(),
fInfo()
{
carla_debug("CarlaPluginJack::CarlaPluginJack(%p, %i, %s, %s)", engine, id, BinaryType2Str(btype), PluginType2Str(ptype));
carla_debug("CarlaPluginJack::CarlaPluginJack(%p, %i)", engine, id);

pData->hints |= PLUGIN_IS_BRIDGE;
}


+ 296
- 11
source/plugin/carla-lv2-single.cpp View File

@@ -25,11 +25,24 @@
#include "CarlaLv2Utils.hpp"
#include "CarlaUtils.h"

#include "juce_audio_basics/juce_audio_basics.h"

#if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
# include "juce_gui_basics/juce_gui_basics.h"
#else
# include "juce_events/juce_events.h"
#endif

using juce::FloatVectorOperations;
using juce::ScopedJuceInitialiser_GUI;
using juce::SharedResourcePointer;

// ---------------------------------------------------------------------------------------------------------------------

CARLA_BACKEND_START_NAMESPACE

class CarlaEngineLV2Single : public CarlaEngine
class CarlaEngineLV2Single : public CarlaEngine,
public LV2_External_UI_Widget
{
public:
CarlaEngineLV2Single(const uint32_t bufferSize, const double sampleRate, const char* const bundlePath, const LV2_URID_Map* uridMap)
@@ -37,6 +50,10 @@ public:
fIsRunning(false),
fIsOffline(false)
{
run = extui_run;
show = extui_show;
hide = extui_hide;

// xxxxx
CarlaString binaryDir(bundlePath);
binaryDir += CARLA_OS_SEP_STR "bin" CARLA_OS_SEP_STR;
@@ -158,6 +175,110 @@ public:
fPorts.updateOutputs();
}

// -----------------------------------------------------------------------------------------------------------------

void lv2ui_instantiate(LV2UI_Write_Function writeFunction, LV2UI_Controller controller,
LV2UI_Widget* widget, const LV2_Feature* const* features)
{
fUI.writeFunction = writeFunction;
fUI.controller = controller;

const LV2_URID_Map* uridMap = nullptr;

// -------------------------------------------------------------------------------------------------------------
// see if the host supports external-ui, get uridMap

for (int i=0; features[i] != nullptr; ++i)
{
if (std::strcmp(features[i]->URI, LV2_EXTERNAL_UI__Host) == 0 ||
std::strcmp(features[i]->URI, LV2_EXTERNAL_UI_DEPRECATED_URI) == 0)
{
fUI.host = (const LV2_External_UI_Host*)features[i]->data;
}
else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0)
{
uridMap = (const LV2_URID_Map*)features[i]->data;
}
}

if (fUI.host != nullptr)
{
fUI.name = fUI.host->plugin_human_id;
*widget = (LV2_External_UI_Widget*)this;
return;
}

// -------------------------------------------------------------------------------------------------------------
// no external-ui support, use showInterface

for (int i=0; features[i] != nullptr; ++i)
{
if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0)
{
const LV2_Options_Option* const options((const LV2_Options_Option*)features[i]->data);

for (int j=0; options[j].key != 0; ++j)
{
if (options[j].key == uridMap->map(uridMap->handle, LV2_UI__windowTitle))
{
fUI.name = (const char*)options[j].value;
break;
}
}
break;
}
}

if (fUI.name.isEmpty())
fUI.name = fPlugin->getName();

*widget = nullptr;
}

void lv2ui_port_event(uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) const
{
if (format != 0 || bufferSize != sizeof(float) || buffer == nullptr)
return;
if (portIndex >= fPorts.indexOffset || ! fUI.visible)
return;

const float value(*(const float*)buffer);
fPlugin->uiParameterChange(portIndex-fPorts.indexOffset, value);
}

void lv2ui_cleanup()
{
if (fUI.visible)
handleUiHide();

fUI.writeFunction = nullptr;
fUI.controller = nullptr;
fUI.host = nullptr;
}

// -----------------------------------------------------------------------------------------------------------------

int lv2ui_idle() const
{
if (! fUI.visible)
return 1;

handleUiRun();
return 0;
}

int lv2ui_show()
{
handleUiShow();
return 0;
}

int lv2ui_hide()
{
handleUiHide();
return 0;
}

protected:
// -----------------------------------------------------------------------------------------------------------------
// CarlaEngine virtual calls
@@ -208,17 +329,47 @@ protected:
return "LV2 Plugin";
}

void callback(const EngineCallbackOpcode action, const uint pluginId, const int value1, const int value2, const float value3, const char* const valueStr) noexcept override
void engineCallback(const EngineCallbackOpcode action, const uint pluginId, const int value1, const int value2, const float value3, const char* const valueStr)
{
CarlaEngine::callback(action, pluginId, value1, value2, value3, valueStr);
switch (action)
{
case ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED:
if (fUI.writeFunction != nullptr && fUI.controller != nullptr && fUI.visible)
fUI.writeFunction(fUI.controller, value1+fPorts.indexOffset, sizeof(float), 0, &value3);
break;

//if (action == ENGINE_CALLBACK_IDLE && ! pData->aboutToClose)
// pHost->dispatcher(pHost->handle, NATIVE_HOST_OPCODE_HOST_IDLE, 0, 0, nullptr, 0.0f);
case ENGINE_CALLBACK_UI_STATE_CHANGED:
fUI.visible = value1 == 1;
if (fUI.host != nullptr)
fUI.host->ui_closed(fUI.controller);
break;

case ENGINE_CALLBACK_IDLE:
break;

default:
carla_stdout("engineCallback(%i:%s, %u, %i, %i, %f, %s)", action, EngineCallbackOpcode2Str(action), pluginId, value1, value2, value3, valueStr);
break;
}
}

void engineCallback(const EngineCallbackOpcode action, const uint pluginId, const int value1, const int value2, const float value3, const char* const valueStr)
// -----------------------------------------------------------------------------------------------------------------

void handleUiRun() const
{
fPlugin->uiIdle();
}

void handleUiShow()
{
carla_stdout("engineCallback(%i:%s, %u, %i, %i, %f, %s)", action, EngineCallbackOpcode2Str(action), pluginId, value1, value2, value3, valueStr);
fPlugin->showCustomUI(true);
fUI.visible = true;
}

void handleUiHide()
{
fUI.visible = false;
fPlugin->showCustomUI(false);
}

// -----------------------------------------------------------------------------------------------------------------
@@ -240,6 +391,7 @@ private:
float* paramsLast;
float** paramsPtr;
bool* paramsOut;
uint32_t indexOffset;
const CarlaPlugin* plugin;

Ports()
@@ -252,6 +404,7 @@ private:
paramsLast(nullptr),
paramsPtr(nullptr),
paramsOut(nullptr),
indexOffset(1),
plugin(nullptr) {}

~Ports()
@@ -320,6 +473,8 @@ private:
paramsOut [i] = plugin->isParameterOutput(i);
}
}

indexOffset = numAudioIns + numAudioOuts + 1;
}

void connectPort(const uint32_t port, void* const dataLocation) noexcept
@@ -378,8 +533,42 @@ private:

} fPorts;

struct UI {
LV2UI_Write_Function writeFunction;
LV2UI_Controller controller;
const LV2_External_UI_Host* host;
CarlaString name;
bool visible;

UI()
: writeFunction(nullptr),
controller(nullptr),
host(nullptr),
visible(false) {}

} fUI;

SharedResourcePointer<ScopedJuceInitialiser_GUI> sJuceInitialiser;

// -------------------------------------------------------------------

#define handlePtr ((CarlaEngineLV2Single*)handle)

static void extui_run(LV2_External_UI_Widget* handle)
{
handlePtr->handleUiRun();
}

static void extui_show(LV2_External_UI_Widget* handle)
{
handlePtr->handleUiShow();
}

static void extui_hide(LV2_External_UI_Widget* handle)
{
handlePtr->handleUiHide();
}

static void _engine_callback(void* handle, EngineCallbackOpcode action, uint pluginId, int value1, int value2, float value3, const char* valueStr)
{
handlePtr->engineCallback(action, pluginId, value1, value2, value3, valueStr);
@@ -392,11 +581,11 @@ private:

CARLA_BACKEND_END_NAMESPACE

// ---------------------------------------------------------------------------------------------------------------------
// LV2 plugin descriptor functions

CARLA_BACKEND_USE_NAMESPACE

// ---------------------------------------------------------------------------------------------------------------------
// LV2 DSP functions

static LV2_Handle lv2_instantiate(const LV2_Descriptor* lv2Descriptor, double sampleRate, const char* bundlePath, const LV2_Feature* const* features)
{
carla_stdout("lv2_instantiate(%p, %g, %s, %p)", lv2Descriptor, sampleRate, bundlePath, features);
@@ -516,6 +705,85 @@ static const void* lv2_extension_data(const char* uri)

#undef instancePtr

// ---------------------------------------------------------------------------------------------------------------------
// LV2 UI functions

static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char*, const char*,
LV2UI_Write_Function writeFunction, LV2UI_Controller controller,
LV2UI_Widget* widget, const LV2_Feature* const* features)
{
carla_debug("lv2ui_instantiate(..., %p, %p, %p)", writeFunction, controller, widget, features);

CarlaEngineLV2Single* engine = nullptr;

for (int i=0; features[i] != nullptr; ++i)
{
if (std::strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0)
{
engine = (CarlaEngineLV2Single*)features[i]->data;
break;
}
}

if (engine == nullptr)
{
carla_stderr("Host doesn't support instance-access, cannot show UI");
return nullptr;
}

engine->lv2ui_instantiate(writeFunction, controller, widget, features);

return (LV2UI_Handle)engine;
}

#define uiPtr ((CarlaEngineLV2Single*)ui)

static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer)
{
carla_debug("lv2ui_port_event(%p, %i, %i, %i, %p)", ui, portIndex, bufferSize, format, buffer);
uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer);
}

static void lv2ui_cleanup(LV2UI_Handle ui)
{
carla_debug("lv2ui_cleanup(%p)", ui);
uiPtr->lv2ui_cleanup();
}

static int lv2ui_idle(LV2UI_Handle ui)
{
return uiPtr->lv2ui_idle();
}

static int lv2ui_show(LV2UI_Handle ui)
{
carla_debug("lv2ui_show(%p)", ui);
return uiPtr->lv2ui_show();
}

static int lv2ui_hide(LV2UI_Handle ui)
{
carla_debug("lv2ui_hide(%p)", ui);
return uiPtr->lv2ui_hide();
}

static const void* lv2ui_extension_data(const char* uri)
{
carla_stdout("lv2ui_extension_data(\"%s\")", uri);

static const LV2UI_Idle_Interface uiidle = { lv2ui_idle };
static const LV2UI_Show_Interface uishow = { lv2ui_show, lv2ui_hide };

if (std::strcmp(uri, LV2_UI__idleInterface) == 0)
return &uiidle;
if (std::strcmp(uri, LV2_UI__showInterface) == 0)
return &uishow;

return nullptr;
}

#undef uiPtr

// ---------------------------------------------------------------------------------------------------------------------
// Startup code

@@ -555,7 +823,24 @@ const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
{
carla_stdout("lv2ui_descriptor(%i)", index);

return nullptr;
static CarlaString ret;

if (ret.isEmpty())
{
using namespace juce;
const File file(File::getSpecialLocation(File::currentExecutableFile).getSiblingFile("ext-ui"));
ret = String("file://" + file.getFullPathName()).toRawUTF8();
}

static const LV2UI_Descriptor lv2UiExtDesc = {
/* URI */ ret.buffer(),
/* instantiate */ lv2ui_instantiate,
/* cleanup */ lv2ui_cleanup,
/* port_event */ lv2ui_port_event,
/* extension_data */ lv2ui_extension_data
};

return (index == 0) ? &lv2UiExtDesc : nullptr;
}

// ---------------------------------------------------------------------------------------------------------------------

+ 5
- 9
source/plugin/carla-lv2.cpp View File

@@ -883,7 +883,7 @@ public:
if (fUI.host != nullptr)
{
fHost.uiName = carla_strdup(fUI.host->plugin_human_id);
*widget = this;
*widget = (LV2_External_UI_Widget_Compat*)this;
return;
}

@@ -1460,29 +1460,25 @@ private:

// -------------------------------------------------------------------

#define handlePtr ((NativePlugin*)self)
#define handlePtr ((NativePlugin*)handle)

static void extui_run(LV2_External_UI_Widget_Compat* self)
static void extui_run(LV2_External_UI_Widget_Compat* handle)
{
handlePtr->handleUiRun();
}

static void extui_show(LV2_External_UI_Widget_Compat* self)
static void extui_show(LV2_External_UI_Widget_Compat* handle)
{
handlePtr->handleUiShow();
}

static void extui_hide(LV2_External_UI_Widget_Compat* self)
static void extui_hide(LV2_External_UI_Widget_Compat* handle)
{
handlePtr->handleUiHide();
}

#undef handlePtr

// -------------------------------------------------------------------

#define handlePtr ((NativePlugin*)handle)

static uint32_t host_get_buffer_size(NativeHostHandle handle)
{
return handlePtr->fBufferSize;


+ 1
- 1
source/widgets/racklistwidget.py View File

@@ -196,7 +196,7 @@ class RackListWidget(QListWidget):

exts = gCarla.utils.get_supported_file_extensions().split(";")

if WINDOWS and not MACOS:
if WINDOWS or (LINUX and not MACOS):
# FIXME not for disabled bridges
exts.append(".dll")



Loading…
Cancel
Save