Browse Source

More work for native to lv2, UIs working now

tags/1.9.4
falkTX 11 years ago
parent
commit
475e54b91c
5 changed files with 325 additions and 126 deletions
  1. +1
    -0
      .gitignore
  2. +1
    -1
      source/plugin/Makefile
  3. +7
    -1
      source/plugin/carla-native-export.cpp
  4. +315
    -124
      source/plugin/carla-native-plugin.cpp
  5. +1
    -0
      source/plugin/carla-native.lv2/resources

+ 1
- 0
.gitignore View File

@@ -67,6 +67,7 @@ carla-native-export

data/windows/Carla
data/windows/CarlaControl
source/plugin/carla-native.lv2/*.ttl
source/tests/ANSI
source/tests/CarlaString
source/tests/DGL1


+ 1
- 1
source/plugin/Makefile View File

@@ -118,7 +118,7 @@ all: $(TARGETS)

clean:
rm -f *.o
rm -f carla-native.lv2/*
rm -f carla-native.lv2/*.*

debug:
$(MAKE) DEBUG=true


+ 7
- 1
source/plugin/carla-native-export.cpp View File

@@ -134,6 +134,11 @@ void writeManifestFile()
text += " a <" LV2_EXTERNAL_UI__Widget "> ;\n";
text += " ui:binary <carla-native" PLUGIN_EXT "> ;\n";
text += " lv2:requiredFeature <" LV2_INSTANCE_ACCESS_URI "> .\n";
text += "\n";
text += "<http://kxstudio.sf.net/carla#UIold>\n";
text += " a <" LV2_EXTERNAL_UI_DEPRECATED_URI "> ;\n";
text += " ui:binary <carla-native" PLUGIN_EXT "> ;\n";
text += " lv2:requiredFeature <" LV2_INSTANCE_ACCESS_URI "> .\n";

// -------------------------------------------------------------------
// Write file now
@@ -219,7 +224,8 @@ void writePluginFile(const PluginDescriptor* const pluginDesc)

if (pluginDesc->hints & PLUGIN_HAS_GUI)
{
text += " ui:ui <http://kxstudio.sf.net/carla#UI> ;\n";
text += " ui:ui <http://kxstudio.sf.net/carla#UI> ,\n";
text += " <http://kxstudio.sf.net/carla#UIold> ;\n";
text += "\n";
}



+ 315
- 124
source/plugin/carla-native-plugin.cpp View File

@@ -21,9 +21,11 @@

#include "lv2/atom.h"
#include "lv2/buf-size.h"
#include "lv2/instance-access.h"
#include "lv2/options.h"
#include "lv2/state.h"
#include "lv2/ui.h"
#include "lv2/lv2_external_ui.h"

#include <QtCore/Qt>

@@ -36,90 +38,113 @@
// -----------------------------------------------------------------------
// LV2 descriptor functions

class NativePlugin
class NativePlugin : public LV2_External_UI_Widget
{
public:
static const uint32_t kMaxMidiEvents = 512;

NativePlugin(const PluginDescriptor* const desc, const double sampleRate, const char* const bundlePath, const LV2_Feature* const* features)
: fHandle(nullptr),
fDescriptor(desc),
fIsProcessing(false),
fIsUiVisible(false),
fMidiEventCount(0),
fBufferSize(0),
fSampleRate(sampleRate),
fFeatures(features)
{
fHost.handle = this;
fHost.resourceDir = carla_strdup(bundlePath);
fHost.uiName = nullptr;

fHost.get_buffer_size = carla_host_get_buffer_size;
fHost.get_sample_rate = carla_host_get_sample_rate;
fHost.is_offline = carla_host_is_offline;
fHost.get_time_info = carla_host_get_time_info;
fHost.write_midi_event = carla_host_write_midi_event;
fHost.ui_parameter_changed = carla_host_ui_parameter_changed;
fHost.ui_custom_data_changed = carla_host_ui_custom_data_changed;
fHost.ui_closed = carla_host_ui_closed;
fHost.ui_open_file = carla_host_ui_open_file;
fHost.ui_save_file = carla_host_ui_save_file;
fHost.dispatcher = carla_host_dispatcher;

const LV2_Options_Option* options = nullptr;
const LV2_URID_Map* uridMap = nullptr;

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

if (uridMap == nullptr || options == nullptr)
return;

for (int i=0; options[i].key != 0; ++i)
{
if (options[i].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength))
{
if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Int))
fBufferSize = *(const int*)options[i].value;
else
carla_stderr("Host provides maxBlockLength but has wrong value type");
break;
}
}
}

~NativePlugin()
{
CARLA_ASSERT(fHandle != nullptr);

if (fHost.resourceDir != nullptr)
{
delete[] fHost.resourceDir;
fHost.resourceDir = nullptr;
}
}

bool init()
{
if (fDescriptor->instantiate == nullptr || fDescriptor->process == nullptr || fBufferSize == 0)
return false;

fHandle = fDescriptor->instantiate(&fHost);

if (fHandle == nullptr)
return false;

carla_zeroStruct<MidiEvent>(fMidiEvents, kMaxMidiEvents*2);
carla_zeroStruct<TimeInfo>(fTimeInfo);
fPorts.init(fDescriptor, fHandle);
return true;
}
static const uint32_t kMaxMidiEvents = 512;

NativePlugin(const PluginDescriptor* const desc, const double sampleRate, const char* const bundlePath, const LV2_Feature* const* features)
: fHandle(nullptr),
fDescriptor(desc),
fMidiEventCount(0),
fIsProcessing(false),
fBufferSize(0),
fSampleRate(sampleRate)
{
run = extui_run;
show = extui_show;
hide = extui_hide;

fHost.handle = this;
fHost.resourceDir = carla_strdup(bundlePath);
fHost.uiName = nullptr;

fHost.get_buffer_size = host_get_buffer_size;
fHost.get_sample_rate = host_get_sample_rate;
fHost.is_offline = host_is_offline;
fHost.get_time_info = host_get_time_info;
fHost.write_midi_event = host_write_midi_event;
fHost.ui_parameter_changed = host_ui_parameter_changed;
fHost.ui_custom_data_changed = host_ui_custom_data_changed;
fHost.ui_closed = host_ui_closed;
fHost.ui_open_file = host_ui_open_file;
fHost.ui_save_file = host_ui_save_file;
fHost.dispatcher = host_dispatcher;

const LV2_Options_Option* options = nullptr;
const LV2_URID_Map* uridMap = nullptr;

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

if (options == nullptr || uridMap == nullptr)
{
carla_stderr("Host don't provides option or urid-map features");
return;
}

fBufferSize = 1024;

for (int i=0; options[i].key != 0; ++i)
{
if (options[i].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength))
{
if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Int))
fBufferSize = *(const int*)options[i].value;
else
carla_stderr("Host provides maxBlockLength but has wrong value type");
break;
}
}

fUridMap = uridMap;

fUI.offset += (desc->midiIns > 0) ? desc->midiIns : 1;
fUI.offset += desc->midiOuts;
fUI.offset += 1; // freewheel
fUI.offset += desc->audioIns;
fUI.offset += desc->audioOuts;
}

~NativePlugin()
{
CARLA_ASSERT(fHandle == nullptr);

if (fHost.resourceDir != nullptr)
{
delete[] fHost.resourceDir;
fHost.resourceDir = nullptr;
}
}

bool init()
{
if (fDescriptor->instantiate == nullptr || fDescriptor->process == nullptr)
{
carla_stderr("Plugin is missing something...");
return false;
}
if (fBufferSize == 0)
{
carla_stderr("Plugin is missing bufferSize value");
return false;
}

fHandle = fDescriptor->instantiate(&fHost);

if (fHandle == nullptr)
return false;

carla_zeroStruct<MidiEvent>(fMidiEvents, kMaxMidiEvents*2);
carla_zeroStruct<TimeInfo>(fTimeInfo);
fPorts.init(fDescriptor, fHandle);
return true;
}

// -------------------------------------------------------------------
// LV2 functions
@@ -145,6 +170,7 @@ public:
{
if (fDescriptor->cleanup != nullptr)
fDescriptor->cleanup(fHandle);
fHandle = nullptr;
}

void lv2_run(const uint32_t frames)
@@ -178,7 +204,84 @@ public:

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

void lv2ui_instantiate(LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget,
const LV2_Feature* const* features)
{
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;
break;
}
}

if (fUI.host != nullptr)
fHost.uiName = fUI.host->plugin_human_id;

fUI.writeFunction = writeFunction;
fUI.controller = controller;
*widget = this;
}

void lv2ui_port_event(uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer)
{
if (format != 0 || bufferSize != sizeof(float) || buffer == nullptr)
return;
if (portIndex >= fUI.offset || ! fUI.isVisible)
return;
if (fDescriptor->ui_set_parameter_value == nullptr)
return;

const float value(*(const float*)buffer);
fDescriptor->ui_set_parameter_value(fHandle, portIndex-fUI.offset, value);
}

void lv2ui_cleanup()
{
fUI.host = nullptr;
fUI.writeFunction = nullptr;
fUI.controller = nullptr;

if (! fUI.isVisible)
return;

if (fDescriptor->ui_show != nullptr)
fDescriptor->ui_show(fHandle, false);

fUI.isVisible = false;
}

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

protected:
void handleUiRun()
{
// TODO - idle Qt if needed

if (fDescriptor->ui_idle != nullptr)
fDescriptor->ui_idle(fHandle);
}

void handleUiShow()
{
if (fDescriptor->ui_show != nullptr)
fDescriptor->ui_show(fHandle, true);

fUI.isVisible = true;
}

void handleUiHide()
{
if (fDescriptor->ui_show != nullptr)
fDescriptor->ui_show(fHandle, false);

fUI.isVisible = false;
}

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

uint32_t handleGetBufferSize()
{
return fBufferSize;
@@ -191,13 +294,15 @@ protected:

bool handleIsOffline()
{
CARLA_ASSERT(fIsProcessing);
return (fPorts.freewheel != nullptr && *fPorts.freewheel < 0.5f);
CARLA_SAFE_ASSERT_RETURN(fIsProcessing, false);

return (fPorts.freewheel != nullptr && *fPorts.freewheel > 0.5f);
}

const TimeInfo* handleGetTimeInfo()
{
CARLA_ASSERT(fIsProcessing);
CARLA_SAFE_ASSERT_RETURN(fIsProcessing, nullptr);

return &fTimeInfo;
}

@@ -224,20 +329,26 @@ protected:
return true;
}

void handleUiParameterChanged(const uint32_t /*index*/, const float /*value*/)
void handleUiParameterChanged(const uint32_t index, const float value)
{
//setParameterValue(index, value, false, true, true);
if (fUI.writeFunction != nullptr && fUI.controller != nullptr)
fUI.writeFunction(fUI.controller, index+fUI.offset, sizeof(float), 0, &value);
}

void handleUiCustomDataChanged(const char* const /*key*/, const char* const /*value*/)
{
//setCustomData(CUSTOM_DATA_STRING, key, value, false);
//storeCustomData(key, value);
}

void handleUiClosed()
{
// call external ui close
fIsUiVisible = false;
if (fUI.host != nullptr && fUI.host->ui_closed != nullptr && fUI.controller != nullptr)
fUI.host->ui_closed(fUI.controller);

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

const char* handleUiOpenFile(const bool isDir, const char* const title, const char* const filter)
@@ -287,21 +398,15 @@ protected:
break;
case HOST_OPCODE_GET_PARAMETER_MIDI_CC:
case HOST_OPCODE_SET_PARAMETER_MIDI_CC:
break;
case HOST_OPCODE_SET_PROCESS_PRECISION:
break;
case HOST_OPCODE_UPDATE_PARAMETER:
break;
case HOST_OPCODE_UPDATE_MIDI_PROGRAM:
break;
case HOST_OPCODE_RELOAD_PARAMETERS:
break;
case HOST_OPCODE_RELOAD_MIDI_PROGRAMS:
break;
case HOST_OPCODE_RELOAD_ALL:
break;
case HOST_OPCODE_UI_UNAVAILABLE:
//kData->engine->callback(CALLBACK_SHOW_GUI, fId, -1, 0, 0.0f, nullptr);
handleUiClosed();
break;
}

@@ -335,19 +440,32 @@ private:
HostDescriptor fHost;
const PluginDescriptor* const fDescriptor;

bool fIsProcessing;
bool fIsUiVisible;

uint32_t fMidiEventCount;
MidiEvent fMidiEvents[kMaxMidiEvents*2];
TimeInfo fTimeInfo;

TimeInfo fTimeInfo;
bool fIsProcessing;

// Lv2 host data
uint32_t fBufferSize;
double fSampleRate;

const LV2_Feature* const* fFeatures;
const LV2_URID_Map* fUridMap;

struct UI {
const LV2_External_UI_Host* host;
LV2UI_Write_Function writeFunction;
LV2UI_Controller controller;
bool isVisible;
uint32_t offset;

UI()
: host(nullptr),
writeFunction(nullptr),
controller(nullptr),
isVisible(false),
offset(0) {}
} fUI;

struct Ports {
LV2_Atom_Sequence** eventsIn;
@@ -531,59 +649,80 @@ private:

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

#define handlePtr ((NativePlugin*)_this_)

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

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

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

#undef handlePtr

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

#define handlePtr ((NativePlugin*)handle)

static uint32_t carla_host_get_buffer_size(::HostHandle handle)
static uint32_t host_get_buffer_size(HostHandle handle)
{
return handlePtr->handleGetBufferSize();
}

static double carla_host_get_sample_rate(::HostHandle handle)
static double host_get_sample_rate(HostHandle handle)
{
return handlePtr->handleGetSampleRate();
}

static bool carla_host_is_offline(::HostHandle handle)
static bool host_is_offline(HostHandle handle)
{
return handlePtr->handleIsOffline();
}

static const ::TimeInfo* carla_host_get_time_info(::HostHandle handle)
static const TimeInfo* host_get_time_info(HostHandle handle)
{
return handlePtr->handleGetTimeInfo();
}

static bool carla_host_write_midi_event(::HostHandle handle, const ::MidiEvent* event)
static bool host_write_midi_event(HostHandle handle, const ::MidiEvent* event)
{
return handlePtr->handleWriteMidiEvent(event);
}

static void carla_host_ui_parameter_changed(::HostHandle handle, uint32_t index, float value)
static void host_ui_parameter_changed(HostHandle handle, uint32_t index, float value)
{
handlePtr->handleUiParameterChanged(index, value);
}

static void carla_host_ui_custom_data_changed(::HostHandle handle, const char* key, const char* value)
static void host_ui_custom_data_changed(HostHandle handle, const char* key, const char* value)
{
handlePtr->handleUiCustomDataChanged(key, value);
}

static void carla_host_ui_closed(::HostHandle handle)
static void host_ui_closed(HostHandle handle)
{
handlePtr->handleUiClosed();
}

static const char* carla_host_ui_open_file(::HostHandle handle, bool isDir, const char* title, const char* filter)
static const char* host_ui_open_file(HostHandle handle, bool isDir, const char* title, const char* filter)
{
return handlePtr->handleUiOpenFile(isDir, title, filter);
}

static const char* carla_host_ui_save_file(::HostHandle handle, bool isDir, const char* title, const char* filter)
static const char* host_ui_save_file(HostHandle handle, bool isDir, const char* title, const char* filter)
{
return handlePtr->handleUiSaveFile(isDir, title, filter);
}

static intptr_t carla_host_dispatcher(::HostHandle handle, ::HostDispatcherOpcode opcode, int32_t index, intptr_t value, void* ptr, float opt)
static intptr_t host_dispatcher(HostHandle handle, HostDispatcherOpcode opcode, int32_t index, intptr_t value, void* ptr, float opt)
{
return handlePtr->handleDispatcher(opcode, index, value, ptr, opt);
}
@@ -724,6 +863,50 @@ static const void* lv2_extension_data(const char* uri)
// -----------------------------------------------------------------------
// Startup code

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)
{
NativePlugin* plugin = nullptr;

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

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

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

return (LV2UI_Handle)plugin;
}

#define uiPtr ((NativePlugin*)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();
}

#undef uiPtr

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

CARLA_EXPORT const LV2_Descriptor* lv2_descriptor(uint32_t index)
{
carla_debug("lv2_descriptor(%i)", index);
@@ -775,23 +958,31 @@ CARLA_EXPORT const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
{
carla_debug("lv2ui_descriptor(%i)", index);

//if (index != 0)
return nullptr;

#if 0
static const LV2UI_Descriptor lv2UiDesc = {
/* URI */ "http://kxstudio.sf.net/carla#UI",
/* instantiate */ lv2ui_instantiate,
/* connect_port */ nullptr,
/* activate */ nullptr,
/* run */ nullptr,
/* deactivate */ nullptr,
/* cleanup */ nullptr,
/* cleanup */ lv2ui_cleanup,
/* port_event */ lv2ui_port_event,
/* extension_data */ nullptr
};

return &lv2UiDesc;
#endif
static const LV2UI_Descriptor lv2UiDescOld = {
/* URI */ "http://kxstudio.sf.net/carla#UIold",
/* instantiate */ lv2ui_instantiate,
/* cleanup */ lv2ui_cleanup,
/* port_event */ lv2ui_port_event,
/* extension_data */ nullptr
};

switch (index)
{
case 0:
return &lv2UiDesc;
case 1:
return &lv2UiDescOld;
default:
return nullptr;
}
}

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

+ 1
- 0
source/plugin/carla-native.lv2/resources View File

@@ -0,0 +1 @@
../../backend/resources/

Loading…
Cancel
Save