Browse Source

LV2 UIs working, limited to Qt, X11 and External

tags/1.9.4
falkTX 11 years ago
parent
commit
67a8e3fff6
3 changed files with 582 additions and 32 deletions
  1. +0
    -18
      source/backend/plugin/CarlaPluginGui.cpp
  2. +0
    -1
      source/backend/plugin/CarlaPluginGui.hpp
  3. +582
    -13
      source/backend/plugin/Lv2Plugin.cpp

+ 0
- 18
source/backend/plugin/CarlaPluginGui.cpp View File

@@ -70,10 +70,6 @@ CarlaPluginGui::~CarlaPluginGui()
delete fContainer;
#endif
}
else
{
CARLA_ASSERT(fContainer == nullptr);
}
}

void CarlaPluginGui::setSize(const int width, const int height)
@@ -109,20 +105,6 @@ void CarlaPluginGui::setWidget(QWidget* const widget)
fContainer = widget;
}

void CarlaPluginGui::removeWidget()
{
CARLA_ASSERT(fContainer != nullptr);
carla_debug("CarlaPluginGui::removeWidget()");

if (fContainer == nullptr)
return;

fContainer->setParent(nullptr);
setCentralWidget(nullptr);

fContainer = nullptr;
}

void CarlaPluginGui::closeEvent(QCloseEvent* const event)
{
CARLA_ASSERT(event != nullptr);


+ 0
- 1
source/backend/plugin/CarlaPluginGui.hpp View File

@@ -57,7 +57,6 @@ public:

// Qt UIs
void setWidget(QWidget* widget);
void removeWidget();

protected:
void closeEvent(QCloseEvent* const event);


+ 582
- 13
source/backend/plugin/Lv2Plugin.cpp View File

@@ -354,15 +354,6 @@ public:
}
}

if (fUi.descriptor != nullptr)
{
if (fUi.descriptor->cleanup != nullptr && fUi.handle != nullptr)
fUi.descriptor->cleanup(fUi.handle);

fUi.handle = nullptr;
fUi.descriptor = nullptr;
}

if (fFeatures[kFeatureIdUiDataAccess] != nullptr && fFeatures[kFeatureIdUiDataAccess]->data != nullptr)
delete (LV2_Extension_Data_Feature*)fFeatures[kFeatureIdUiDataAccess]->data;

@@ -382,6 +373,9 @@ public:
delete uiHost;
}

fUi.descriptor = nullptr;
fUi.rdfDescriptor = nullptr;

kData->uiLibClose();
}

@@ -974,7 +968,7 @@ public:

// take some precautions
CARLA_ASSERT(fUi.descriptor != nullptr);
CARLA_ASSERT(fUi.rdfDescriptor == nullptr);
CARLA_ASSERT(fUi.rdfDescriptor != nullptr);

if (fUi.descriptor == nullptr)
return;
@@ -990,7 +984,6 @@ public:
}
else
{
CARLA_ASSERT(fUi.handle != nullptr);
CARLA_ASSERT(fUi.descriptor->cleanup != nullptr);

if (fUi.handle == nullptr)
@@ -1021,6 +1014,8 @@ public:
return;
}

updateUi();

LV2_EXTERNAL_UI_SHOW((LV2_External_UI_Widget*)fUi.widget);
}
else
@@ -1032,6 +1027,7 @@ public:

fUi.descriptor->cleanup(fUi.handle);
fUi.handle = nullptr;
fUi.widget = nullptr;
}
}
else // means PLUGIN_UI_PARENT || PLUGIN_UI_QT
@@ -1043,7 +1039,7 @@ public:
// TODO
CarlaPluginGui::Options guiOptions;
guiOptions.parented = (fUi.type == PLUGIN_UI_PARENT);
guiOptions.resizable = true;
guiOptions.resizable = isUiResizable();

kData->gui = new CarlaPluginGui(kData->engine, this, guiOptions);
}
@@ -1077,6 +1073,11 @@ public:
return;
}

if (fUi.type == PLUGIN_UI_QT)
kData->gui->setWidget((QWidget*)fUi.widget);

updateUi();

kData->gui->setWindowTitle(QString("%1 (GUI)").arg((const char*)fName));
kData->gui->show();
}
@@ -1084,6 +1085,7 @@ public:
{
fUi.descriptor->cleanup(fUi.handle);
fUi.handle = nullptr;
fUi.widget = nullptr;

if (kData->gui != nullptr)
{
@@ -1836,6 +1838,14 @@ public:
// plugin hints
fHints = 0x0;

if (fUi.type != PLUGIN_UI_NULL)
{
fHints |= PLUGIN_HAS_GUI;

if (fUi.type == PLUGIN_UI_QT || fUi.type == PLUGIN_UI_PARENT)
fHints |= PLUGIN_HAS_SINGLE_THREAD;
}

if (LV2_IS_GENERATOR(fRdfDescriptor->Type[0], fRdfDescriptor->Type[1]))
fHints |= PLUGIN_IS_SYNTH;

@@ -2878,6 +2888,126 @@ public:
// -------------------------------------------------------------------
// Post-poned UI Stuff

void uiParameterChange(const uint32_t index, const float value) override
{
CARLA_ASSERT(fDescriptor != nullptr);
CARLA_ASSERT(fHandle != nullptr);
CARLA_ASSERT(index < kData->param.count);

if (fDescriptor == nullptr || fHandle == nullptr)
return;
if (index >= kData->param.count)
return;

if (fUi.type == PLUGIN_UI_OSC)
{
if (kData->osc.data.target != nullptr)
osc_send_control(&kData->osc.data, kData->param.data[index].rindex, value);
}
else
{
if (fUi.handle != nullptr && fUi.descriptor != nullptr && fUi.descriptor->port_event != nullptr)
fUi.descriptor->port_event(fUi.handle, kData->param.data[index].rindex, sizeof(float), 0, &value);
}
}

void uiMidiProgramChange(const uint32_t index) override
{
CARLA_ASSERT(index < kData->midiprog.count);

if (index >= kData->midiprog.count)
return;

if (fUi.type == PLUGIN_UI_OSC)
{
if (kData->osc.data.target != nullptr)
osc_send_midi_program(&kData->osc.data, kData->midiprog.data[index].bank, kData->midiprog.data[index].program);
}
else
{
if (fExt.uiprograms != nullptr && fExt.uiprograms->select_program != nullptr)
fExt.uiprograms->select_program(fUi.handle, kData->midiprog.data[index].bank, kData->midiprog.data[index].program);
}
}

void uiNoteOn(const uint8_t channel, const uint8_t note, const uint8_t velo) override
{
CARLA_ASSERT(channel < MAX_MIDI_CHANNELS);
CARLA_ASSERT(note < MAX_MIDI_NOTE);
CARLA_ASSERT(velo > 0 && velo < MAX_MIDI_VALUE);

if (channel >= MAX_MIDI_CHANNELS)
return;
if (note >= MAX_MIDI_NOTE)
return;
if (velo >= MAX_MIDI_VALUE)
return;

if (fUi.type == PLUGIN_UI_OSC)
{
if (kData->osc.data.target != nullptr)
{
uint8_t midiData[4] = { 0 };
midiData[1] = MIDI_STATUS_NOTE_ON + channel;
midiData[2] = note;
midiData[3] = velo;
osc_send_midi(&kData->osc.data, midiData);
}
}
else
{
if (fUi.handle != nullptr && fUi.descriptor != nullptr && fUi.descriptor->port_event != nullptr)
{
LV2_Atom_MidiEvent midiEv;
midiEv.event.time.frames = 0;
midiEv.event.body.type = CARLA_URI_MAP_ID_MIDI_EVENT;
midiEv.event.body.size = 3;
midiEv.data[0] = MIDI_STATUS_NOTE_OFF + channel;
midiEv.data[1] = note;
midiEv.data[2] = velo;

fUi.descriptor->port_event(fUi.handle, 0, 3, CARLA_URI_MAP_ID_ATOM_TRANSFER_ATOM, &midiEv);
}
}
}

void uiNoteOff(const uint8_t channel, const uint8_t note) override
{
CARLA_ASSERT(channel < MAX_MIDI_CHANNELS);
CARLA_ASSERT(note < MAX_MIDI_NOTE);

if (channel >= MAX_MIDI_CHANNELS)
return;
if (note >= MAX_MIDI_NOTE)
return;

if (fUi.type == PLUGIN_UI_OSC)
{
if (kData->osc.data.target != nullptr)
{
uint8_t midiData[4] = { 0 };
midiData[1] = MIDI_STATUS_NOTE_OFF + channel;
midiData[2] = note;
osc_send_midi(&kData->osc.data, midiData);
}
}
else
{
if (fUi.handle != nullptr && fUi.descriptor != nullptr && fUi.descriptor->port_event != nullptr)
{
LV2_Atom_MidiEvent midiEv;
midiEv.event.time.frames = 0;
midiEv.event.body.type = CARLA_URI_MAP_ID_MIDI_EVENT;
midiEv.event.body.size = 3;
midiEv.data[0] = MIDI_STATUS_NOTE_OFF + channel;
midiEv.data[1] = note;
midiEv.data[2] = 0;

fUi.descriptor->port_event(fUi.handle, 0, 3, CARLA_URI_MAP_ID_ATOM_TRANSFER_ATOM, &midiEv);
}
}
}

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

protected:
@@ -3135,6 +3265,118 @@ protected:
return 0;
}

void handleUiWrite(const uint32_t rindex, const uint32_t bufferSize, const uint32_t format, const void* const buffer)
{
if (format == 0)
{
CARLA_ASSERT(buffer != nullptr);
CARLA_ASSERT(bufferSize == sizeof(float));

if (buffer == nullptr || bufferSize != sizeof(float))
return;

float value = *(float*)buffer;

for (uint32_t i=0; i < kData->param.count; ++i)
{
if (kData->param.data[i].rindex == static_cast<int32_t>(rindex))
return setParameterValue(i, value, false, true, true);
}
}
else if (format == CARLA_URI_MAP_ID_ATOM_TRANSFER_ATOM)
{
CARLA_ASSERT(buffer != nullptr);

if (buffer == nullptr)
return;

//const LV2_Atom* const atom = (const LV2_Atom*)buffer;
//handleTransferAtom(rindex, atom);
}
else if (format == CARLA_URI_MAP_ID_ATOM_TRANSFER_EVENT)
{
CARLA_ASSERT(buffer != nullptr);

if (buffer == nullptr)
return;

//const LV2_Atom* const atom = (const LV2_Atom*)buffer;
//handleTransferEvent(rindex, atom);
}
}

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

bool isUiBridgeable(const uint32_t uiId)
{
const LV2_RDF_UI& rdfUi(fRdfDescriptor->UIs[uiId]);

// Calf Analyzer is useless without instance-data
if (std::strcmp(rdfUi.URI, "http://calf.sourceforge.net/plugins/Analyzer") == 0)
return false;

for (uint32_t i=0; i < rdfUi.FeatureCount; ++i)
{
if (std::strcmp(rdfUi.Features[i].URI, LV2_INSTANCE_ACCESS_URI) == 0)
return false;
if (std::strcmp(rdfUi.Features[i].URI, LV2_DATA_ACCESS_URI) == 0)
return false;
}

return true;
}

bool isUiResizable()
{
for (uint32_t i=0; i < fUi.rdfDescriptor->FeatureCount; ++i)
{
if (std::strcmp(fUi.rdfDescriptor->Features[i].URI, LV2_UI__fixedSize) == 0)
return false;
if (std::strcmp(fUi.rdfDescriptor->Features[i].URI, LV2_UI__noUserResize) == 0)
return false;
}

return true;
}

void updateUi()
{
CARLA_ASSERT(fUi.handle != nullptr);
CARLA_ASSERT(fUi.descriptor != nullptr);

fExt.uiidle = nullptr;
fExt.uiprograms = nullptr;

if (fUi.descriptor->extension_data != nullptr)
{
fExt.uiidle = (const LV2UI_Idle_Interface*)fUi.descriptor->extension_data(LV2_UI__idleInterface);
fExt.uiprograms = (const LV2_Programs_UI_Interface*)fUi.descriptor->extension_data(LV2_PROGRAMS__UIInterface);

// check if invalid
if (fExt.uiidle != nullptr && fExt.uiidle->idle == nullptr)
fExt.uiidle = nullptr;

if (fExt.uiprograms != nullptr && fExt.uiprograms->select_program == nullptr)
fExt.uiprograms = nullptr;

// update midi program
if (fExt.uiprograms && kData->midiprog.count > 0 && kData->midiprog.current >= 0)
fExt.uiprograms->select_program(fUi.handle, kData->midiprog.data[kData->midiprog.current].bank,
kData->midiprog.data[kData->midiprog.current].program);
}

if (fUi.descriptor->port_event != nullptr)
{
// update control ports
float value;
for (uint32_t i=0; i < kData->param.count; ++i)
{
value = getParameterValue(i);
fUi.descriptor->port_event(fUi.handle, kData->param.data[i].rindex, sizeof(float), CARLA_URI_MAP_ID_NULL, &value);
}
}
}

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

public:
@@ -3477,6 +3719,333 @@ public:
if (fRdfDescriptor->UICount == 0)
return true;

// -----------------------------------------------------------
// find more appropriate ui

int eQt4, eQt5, eCocoa, eWindows, eX11, eGtk2, eGtk3, iCocoa, iWindows, iX11, iQt4, iQt5, iExt, iFinal;
eQt4 = eQt5 = eCocoa = eWindows = eX11 = eGtk2 = eGtk3 = iQt4 = iQt5 = iCocoa = iWindows = iX11 = iExt = iFinal = -1;

//#ifdef BUILD_BRIDGE
// const bool preferUiBridges(kData->engine->getOptions().preferUiBridges);
//#else
// const bool preferUiBridges(kData->engine->getOptions().preferUiBridges && (fHints & PLUGIN_IS_BRIDGE) == 0);
//#endif
// TODO
const bool preferUiBridges(false);

for (uint32_t i=0; i < fRdfDescriptor->UICount; ++i)
{
CARLA_ASSERT(fRdfDescriptor->UIs[i].URI != nullptr);

if (fRdfDescriptor->UIs[i].URI == nullptr)
{
carla_stderr("Plugin has an UI without a valid URI");
continue;
}

switch (fRdfDescriptor->UIs[i].Type)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
case LV2_UI_QT4:
if (isUiBridgeable(i))
eQt4 = i;
break;
#else
case LV2_UI_QT4:
if (isUiBridgeable(i) && preferUiBridges)
eQt4 = i;
iQt4 = i;
break;
#endif

#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
case LV2_UI_QT5:
if (isUiBridgeable(i) && preferUiBridges)
eQt5 = i;
iQt5 = i;
break;
#else
case LV2_UI_QT5:
if (isUiBridgeable(i) && preferUiBridges)
eQt5 = i;
break;
#endif

#ifdef CARLA_OS_MAC
case LV2_UI_COCOA:
if (isUiBridgeable(i) && preferUiBridges)
eCocoa = i;
iCocoa = i;
break;
#endif

#ifdef CARLA_OS_WIN
case LV2_UI_WINDOWS:
if (isUiBridgeable(i) && preferUiBridges)
eWindows = i;
iWindows = i;
break;
#endif

case LV2_UI_X11:
if (isUiBridgeable(i) && preferUiBridges)
eX11 = i;
#ifdef Q_WS_X11
iX11 = i;
#endif
break;

case LV2_UI_GTK2:
if (isUiBridgeable(i))
eGtk2 = i;
break;

case LV2_UI_GTK3:
if (isUiBridgeable(i))
eGtk3 = i;
break;

case LV2_UI_EXTERNAL:
case LV2_UI_OLD_EXTERNAL:
// Calf Analyzer is useless using external-ui
if (std::strcmp(fRdfDescriptor->URI, "http://calf.sourceforge.net/plugins/Analyzer") != 0)
iExt = i;
break;

default:
break;
}
}

if (eQt4 >= 0)
iFinal = eQt4;
else if (eQt5 >= 0)
iFinal = eQt5;
else if (eCocoa >= 0)
iFinal = eCocoa;
else if (eWindows >= 0)
iFinal = eWindows;
else if (eX11 >= 0)
iFinal = eX11;
else if (eGtk2 >= 0)
iFinal = eGtk2;
else if (eGtk3 >= 0)
iFinal = eGtk3;
else if (iQt4 >= 0)
iFinal = iQt4;
else if (iQt5 >= 0)
iFinal = iQt5;
else if (iCocoa >= 0)
iFinal = iCocoa;
else if (iWindows >= 0)
iFinal = iWindows;
else if (iX11 >= 0)
iFinal = iX11;
else if (iExt >= 0)
iFinal = iExt;

// TODO
const bool isBridged(false);

if (iFinal < 0)
{
carla_stderr("Failed to find an appropriate LV2 UI for this plugin");
return true;
}

fUi.rdfDescriptor = &fRdfDescriptor->UIs[iFinal];

// -----------------------------------------------------------
// check supported ui features

canContinue = true;

for (uint32_t i=0; i < fUi.rdfDescriptor->FeatureCount; ++i)
{
if (LV2_IS_FEATURE_REQUIRED(fUi.rdfDescriptor->Features[i].Type) && ! is_lv2_ui_feature_supported(fUi.rdfDescriptor->Features[i].URI))
{
carla_stderr2("Plugin UI requires a feature that is not supported:\n%s", fUi.rdfDescriptor->Features[i].URI);
canContinue = false;
break;
}
}

if (! canContinue)
{
fUi.rdfDescriptor = nullptr;
return true;
}

// -------------------------------------------------------
// open UI DLL

if (! kData->uiLibOpen(fUi.rdfDescriptor->Binary))
{
carla_stderr2("Could not load UI library, error was:\n%s", kData->libError(fUi.rdfDescriptor->Binary));
fUi.rdfDescriptor = nullptr;
return true;
}

// -------------------------------------------------------
// get UI DLL main entry

LV2UI_DescriptorFunction uiDescFn = (LV2UI_DescriptorFunction)kData->uiLibSymbol("lv2ui_descriptor");

if (uiDescFn == nullptr)
{
carla_stderr2("Could not find the LV2UI Descriptor in the UI library");
kData->uiLibClose();
fUi.rdfDescriptor = nullptr;
return true;
}

// -------------------------------------------------------
// get UI descriptor that matches UI URI

uint32_t i = 0;
while ((fUi.descriptor = uiDescFn(i++)))
{
if (std::strcmp(fUi.descriptor->URI, fUi.rdfDescriptor->URI) == 0)
break;
}

if (fUi.descriptor == nullptr)
{
carla_stderr2("Could not find the requested GUI in the plugin UI library");
kData->uiLibClose();
fUi.rdfDescriptor = nullptr;
return true;
}

// -----------------------------------------------------------
// initialize ui according to type

const LV2_Property uiType(fUi.rdfDescriptor->Type);

if (isBridged)
{
// -------------------------------------------------------
// initialize ui bridge

if (const char* const oscBinary = nullptr /*getUiBridgePath(uiType)*/)
{
fUi.type = PLUGIN_UI_OSC;
kData->osc.thread.setOscData(oscBinary, fDescriptor->URI, fUi.descriptor->URI);
}
}
else
{

// -------------------------------------------------------
// check if ui is usable

switch (uiType)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
case LV2_UI_QT5:
carla_debug("Will use LV2 Qt5 UI");
fUi.type = PLUGIN_UI_QT;
break;
#else
case LV2_UI_QT4:
carla_debug("Will use LV2 Qt4 UI");
fUi.type = PLUGIN_UI_QT;
break;
#endif

#ifdef CARLA_OS_MAC
case LV2_UI_COCOA:
carla_debug("Will use LV2 Cocoa UI");
fUi.type = PLUGIN_UI_PARENT;
break;
#endif

#ifdef CARLA_OS_WIN
case LV2_UI_WINDOWS:
carla_debug("Will use LV2 Windows UI");
fUi.type = PLUGIN_UI_PARENT;
break;
#endif

#ifdef Q_WS_X11
case LV2_UI_X11:
carla_debug("Will use LV2 X11 UI");
fUi.type = PLUGIN_UI_PARENT;
break;
#endif

case LV2_UI_GTK2:
carla_debug("Will use LV2 Gtk2 UI, NOT!");
break;

case LV2_UI_GTK3:
carla_debug("Will use LV2 Gtk3 UI, NOT!");
break;

case LV2_UI_EXTERNAL:
case LV2_UI_OLD_EXTERNAL:
carla_debug("Will use LV2 External UI");
fUi.type = PLUGIN_UI_EXTERNAL;
break;
}

if (fUi.type == PLUGIN_UI_NULL)
{
kData->uiLibClose();
fUi.descriptor = nullptr;
fUi.rdfDescriptor = nullptr;
return true;
}

// -------------------------------------------------------
// initialize ui features

QString guiTitle(QString("%1 (GUI)").arg((const char*)fName));

LV2_Extension_Data_Feature* const uiDataFt = new LV2_Extension_Data_Feature;
uiDataFt->data_access = fDescriptor->extension_data;

LV2UI_Port_Map* const uiPortMapFt = new LV2UI_Port_Map;
uiPortMapFt->handle = this;
uiPortMapFt->port_index = carla_lv2_ui_port_map;

LV2UI_Resize* const uiResizeFt = new LV2UI_Resize;
uiResizeFt->handle = this;
uiResizeFt->ui_resize = carla_lv2_ui_resize;

LV2_External_UI_Host* const uiExternalHostFt = new LV2_External_UI_Host;
uiExternalHostFt->ui_closed = carla_lv2_external_ui_closed;
uiExternalHostFt->plugin_human_id = carla_strdup(guiTitle.toUtf8().constData());

fFeatures[kFeatureIdUiDataAccess] = new LV2_Feature;
fFeatures[kFeatureIdUiDataAccess]->URI = LV2_DATA_ACCESS_URI;
fFeatures[kFeatureIdUiDataAccess]->data = uiDataFt;

fFeatures[kFeatureIdUiInstanceAccess] = new LV2_Feature;
fFeatures[kFeatureIdUiInstanceAccess]->URI = LV2_INSTANCE_ACCESS_URI;
fFeatures[kFeatureIdUiInstanceAccess]->data = fHandle;

fFeatures[kFeatureIdUiParent] = new LV2_Feature;
fFeatures[kFeatureIdUiParent]->URI = LV2_UI__parent;
fFeatures[kFeatureIdUiParent]->data = nullptr;

fFeatures[kFeatureIdUiPortMap] = new LV2_Feature;
fFeatures[kFeatureIdUiPortMap]->URI = LV2_UI__portMap;
fFeatures[kFeatureIdUiPortMap]->data = uiPortMapFt;

fFeatures[kFeatureIdUiResize] = new LV2_Feature;
fFeatures[kFeatureIdUiResize]->URI = LV2_UI__resize;
fFeatures[kFeatureIdUiResize]->data = uiResizeFt;

fFeatures[kFeatureIdExternalUi] = new LV2_Feature;
fFeatures[kFeatureIdExternalUi]->URI = LV2_EXTERNAL_UI__Host;
fFeatures[kFeatureIdExternalUi]->data = uiExternalHostFt;

fFeatures[kFeatureIdExternalUiOld] = new LV2_Feature;
fFeatures[kFeatureIdExternalUiOld]->URI = LV2_EXTERNAL_UI_DEPRECATED_URI;
fFeatures[kFeatureIdExternalUiOld]->data = uiExternalHostFt;
}

return true;
}

@@ -3922,7 +4491,7 @@ private:
if (controller == nullptr)
return;

//((Lv2Plugin*)handle)->handleUiWrite(port_index, buffer_size, format, buffer);
((Lv2Plugin*)controller)->handleUiWrite(port_index, buffer_size, format, buffer);
}

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


Loading…
Cancel
Save