From ca28780f45d6d2019062f21dcd0c6c8c106a0cf3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 13 Sep 2012 16:27:35 +0100 Subject: [PATCH] Carla: LV2 work for event messages (experimental) --- c++/carla-backend/lv2.cpp | 321 ++++++++++++++++++++++++++++++++------ 1 file changed, 272 insertions(+), 49 deletions(-) diff --git a/c++/carla-backend/lv2.cpp b/c++/carla-backend/lv2.cpp index 0f7aad8..8fdb9de 100644 --- a/c++/carla-backend/lv2.cpp +++ b/c++/carla-backend/lv2.cpp @@ -139,6 +139,7 @@ const uint32_t CARLA_URI_MAP_ID_COUNT = 18; struct Lv2EventData { uint32_t type; + uint32_t rindex; CarlaEngineMidiPort* port; union { LV2_Atom_Sequence* atom; @@ -148,6 +149,7 @@ struct Lv2EventData { Lv2EventData() : type(0), + rindex(0), port(nullptr) {} }; @@ -206,6 +208,173 @@ LV2_Atom_Event* getLv2AtomEvent(LV2_Atom_Sequence* const atom, const uint32_t of return (LV2_Atom_Event*)((char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, atom) + offset); } +class Lv2AtomQueue +{ +public: + Lv2AtomQueue() + { + index = indexPool = 0; + empty = true; + full = false; + + memset(dataPool, 0, sizeof(unsigned char)*MAX_POOL_SIZE); + } + + void copyDataFrom(Lv2AtomQueue* const queue) + { + // lock mutexes + queue->mutex.lock(); + mutex.lock(); + + // copy data from queue + memcpy(data, queue->data, sizeof(datatype)*MAX_SIZE); + memcpy(dataPool, queue->dataPool, sizeof(unsigned char)*MAX_POOL_SIZE); + index = queue->index; + indexPool = queue->indexPool; + empty = queue->empty; + full = queue->full; + + // unlock our mutex, no longer needed + mutex.unlock(); + + // reset queque + memset(queue->data, 0, sizeof(datatype)*MAX_SIZE); + memset(queue->dataPool, 0, sizeof(unsigned char)*MAX_POOL_SIZE); + queue->index = queue->indexPool = 0; + queue->empty = true; + queue->full = false; + + // unlock queque mutex + queue->mutex.unlock(); + } + + bool isEmpty() + { + return empty; + } + + bool isFull() + { + return full; + } + + void lock() + { + mutex.lock(); + } + + void unlock() + { + mutex.unlock(); + } + + void put(const uint32_t portIndex, const LV2_Atom& atom, const bool lock = true) + { + qDebug("Lv2AtomQueue::put(%i, size:%i, %s)", portIndex, atom.size, bool2str(lock)); + Q_ASSERT(atom.size > 0); + Q_ASSERT(indexPool + atom.size < MAX_POOL_SIZE); // overflow + + if (full || atom.size == 0 || indexPool + atom.size >= MAX_POOL_SIZE) + return; + + if (lock) + mutex.lock(); + + for (unsigned short i=0; i < MAX_SIZE; i++) + { + if (data[i].size == 0) + { + data[i].portIndex = portIndex; + data[i].size = atom.size; + data[i].poolOffset = indexPool; + memcpy(dataPool + indexPool, (const unsigned char*)LV2_ATOM_BODY_CONST(&atom), atom.size); + empty = false; + full = (i == MAX_SIZE-1); + indexPool += atom.size; + break; + } + } + + if (lock) + mutex.unlock(); + } + + bool get(uint32_t* const portIndex, LV2_Atom** const atom, const bool lock = true) + { + qDebug("Lv2AtomQueue::get(%p, %p, %s)", portIndex, atom, bool2str(lock)); + Q_ASSERT(portIndex && atom); + + if (empty || ! (portIndex && atom)) + return false; + + if (lock) + mutex.lock(); + + full = false; + + if (data[index].size == 0) + { + index = indexPool = 0; + empty = true; + + if (lock) + mutex.lock(); + + return false; + } + + static struct { + uint32_t size; + uint32_t type; + unsigned char* data; + } thisAtom; + thisAtom.size = data[index].size; + thisAtom.type = data[index].type; + thisAtom.data = dataPool + data[index].poolOffset; + + *portIndex = data[index].portIndex; + *atom = (LV2_Atom*)&thisAtom; + + data[index].portIndex = 0; + data[index].size = 0; + data[index].type = 0; + data[index].poolOffset = 0; + index++; + empty = false; + + if (lock) + mutex.unlock(); + + return true; + } + +private: + struct datatype { + size_t size; + LV2_URID type; + uint32_t portIndex; + uint32_t poolOffset; + + datatype() + : size(0), + type(CARLA_URI_MAP_ID_NULL), + portIndex(0), + poolOffset(0) {} + }; + + static const unsigned short MAX_SIZE = 32; + static const unsigned short MAX_POOL_SIZE = 8192; + + datatype data[MAX_SIZE]; + unsigned char dataPool[MAX_POOL_SIZE]; + + unsigned short index; + uint32_t indexPool; + bool empty, full; + + QMutex mutex; +}; + class Lv2Plugin : public CarlaPlugin { public: @@ -1018,9 +1187,36 @@ public: void idleGui() { - // Update external UI - if (gui.type == GUI_EXTERNAL_LV2 && ui.handle && ui.descriptor && ui.widget) - LV2_EXTERNAL_UI_RUN((LV2_External_UI_Widget*)ui.widget); + const bool haveUI = (gui.type == GUI_EXTERNAL_OSC && osc.data.target) || (ui.handle && ui.descriptor); + + if (haveUI) + { + // Update event ports + static Lv2AtomQueue queue; + queue.copyDataFrom(&atomQueueOut); + + uint32_t portIndex; + LV2_Atom* atom; + + while (atomQueueIn.get(&portIndex, &atom, false)) + { +#ifndef BUILD_BRIDGE + if (gui.type == GUI_EXTERNAL_OSC) + { + osc_send_lv2_transfer_event(&osc.data, nullptr, nullptr); + } + else +#endif + { + if (ui.descriptor->port_event) + ui.descriptor->port_event(ui.handle, portIndex, atom->size, CARLA_URI_MAP_ID_ATOM_TRANSFER_EVENT, atom); + } + } + + // Update external UI + if (gui.type == GUI_EXTERNAL_LV2 && ui.widget) + LV2_EXTERNAL_UI_RUN((LV2_External_UI_Widget*)ui.widget); + } CarlaPlugin::idleGui(); } @@ -1283,6 +1479,8 @@ public: descriptor->connect_port(handle, i, evIn.data[j].atom); if (h2) descriptor->connect_port(h2, i, evIn.data[j].atom); + evIn.data[j].rindex = i; + if (portType & LV2_PORT_SUPPORTS_MIDI_EVENT) { evIn.data[j].type |= CARLA_EVENT_TYPE_MIDI; @@ -1299,6 +1497,8 @@ public: descriptor->connect_port(handle, i, evOut.data[j].atom); if (h2) descriptor->connect_port(h2, i, evOut.data[j].atom); + evOut.data[j].rindex = i; + if (portType & LV2_PORT_SUPPORTS_MIDI_EVENT) { evOut.data[j].type |= CARLA_EVENT_TYPE_MIDI; @@ -1320,6 +1520,8 @@ public: descriptor->connect_port(handle, i, evIn.data[j].event); if (h2) descriptor->connect_port(h2, i, evIn.data[j].event); + evIn.data[j].rindex = i; + if (portType & LV2_PORT_SUPPORTS_MIDI_EVENT) { evIn.data[j].type |= CARLA_EVENT_TYPE_MIDI; @@ -1332,6 +1534,8 @@ public: descriptor->connect_port(handle, i, evOut.data[j].event); if (h2) descriptor->connect_port(h2, i, evOut.data[j].event); + evOut.data[j].rindex = i; + if (portType & LV2_PORT_SUPPORTS_MIDI_EVENT) { evOut.data[j].type |= CARLA_EVENT_TYPE_MIDI; @@ -1349,8 +1553,9 @@ public: descriptor->connect_port(handle, i, evIn.data[j].midi); if (h2) descriptor->connect_port(h2, i, evIn.data[j].midi); - evIn.data[j].type |= CARLA_EVENT_TYPE_MIDI; - evIn.data[j].port = (CarlaEngineMidiPort*)x_client->addPort(CarlaEnginePortTypeMIDI, portName, true); + evIn.data[j].type |= CARLA_EVENT_TYPE_MIDI; + evIn.data[j].port = (CarlaEngineMidiPort*)x_client->addPort(CarlaEnginePortTypeMIDI, portName, true); + evIn.data[j].rindex = i; } else if (LV2_IS_PORT_OUTPUT(portType)) { @@ -1358,8 +1563,9 @@ public: descriptor->connect_port(handle, i, evOut.data[j].midi); if (h2) descriptor->connect_port(h2, i, evOut.data[j].midi); - evOut.data[j].type |= CARLA_EVENT_TYPE_MIDI; - evOut.data[j].port = (CarlaEngineMidiPort*)x_client->addPort(CarlaEnginePortTypeMIDI, portName, false); + evOut.data[j].type |= CARLA_EVENT_TYPE_MIDI; + evOut.data[j].port = (CarlaEngineMidiPort*)x_client->addPort(CarlaEnginePortTypeMIDI, portName, false); + evOut.data[j].rindex = i; } else qWarning("WARNING - Got a broken Port (Midi, but not input or output)"); @@ -2225,13 +2431,24 @@ public: } #endif - // TODO - get messages from ringbuffer + atomQueueIn.lock(); + + if (! atomQueueIn.isEmpty()) + { + uint32_t portIndex; + LV2_Atom* atom; + + while (atomQueueIn.get(&portIndex, &atom, false)) + { + LV2_Atom_Event* const aev = getLv2AtomEvent(evIn.data[i].atom, evInAtomOffsets[i]); + aev->time.frames = framesOffset; + aev->body.type = CARLA_URI_MAP_ID_ATOM_TRANSFER_EVENT; + aev->body.size = atom->size; + memcpy(LV2_ATOM_BODY(&aev->body), LV2_ATOM_BODY(atom), atom->size); + } + } - //LV2_Atom_Event* const aev = getLv2AtomEvent(evIn.data[i].atom, evInAtomOffsets[i]); - //aev->time.frames = framesOffset; - //aev->body.type = CARLA_URI_MAP_ID_ATOM_TRANSFER_EVENT; - //aev->body.size = 0; // message size - //memcpy(LV2_ATOM_BODY(&aev->body), nullptr /* message data body */, 0 /* message size */); + atomQueueIn.unlock(); } } @@ -2525,12 +2742,12 @@ public: if (evOut.count > 0 && m_active) { - // ---------------------------------------------------------------------------------------------------- - // MIDI Output + atomQueueOut.lock(); for (i=0; i < evOut.count; i++) { - if (! evOut.data[i].port) + // midi events need the midi port to send events to + if ((evOut.data[i].type & CARLA_EVENT_TYPE_MIDI) > 0 && ! evOut.data[i].port) continue; if (evOut.data[i].type & CARLA_EVENT_DATA_ATOM) @@ -2550,6 +2767,11 @@ public: const unsigned char* const data = (unsigned char*)LV2_ATOM_BODY(&aev->body); evOut.data[i].port->writeEvent(aev->time.frames, data, aev->body.size); } + else if (aev->body.type == CARLA_URI_MAP_ID_ATOM_TRANSFER_EVENT) + { + if (! atomQueueOut.isFull()) + atomQueueOut.put(evOut.data[i].rindex, aev->body); + } offset += lv2_atom_pad_size(sizeof(LV2_Atom_Event) + aev->body.size); } @@ -2588,10 +2810,7 @@ public: } } - // ---------------------------------------------------------------------------------------------------- - // Message Output - - // TODO + atomQueueOut.unlock(); } // End of Event Output @@ -2856,20 +3075,18 @@ public: // ------------------------------------------------------------------- - void handleTransferAtom(const LV2_Atom* const atom, const char* const stype) + void handleTransferAtom(const uint32_t portIndex, const LV2_Atom* const atom) { - qDebug("Lv2Plugin::handleAtomTransfer(%p, \"%s\")", atom, stype); + qDebug("Lv2Plugin::handleAtomTransfer(%i, %p)", portIndex, atom); Q_ASSERT(atom); - Q_ASSERT(stype); // TODO } - void handleTransferEvent(const LV2_Atom* const atom, const char* const stype) + void handleTransferEvent(const uint32_t portIndex, const LV2_Atom* const atom) { - qDebug("Lv2Plugin::handleEventTransfer(%p, \"%s\")", atom, stype); + qDebug("Lv2Plugin::handleEventTransfer(%i, %p)", portIndex, atom); Q_ASSERT(atom); - Q_ASSERT(stype); const LV2_URID uridAtomBlank = getCustomURID(LV2_ATOM__Blank); const LV2_URID uridPatchBody = getCustomURID(LV2_PATCH__body); @@ -2877,7 +3094,7 @@ public: if (atom->type != uridAtomBlank) { - qWarning("Lv2Plugin::handleEventTransfer() - Not blank"); + qWarning("Lv2Plugin::handleEventTransfer() - not blank"); return; } @@ -2885,7 +3102,7 @@ public: if (obj->body.otype != uridPatchSet) { - qWarning("Lv2Plugin::handleEventTransfer() - Not Patch Set"); + qWarning("Lv2Plugin::handleEventTransfer() - not Patch:Set"); return; } @@ -2894,12 +3111,14 @@ public: if (! body) { - qWarning("Lv2Plugin::handleEventTransfer() - Has no body"); + qWarning("Lv2Plugin::handleEventTransfer() - has no body"); return; } LV2_ATOM_OBJECT_FOREACH(body, iter) { + atomQueueIn.put(portIndex, iter->value); +#if 0 CustomDataType dtype = CUSTOM_DATA_INVALID; const char* const key = getCustomURIString(iter->key); const char* value = nullptr; @@ -2921,6 +3140,7 @@ public: setCustomData(dtype, key, value, false); free((void*)value); +#endif } } @@ -3155,20 +3375,20 @@ public: Q_ASSERT(buffer); const LV2_Atom* const atom = (const LV2_Atom*)buffer; - handleTransferAtom(atom, getCustomURIString(atom->type)); + handleTransferAtom(rindex, atom); - if (ui.handle && ui.descriptor && ui.descriptor->port_event) - ui.descriptor->port_event(ui.handle, 0, atom->size, CARLA_URI_MAP_ID_ATOM_TRANSFER_ATOM, atom); + //if (ui.handle && ui.descriptor && ui.descriptor->port_event) + // ui.descriptor->port_event(ui.handle, 0, atom->size, CARLA_URI_MAP_ID_ATOM_TRANSFER_ATOM, atom); } else if (format == CARLA_URI_MAP_ID_ATOM_TRANSFER_EVENT) { Q_ASSERT(buffer); const LV2_Atom* const atom = (const LV2_Atom*)buffer; - handleTransferEvent(atom, getCustomURIString(atom->type)); + handleTransferEvent(rindex, atom); - if (ui.handle && ui.descriptor && ui.descriptor->port_event) - ui.descriptor->port_event(ui.handle, 0, atom->size, CARLA_URI_MAP_ID_ATOM_TRANSFER_EVENT, atom); + //if (ui.handle && ui.descriptor && ui.descriptor->port_event) + // ui.descriptor->port_event(ui.handle, 0, atom->size, CARLA_URI_MAP_ID_ATOM_TRANSFER_EVENT, atom); } } @@ -4343,6 +4563,8 @@ private: } suil; #endif + Lv2AtomQueue atomQueueIn; + Lv2AtomQueue atomQueueOut; Lv2PluginEventData evIn; Lv2PluginEventData evOut; float* paramBuffers; @@ -4404,16 +4626,17 @@ int CarlaOsc::handle_lv2_atom_transfer(CARLA_OSC_HANDLE_ARGS2) qDebug("CarlaOsc::handle_lv2_atom_transfer()"); CARLA_OSC_CHECK_OSC_TYPES(2, "ss"); - const char* const type = (const char*)&argv[0]->s; - const char* const value = (const char*)&argv[1]->s; + //const char* const type = (const char*)&argv[0]->s; + //const char* const value = (const char*)&argv[1]->s; + + //QByteArray chunk; + //chunk = QByteArray::fromBase64(value); - QByteArray chunk; - chunk = QByteArray::fromBase64(value); + //const LV2_Atom* const atom = (LV2_Atom*)chunk.constData(); - const LV2_Atom* const atom = (LV2_Atom*)chunk.constData(); + //CarlaBackend::Lv2Plugin* const lv2plugin = (CarlaBackend::Lv2Plugin*)plugin; + //lv2plugin->handleTransferAtom(atom, type); - CarlaBackend::Lv2Plugin* const lv2plugin = (CarlaBackend::Lv2Plugin*)plugin; - lv2plugin->handleTransferAtom(atom, type); return 0; } @@ -4423,16 +4646,16 @@ int CarlaOsc::handle_lv2_event_transfer(CARLA_OSC_HANDLE_ARGS2) qDebug("CarlaOsc::handle_lv2_event_transfer()"); CARLA_OSC_CHECK_OSC_TYPES(2, "ss"); - const char* const type = (const char*)&argv[0]->s; - const char* const value = (const char*)&argv[1]->s; + //const char* const type = (const char*)&argv[0]->s; + //const char* const value = (const char*)&argv[1]->s; - QByteArray chunk; - chunk = QByteArray::fromBase64(value); + //QByteArray chunk; + //chunk = QByteArray::fromBase64(value); - const LV2_Atom* const atom = (LV2_Atom*)chunk.constData(); + //const LV2_Atom* const atom = (LV2_Atom*)chunk.constData(); - CarlaBackend::Lv2Plugin* const lv2plugin = (CarlaBackend::Lv2Plugin*)plugin; - lv2plugin->handleTransferEvent(atom, type); + //CarlaBackend::Lv2Plugin* const lv2plugin = (CarlaBackend::Lv2Plugin*)plugin; + //lv2plugin->handleTransferEvent(atom, type); return 0; }