/* * Carla Bridge UI, LV2 version * Copyright (C) 2011-2013 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * For a full copy of the GNU General Public License see the GPL.txt file */ #ifdef BRIDGE_LV2 #include "CarlaBridgeClient.hpp" #include "CarlaLv2Utils.hpp" #include "CarlaMIDI.h" extern "C" { #include "rtmempool/rtmempool-lv2.h" } #include #include Lv2WorldClass gLv2World; CARLA_BRIDGE_START_NAMESPACE // ------------------------------------------------------------------------- // fake values uint32_t bufferSize = 512; double sampleRate = 44100.0; // static max values const unsigned int MAX_EVENT_BUFFER = 8192; // 0x2000 // feature ids const uint32_t lv2_feature_id_bufsize_bounded = 0; const uint32_t lv2_feature_id_bufsize_fixed = 1; const uint32_t lv2_feature_id_bufsize_powerof2 = 2; const uint32_t lv2_feature_id_event = 3; const uint32_t lv2_feature_id_logs = 4; const uint32_t lv2_feature_id_options = 5; const uint32_t lv2_feature_id_programs = 6; const uint32_t lv2_feature_id_rtmempool = 7; const uint32_t lv2_feature_id_state_make_path = 8; const uint32_t lv2_feature_id_state_map_path = 9; const uint32_t lv2_feature_id_strict_bounds = 10; const uint32_t lv2_feature_id_uri_map = 11; const uint32_t lv2_feature_id_urid_map = 12; const uint32_t lv2_feature_id_urid_unmap = 13; const uint32_t lv2_feature_id_ui_parent = 14; const uint32_t lv2_feature_id_ui_port_map = 15; const uint32_t lv2_feature_id_ui_resize = 16; const uint32_t lv2_feature_count = 17; // pre-set uri[d] map ids const uint32_t CARLA_URI_MAP_ID_NULL = 0; const uint32_t CARLA_URI_MAP_ID_ATOM_CHUNK = 1; const uint32_t CARLA_URI_MAP_ID_ATOM_DOUBLE = 2; const uint32_t CARLA_URI_MAP_ID_ATOM_INT = 3; const uint32_t CARLA_URI_MAP_ID_ATOM_PATH = 4; const uint32_t CARLA_URI_MAP_ID_ATOM_SEQUENCE = 5; const uint32_t CARLA_URI_MAP_ID_ATOM_STRING = 6; const uint32_t CARLA_URI_MAP_ID_ATOM_WORKER = 7; const uint32_t CARLA_URI_MAP_ID_ATOM_TRANSFER_ATOM = 8; const uint32_t CARLA_URI_MAP_ID_ATOM_TRANSFER_EVENT = 9; const uint32_t CARLA_URI_MAP_ID_BUF_MAX_LENGTH = 10; const uint32_t CARLA_URI_MAP_ID_BUF_MIN_LENGTH = 11; const uint32_t CARLA_URI_MAP_ID_BUF_SEQUENCE_SIZE = 12; const uint32_t CARLA_URI_MAP_ID_LOG_ERROR = 13; const uint32_t CARLA_URI_MAP_ID_LOG_NOTE = 14; const uint32_t CARLA_URI_MAP_ID_LOG_TRACE = 15; const uint32_t CARLA_URI_MAP_ID_LOG_WARNING = 16; const uint32_t CARLA_URI_MAP_ID_MIDI_EVENT = 17; const uint32_t CARLA_URI_MAP_ID_PARAM_SAMPLE_RATE = 18; const uint32_t CARLA_URI_MAP_ID_COUNT = 19; // ------------------------------------------------------------------------- struct Lv2PluginOptions { uint32_t eventSize; uint32_t bufferSize; double sampleRate; LV2_Options_Option oNull; LV2_Options_Option oMaxBlockLenth; LV2_Options_Option oMinBlockLenth; LV2_Options_Option oSequenceSize; LV2_Options_Option oSampleRate; Lv2PluginOptions() : eventSize(MAX_EVENT_BUFFER), bufferSize(0), sampleRate(0.0) {} }; Lv2PluginOptions lv2Options; class CarlaLv2Client : public CarlaBridgeClient { public: CarlaLv2Client(const char* const uiTitle) : CarlaBridgeClient(uiTitle) { handle = nullptr; widget = nullptr; descriptor = nullptr; rdf_descriptor = nullptr; rdf_ui_descriptor = nullptr; programs = nullptr; #ifdef BRIDGE_LV2_X11 m_resizable = false; #else m_resizable = true; #endif for (uint32_t i=0; i < CARLA_URI_MAP_ID_COUNT; i++) customURIDs.push_back(nullptr); for (uint32_t i=0; i < lv2_feature_count+1; i++) features[i] = nullptr; // ----------------------------------------------------------------- // initialize options lv2Options.bufferSize = bufferSize; lv2Options.sampleRate = sampleRate; lv2Options.oNull.key = CARLA_URI_MAP_ID_NULL; lv2Options.oNull.size = 0; lv2Options.oNull.type = CARLA_URI_MAP_ID_NULL; lv2Options.oNull.value = nullptr; lv2Options.oMaxBlockLenth.key = CARLA_URI_MAP_ID_BUF_MAX_LENGTH; lv2Options.oMaxBlockLenth.size = sizeof(uint32_t); lv2Options.oMaxBlockLenth.type = CARLA_URI_MAP_ID_ATOM_INT; lv2Options.oMaxBlockLenth.value = &lv2Options.bufferSize; lv2Options.oMinBlockLenth.key = CARLA_URI_MAP_ID_BUF_MIN_LENGTH; lv2Options.oMinBlockLenth.size = sizeof(uint32_t); lv2Options.oMinBlockLenth.type = CARLA_URI_MAP_ID_ATOM_INT; lv2Options.oMinBlockLenth.value = &lv2Options.bufferSize; lv2Options.oSequenceSize.key = CARLA_URI_MAP_ID_BUF_SEQUENCE_SIZE; lv2Options.oSequenceSize.size = sizeof(uint32_t); lv2Options.oSequenceSize.type = CARLA_URI_MAP_ID_ATOM_INT; lv2Options.oSequenceSize.value = &lv2Options.eventSize; lv2Options.oSampleRate.key = CARLA_URI_MAP_ID_PARAM_SAMPLE_RATE; lv2Options.oSampleRate.size = sizeof(double); lv2Options.oSampleRate.type = CARLA_URI_MAP_ID_ATOM_DOUBLE; lv2Options.oSampleRate.value = &lv2Options.sampleRate; // ----------------------------------------------------------------- // initialize features LV2_Event_Feature* const eventFt = new LV2_Event_Feature; eventFt->callback_data = this; eventFt->lv2_event_ref = carla_lv2_event_ref; eventFt->lv2_event_unref = carla_lv2_event_unref; LV2_Log_Log* const logFt = new LV2_Log_Log; logFt->handle = this; logFt->printf = carla_lv2_log_printf; logFt->vprintf = carla_lv2_log_vprintf; LV2_Programs_Host* const programsFt = new LV2_Programs_Host; programsFt->handle = this; programsFt->program_changed = carla_lv2_program_changed; LV2_RtMemPool_Pool* const rtMemPoolFt = new LV2_RtMemPool_Pool; lv2_rtmempool_init(rtMemPoolFt); LV2_State_Make_Path* const stateMakePathFt = new LV2_State_Make_Path; stateMakePathFt->handle = this; stateMakePathFt->path = carla_lv2_state_make_path; LV2_State_Map_Path* const stateMapPathFt = new LV2_State_Map_Path; stateMapPathFt->handle = this; stateMapPathFt->abstract_path = carla_lv2_state_map_abstract_path; stateMapPathFt->absolute_path = carla_lv2_state_map_absolute_path; LV2_URI_Map_Feature* const uriMapFt = new LV2_URI_Map_Feature; uriMapFt->callback_data = this; uriMapFt->uri_to_id = carla_lv2_uri_to_id; LV2_URID_Map* const uridMapFt = new LV2_URID_Map; uridMapFt->handle = this; uridMapFt->map = carla_lv2_urid_map; LV2_URID_Unmap* const uridUnmapFt = new LV2_URID_Unmap; uridUnmapFt->handle = this; uridUnmapFt->unmap = carla_lv2_urid_unmap; 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_Options_Option* const optionsFt = new LV2_Options_Option [5]; optionsFt[0] = lv2Options.oMaxBlockLenth; optionsFt[1] = lv2Options.oMinBlockLenth; optionsFt[2] = lv2Options.oSequenceSize; optionsFt[3] = lv2Options.oSampleRate; optionsFt[4] = lv2Options.oNull; features[lv2_feature_id_bufsize_bounded] = new LV2_Feature; features[lv2_feature_id_bufsize_bounded]->URI = LV2_BUF_SIZE__boundedBlockLength; features[lv2_feature_id_bufsize_bounded]->data = nullptr; features[lv2_feature_id_bufsize_fixed] = new LV2_Feature; features[lv2_feature_id_bufsize_fixed]->URI = LV2_BUF_SIZE__fixedBlockLength; features[lv2_feature_id_bufsize_fixed]->data = nullptr; features[lv2_feature_id_bufsize_powerof2] = new LV2_Feature; features[lv2_feature_id_bufsize_powerof2]->URI = LV2_BUF_SIZE__powerOf2BlockLength; features[lv2_feature_id_bufsize_powerof2]->data = nullptr; features[lv2_feature_id_event] = new LV2_Feature; features[lv2_feature_id_event]->URI = LV2_EVENT_URI; features[lv2_feature_id_event]->data = eventFt; features[lv2_feature_id_logs] = new LV2_Feature; features[lv2_feature_id_logs]->URI = LV2_LOG__log; features[lv2_feature_id_logs]->data = logFt; features[lv2_feature_id_options] = new LV2_Feature; features[lv2_feature_id_options]->URI = LV2_OPTIONS__options; features[lv2_feature_id_options]->data = optionsFt; features[lv2_feature_id_programs] = new LV2_Feature; features[lv2_feature_id_programs]->URI = LV2_PROGRAMS__Host; features[lv2_feature_id_programs]->data = programsFt; features[lv2_feature_id_rtmempool] = new LV2_Feature; features[lv2_feature_id_rtmempool]->URI = LV2_RTSAFE_MEMORY_POOL__Pool; features[lv2_feature_id_rtmempool]->data = rtMemPoolFt; features[lv2_feature_id_state_make_path] = new LV2_Feature; features[lv2_feature_id_state_make_path]->URI = LV2_STATE__makePath; features[lv2_feature_id_state_make_path]->data = stateMakePathFt; features[lv2_feature_id_state_map_path] = new LV2_Feature; features[lv2_feature_id_state_map_path]->URI = LV2_STATE__mapPath; features[lv2_feature_id_state_map_path]->data = stateMapPathFt; features[lv2_feature_id_strict_bounds] = new LV2_Feature; features[lv2_feature_id_strict_bounds]->URI = LV2_PORT_PROPS__supportsStrictBounds; features[lv2_feature_id_strict_bounds]->data = nullptr; features[lv2_feature_id_uri_map] = new LV2_Feature; features[lv2_feature_id_uri_map]->URI = LV2_URI_MAP_URI; features[lv2_feature_id_uri_map]->data = uriMapFt; features[lv2_feature_id_urid_map] = new LV2_Feature; features[lv2_feature_id_urid_map]->URI = LV2_URID__map; features[lv2_feature_id_urid_map]->data = uridMapFt; features[lv2_feature_id_urid_unmap] = new LV2_Feature; features[lv2_feature_id_urid_unmap]->URI = LV2_URID__unmap; features[lv2_feature_id_urid_unmap]->data = uridUnmapFt; features[lv2_feature_id_ui_parent] = new LV2_Feature; features[lv2_feature_id_ui_parent]->URI = LV2_UI__parent; #ifdef BRIDGE_LV2_X11 features[lv2_feature_id_ui_parent]->data = getContainerId(); #else features[lv2_feature_id_ui_parent]->data = nullptr; #endif features[lv2_feature_id_ui_port_map] = new LV2_Feature; features[lv2_feature_id_ui_port_map]->URI = LV2_UI__portMap; features[lv2_feature_id_ui_port_map]->data = uiPortMapFt; features[lv2_feature_id_ui_resize] = new LV2_Feature; features[lv2_feature_id_ui_resize]->URI = LV2_UI__resize; features[lv2_feature_id_ui_resize]->data = uiResizeFt; } ~CarlaLv2Client() { if (rdf_descriptor) delete rdf_descriptor; const LV2_Options_Option* const options = (const LV2_Options_Option*)features[lv2_feature_id_options]->data; delete[] options; delete (LV2_Event_Feature*)features[lv2_feature_id_event]->data; delete (LV2_Log_Log*)features[lv2_feature_id_logs]->data; delete (LV2_Programs_Host*)features[lv2_feature_id_programs]->data; delete (LV2_RtMemPool_Pool*)features[lv2_feature_id_rtmempool]->data; delete (LV2_State_Make_Path*)features[lv2_feature_id_state_make_path]->data; delete (LV2_State_Map_Path*)features[lv2_feature_id_state_map_path]->data; delete (LV2_URI_Map_Feature*)features[lv2_feature_id_uri_map]->data; delete (LV2_URID_Map*)features[lv2_feature_id_urid_map]->data; delete (LV2_URID_Unmap*)features[lv2_feature_id_urid_unmap]->data; delete (LV2UI_Port_Map*)features[lv2_feature_id_ui_port_map]->data; delete (LV2UI_Resize*)features[lv2_feature_id_ui_resize]->data; for (uint32_t i=0; i < lv2_feature_count; i++) { if (features[i]) delete features[i]; } for (size_t i=0; i < customURIDs.size(); i++) { if (customURIDs[i]) free((void*)customURIDs[i]); } customURIDs.clear(); } // --------------------------------------------------------------------- // ui initialization bool uiInit(const char* pluginURI, const char* uiURI) { // ----------------------------------------------------------------- // init CarlaBridgeClient::uiInit(pluginURI, uiURI); // ----------------------------------------------------------------- // get plugin from lv2_rdf (lilv) gLv2World.init(); rdf_descriptor = lv2_rdf_new(pluginURI); if (! rdf_descriptor) return false; // ----------------------------------------------------------------- // find requested UI for (uint32_t i=0; i < rdf_descriptor->UICount; i++) { if (std::strcmp(rdf_descriptor->UIs[i].URI, uiURI) == 0) { rdf_ui_descriptor = &rdf_descriptor->UIs[i]; break; } } if (! rdf_ui_descriptor) return false; // ----------------------------------------------------------------- // open DLL if (! uiLibOpen(rdf_ui_descriptor->Binary)) return false; // ----------------------------------------------------------------- // get DLL main entry const LV2UI_DescriptorFunction ui_descFn = (LV2UI_DescriptorFunction)uiLibSymbol("lv2ui_descriptor"); if (! ui_descFn) return false; // ----------------------------------------------------------- // get descriptor that matches URI uint32_t i = 0; while ((descriptor = ui_descFn(i++))) { if (std::strcmp(descriptor->URI, uiURI) == 0) break; } if (! descriptor) return false; // ----------------------------------------------------------- // initialize UI handle = descriptor->instantiate(descriptor, pluginURI, rdf_ui_descriptor->Bundle, carla_lv2_ui_write_function, this, &widget, features); if (! handle) return false; // ----------------------------------------------------------- // check if not resizable #ifndef BRIDGE_LV2_X11 for (uint32_t i=0; i < rdf_ui_descriptor->FeatureCount; i++) { if (std::strcmp(rdf_ui_descriptor->Features[i].URI, LV2_UI__fixedSize) == 0 || std::strcmp(rdf_ui_descriptor->Features[i].URI, LV2_UI__noUserResize) == 0) { m_resizable = false; break; } } #endif // ----------------------------------------------------------- // check for known extensions for (uint32_t i=0; descriptor->extension_data && i < rdf_ui_descriptor->ExtensionCount; i++) { if (std::strcmp(rdf_ui_descriptor->Extensions[i], LV2_PROGRAMS__UIInterface) == 0) { programs = (LV2_Programs_UI_Interface*)descriptor->extension_data(LV2_PROGRAMS__UIInterface); if (programs && ! programs->select_program) // invalid programs = nullptr; break; } } return true; } void uiClose() { CarlaBridgeClient::uiClose(); if (handle && descriptor && descriptor->cleanup) descriptor->cleanup(handle); uiLibClose(); } // --------------------------------------------------------------------- // ui management void* getWidget() const { return widget; } bool isResizable() const { return m_resizable; } bool needsReparent() const { #ifdef BRIDGE_LV2_X11 return true; #else return false; #endif } // --------------------------------------------------------------------- // processing void setParameter(const int32_t rindex, const float value) { CARLA_ASSERT(handle && descriptor); if (handle && descriptor && descriptor->port_event) { float fvalue = value; descriptor->port_event(handle, rindex, sizeof(float), 0, &fvalue); } } void setProgram(const uint32_t) { } void setMidiProgram(const uint32_t bank, const uint32_t program) { CARLA_ASSERT(handle); if (handle && programs) programs->select_program(handle, bank, program); } void noteOn(const uint8_t channel, const uint8_t note, const uint8_t velo) { CARLA_ASSERT(handle && descriptor); if (handle && descriptor && descriptor->port_event) { 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_ON + channel; midiEv.data[1] = note; midiEv.data[2] = velo; descriptor->port_event(handle, 0, 3, CARLA_URI_MAP_ID_ATOM_TRANSFER_ATOM, &midiEv); } } void noteOff(const uint8_t channel, const uint8_t note) { CARLA_ASSERT(handle && descriptor); if (handle && descriptor && descriptor->port_event) { 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; descriptor->port_event(handle, 0, 3, CARLA_URI_MAP_ID_ATOM_TRANSFER_ATOM, &midiEv); } } // --------------------------------------------------------------------- uint32_t getCustomURID(const char* const uri) { qDebug("CarlaLv2Client::getCustomURID(%s)", uri); CARLA_ASSERT(uri); if (! uri) return CARLA_URI_MAP_ID_NULL; for (size_t i=0; i < customURIDs.size(); i++) { if (customURIDs[i] && std::strcmp(customURIDs[i], uri) == 0) return i; } customURIDs.push_back(strdup(uri)); return customURIDs.size()-1; } const char* getCustomURIString(const LV2_URID urid) const { qDebug("CarlaLv2Client::getCustomURIString(%i)", urid); CARLA_ASSERT(urid > CARLA_URI_MAP_ID_NULL); if (urid == CARLA_URI_MAP_ID_NULL) return nullptr; if (urid < customURIDs.size()) return customURIDs[urid]; return nullptr; } // --------------------------------------------------------------------- void handleTransferAtom(const int32_t portIndex, const LV2_Atom* const atom) { qDebug("CarlaLv2Client::handleTransferEvent(%i, %p)", portIndex, atom); CARLA_ASSERT(portIndex >= 0); CARLA_ASSERT(atom); if (atom && handle && descriptor && descriptor->port_event) descriptor->port_event(handle, portIndex, atom->size, CARLA_URI_MAP_ID_ATOM_TRANSFER_ATOM, atom); } void handleTransferEvent(const int32_t portIndex, const LV2_Atom* const atom) { qDebug("CarlaLv2Client::handleTransferEvent(%i, %p)", portIndex, atom); CARLA_ASSERT(portIndex >= 0); CARLA_ASSERT(atom); if (atom && handle && descriptor && descriptor->port_event) descriptor->port_event(handle, portIndex, atom->size, CARLA_URI_MAP_ID_ATOM_TRANSFER_EVENT, atom); #if 0 if (handle && descriptor && descriptor->port_event) { LV2_URID_Map* const URID_Map = (LV2_URID_Map*)features[lv2_feature_id_urid_map]->data; const LV2_URID uridPatchSet = getCustomURID(LV2_PATCH__Set); const LV2_URID uridPatchBody = getCustomURID(LV2_PATCH__body); Sratom* sratom = sratom_new(URID_Map); SerdChunk chunk = { nullptr, 0 }; LV2_Atom_Forge forge; lv2_atom_forge_init(&forge, URID_Map); lv2_atom_forge_set_sink(&forge, sratom_forge_sink, sratom_forge_deref, &chunk); LV2_Atom_Forge_Frame refFrame, bodyFrame; LV2_Atom_Forge_Ref ref = lv2_atom_forge_blank(&forge, &refFrame, 1, uridPatchSet); lv2_atom_forge_property_head(&forge, uridPatchBody, CARLA_URI_MAP_ID_NULL); lv2_atom_forge_blank(&forge, &bodyFrame, 2, CARLA_URI_MAP_ID_NULL); //lv2_atom_forge_property_head(&forge, getCustomURID(key), CARLA_URI_MAP_ID_NULL); if (std::strcmp(type, "string") == 0) lv2_atom_forge_string(&forge, value, std::strlen(value)); else if (std::strcmp(type, "path") == 0) lv2_atom_forge_path(&forge, value, std::strlen(value)); else if (std::strcmp(type, "chunk") == 0) lv2_atom_forge_literal(&forge, value, std::strlen(value), CARLA_URI_MAP_ID_ATOM_CHUNK, CARLA_URI_MAP_ID_NULL); //else // lv2_atom_forge_literal(&forge, value, std::strlen(value), getCustomURID(key), CARLA_URI_MAP_ID_NULL); lv2_atom_forge_pop(&forge, &bodyFrame); lv2_atom_forge_pop(&forge, &refFrame); const LV2_Atom* const atom = lv2_atom_forge_deref(&forge, ref); descriptor->port_event(handle, 0, atom->size, CARLA_URI_MAP_ID_ATOM_TRANSFER_EVENT, atom); free((void*)chunk.buf); sratom_free(sratom); } #endif } // --------------------------------------------------------------------- void handleProgramChanged(int32_t /*index*/) { if (isOscControlRegistered()) sendOscConfigure("reloadprograms", ""); } uint32_t handleUiPortMap(const char* const symbol) { CARLA_ASSERT(symbol); if (! symbol) return LV2UI_INVALID_PORT_INDEX; for (uint32_t i=0; i < rdf_descriptor->PortCount; i++) { if (std::strcmp(rdf_descriptor->Ports[i].Symbol, symbol) == 0) return i; } return LV2UI_INVALID_PORT_INDEX; } int handleUiResize(int width, int height) { CARLA_ASSERT(width > 0); CARLA_ASSERT(height > 0); if (width <= 0 || height <= 0) return 1; toolkitResize(width, height); return 0; } void handleUiWrite(uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) { if (! (buffer && isOscControlRegistered())) return; if (format == 0) { CARLA_ASSERT(buffer); CARLA_ASSERT(bufferSize == sizeof(float)); if (bufferSize != sizeof(float)) return; float value = *(float*)buffer; sendOscControl(portIndex, value); } else if (format == CARLA_URI_MAP_ID_ATOM_TRANSFER_ATOM) { CARLA_ASSERT(buffer); const LV2_Atom* const atom = (const LV2_Atom*)buffer; QByteArray chunk((const char*)buffer, bufferSize); sendOscLv2TransferAtom(portIndex, getCustomURIString(atom->type), chunk.toBase64().constData()); } else if (format == CARLA_URI_MAP_ID_ATOM_TRANSFER_EVENT) { CARLA_ASSERT(buffer); const LV2_Atom* const atom = (const LV2_Atom*)buffer; QByteArray chunk((const char*)buffer, bufferSize); sendOscLv2TransferEvent(portIndex, getCustomURIString(atom->type), chunk.toBase64().constData()); } } // ----------------- Event Feature --------------------------------------------------- static uint32_t carla_lv2_event_ref(const LV2_Event_Callback_Data callback_data, LV2_Event* const event) { qDebug("CarlaLv2Client::carla_lv2_event_ref(%p, %p)", callback_data, event); CARLA_ASSERT(callback_data); CARLA_ASSERT(event); return 0; } static uint32_t carla_lv2_event_unref(const LV2_Event_Callback_Data callback_data, LV2_Event* const event) { qDebug("CarlaLv2Client::carla_lv2_event_unref(%p, %p)", callback_data, event); CARLA_ASSERT(callback_data); CARLA_ASSERT(event); return 0; } // ----------------- Logs Feature ---------------------------------------------------- static int carla_lv2_log_printf(const LV2_Log_Handle handle, const LV2_URID type, const char* const fmt, ...) { qDebug("CarlaLv2Client::carla_lv2_log_printf(%p, %i, %s, ...)", handle, type, fmt); CARLA_ASSERT(handle); CARLA_ASSERT(type > 0); #ifndef DEBUG if (type == CARLA_URI_MAP_ID_LOG_TRACE) return 0; #endif va_list args; va_start(args, fmt); const int ret = carla_lv2_log_vprintf(handle, type, fmt, args); va_end(args); return ret; } static int carla_lv2_log_vprintf(const LV2_Log_Handle handle, const LV2_URID type, const char* const fmt, va_list ap) { qDebug("CarlaLv2Client::carla_lv2_log_vprintf(%p, %i, %s, ...)", handle, type, fmt); CARLA_ASSERT(handle); CARLA_ASSERT(type > 0); #ifndef DEBUG if (type == CARLA_URI_MAP_ID_LOG_TRACE) return 0; #endif char buf[8196]; vsprintf(buf, fmt, ap); if (*buf == 0) return 0; switch (type) { case CARLA_URI_MAP_ID_LOG_ERROR: qCritical("%s", buf); break; case CARLA_URI_MAP_ID_LOG_NOTE: printf("%s\n", buf); break; case CARLA_URI_MAP_ID_LOG_TRACE: qDebug("%s", buf); break; case CARLA_URI_MAP_ID_LOG_WARNING: carla_stderr("%s", buf); break; default: break; } return std::strlen(buf); } // ----------------- Programs Feature ------------------------------------------------ static void carla_lv2_program_changed(const LV2_Programs_Handle handle, const int32_t index) { qDebug("CarlaLv2Client::carla_lv2_program_changed(%p, %i)", handle, index); CARLA_ASSERT(handle); if (! handle) return; CarlaLv2Client* const client = (CarlaLv2Client*)handle; client->handleProgramChanged(index); } // ----------------- State Feature --------------------------------------------------- static char* carla_lv2_state_make_path(const LV2_State_Make_Path_Handle handle, const char* const path) { qDebug("CarlaLv2Client::carla_lv2_state_make_path(%p, %p)", handle, path); CARLA_ASSERT(handle); CARLA_ASSERT(path); if (! path) return nullptr; QDir dir; dir.mkpath(path); return strdup(path); } static char* carla_lv2_state_map_abstract_path(const LV2_State_Map_Path_Handle handle, const char* const absolute_path) { qDebug("CarlaLv2Client::carla_lv2_state_map_abstract_path(%p, %p)", handle, absolute_path); CARLA_ASSERT(handle); CARLA_ASSERT(absolute_path); if (! absolute_path) return nullptr; QDir dir(absolute_path); return strdup(dir.canonicalPath().toUtf8().constData()); } static char* carla_lv2_state_map_absolute_path(const LV2_State_Map_Path_Handle handle, const char* const abstract_path) { qDebug("CarlaLv2Client::carla_lv2_state_map_absolute_path(%p, %p)", handle, abstract_path); CARLA_ASSERT(handle); CARLA_ASSERT(abstract_path); if (! abstract_path) return nullptr; QDir dir(abstract_path); return strdup(dir.absolutePath().toUtf8().constData()); } // ----------------- URI-Map Feature --------------------------------------- static uint32_t carla_lv2_uri_to_id(const LV2_URI_Map_Callback_Data data, const char* const map, const char* const uri) { qDebug("CarlaLv2Client::carla_lv2_uri_to_id(%p, %s, %s)", data, map, uri); return carla_lv2_urid_map((LV2_URID_Map_Handle*)data, uri); } // ----------------- URID Feature ------------------------------------------ static LV2_URID carla_lv2_urid_map(const LV2_URID_Map_Handle handle, const char* const uri) { qDebug("CarlaLv2Client::carla_lv2_urid_map(%p, %s)", handle, uri); CARLA_ASSERT(handle); CARLA_ASSERT(uri); if (! uri) return CARLA_URI_MAP_ID_NULL; // Atom types if (std::strcmp(uri, LV2_ATOM__Chunk) == 0) return CARLA_URI_MAP_ID_ATOM_CHUNK; if (std::strcmp(uri, LV2_ATOM__Double) == 0) return CARLA_URI_MAP_ID_ATOM_DOUBLE; if (std::strcmp(uri, LV2_ATOM__Int) == 0) return CARLA_URI_MAP_ID_ATOM_INT; if (std::strcmp(uri, LV2_ATOM__Path) == 0) return CARLA_URI_MAP_ID_ATOM_PATH; if (std::strcmp(uri, LV2_ATOM__Sequence) == 0) return CARLA_URI_MAP_ID_ATOM_SEQUENCE; if (std::strcmp(uri, LV2_ATOM__String) == 0) return CARLA_URI_MAP_ID_ATOM_STRING; if (std::strcmp(uri, LV2_ATOM__atomTransfer) == 0) return CARLA_URI_MAP_ID_ATOM_TRANSFER_ATOM; if (std::strcmp(uri, LV2_ATOM__eventTransfer) == 0) return CARLA_URI_MAP_ID_ATOM_TRANSFER_EVENT; // BufSize types if (std::strcmp(uri, LV2_BUF_SIZE__maxBlockLength) == 0) return CARLA_URI_MAP_ID_BUF_MAX_LENGTH; if (std::strcmp(uri, LV2_BUF_SIZE__minBlockLength) == 0) return CARLA_URI_MAP_ID_BUF_MIN_LENGTH; if (std::strcmp(uri, LV2_BUF_SIZE__sequenceSize) == 0) return CARLA_URI_MAP_ID_BUF_SEQUENCE_SIZE; // Log types if (std::strcmp(uri, LV2_LOG__Error) == 0) return CARLA_URI_MAP_ID_LOG_ERROR; if (std::strcmp(uri, LV2_LOG__Note) == 0) return CARLA_URI_MAP_ID_LOG_NOTE; if (std::strcmp(uri, LV2_LOG__Trace) == 0) return CARLA_URI_MAP_ID_LOG_TRACE; if (std::strcmp(uri, LV2_LOG__Warning) == 0) return CARLA_URI_MAP_ID_LOG_WARNING; // Others if (std::strcmp(uri, LV2_MIDI__MidiEvent) == 0) return CARLA_URI_MAP_ID_MIDI_EVENT; if (std::strcmp(uri, LV2_PARAMETERS__sampleRate) == 0) return CARLA_URI_MAP_ID_PARAM_SAMPLE_RATE; if (! handle) return CARLA_URI_MAP_ID_NULL; // Custom types CarlaLv2Client* const client = (CarlaLv2Client*)handle; return client->getCustomURID(uri); } static const char* carla_lv2_urid_unmap(const LV2_URID_Map_Handle handle, const LV2_URID urid) { qDebug("CarlaLv2Client::carla_lv2_urid_unmap(%p, %i)", handle, urid); CARLA_ASSERT(handle); CARLA_ASSERT(urid > CARLA_URI_MAP_ID_NULL); if (urid == CARLA_URI_MAP_ID_NULL) return nullptr; // Atom types if (urid == CARLA_URI_MAP_ID_ATOM_CHUNK) return LV2_ATOM__Chunk; if (urid == CARLA_URI_MAP_ID_ATOM_DOUBLE) return LV2_ATOM__Double; if (urid == CARLA_URI_MAP_ID_ATOM_INT) return LV2_ATOM__Int; if (urid == CARLA_URI_MAP_ID_ATOM_PATH) return LV2_ATOM__Path; if (urid == CARLA_URI_MAP_ID_ATOM_SEQUENCE) return LV2_ATOM__Sequence; if (urid == CARLA_URI_MAP_ID_ATOM_STRING) return LV2_ATOM__String; if (urid == CARLA_URI_MAP_ID_ATOM_TRANSFER_ATOM) return LV2_ATOM__atomTransfer; if (urid == CARLA_URI_MAP_ID_ATOM_TRANSFER_EVENT) return LV2_ATOM__eventTransfer; // BufSize types if (urid == CARLA_URI_MAP_ID_BUF_MAX_LENGTH) return LV2_BUF_SIZE__maxBlockLength; if (urid == CARLA_URI_MAP_ID_BUF_MIN_LENGTH) return LV2_BUF_SIZE__minBlockLength; if (urid == CARLA_URI_MAP_ID_BUF_SEQUENCE_SIZE) return LV2_BUF_SIZE__sequenceSize; // Log types if (urid == CARLA_URI_MAP_ID_LOG_ERROR) return LV2_LOG__Error; if (urid == CARLA_URI_MAP_ID_LOG_NOTE) return LV2_LOG__Note; if (urid == CARLA_URI_MAP_ID_LOG_TRACE) return LV2_LOG__Trace; if (urid == CARLA_URI_MAP_ID_LOG_WARNING) return LV2_LOG__Warning; // Others if (urid == CARLA_URI_MAP_ID_MIDI_EVENT) return LV2_MIDI__MidiEvent; if (urid == CARLA_URI_MAP_ID_PARAM_SAMPLE_RATE) return LV2_PARAMETERS__sampleRate; if (! handle) return nullptr; // Custom types CarlaLv2Client* const client = (CarlaLv2Client*)handle; return client->getCustomURIString(urid); } // ----------------- UI Port-Map Feature --------------------------------------------- static uint32_t carla_lv2_ui_port_map(const LV2UI_Feature_Handle handle, const char* const symbol) { qDebug("CarlaLv2Client::carla_lv2_ui_port_map(%p, %s)", handle, symbol); CARLA_ASSERT(handle); if (! handle) return LV2UI_INVALID_PORT_INDEX; CarlaLv2Client* const client = (CarlaLv2Client*)handle; return client->handleUiPortMap(symbol); } // ----------------- UI Resize Feature ------------------------------------- static int carla_lv2_ui_resize(const LV2UI_Feature_Handle handle, const int width, const int height) { qDebug("CarlaLv2Client::carla_lv2_ui_resize(%p, %i, %i)", handle, width, height); CARLA_ASSERT(handle); if (! handle) return 1; CarlaLv2Client* const client = (CarlaLv2Client*)handle; return client->handleUiResize(width, height); } // ----------------- UI Extension ------------------------------------------ static void carla_lv2_ui_write_function(const LV2UI_Controller controller, const uint32_t port_index, const uint32_t buffer_size, const uint32_t format, const void* const buffer) { qDebug("CarlaLv2Client::carla_lv2_ui_write_function(%p, %i, %i, %i, %p)", controller, port_index, buffer_size, format, buffer); CARLA_ASSERT(controller); if (! controller) return; CarlaLv2Client* const client = (CarlaLv2Client*)controller; client->handleUiWrite(port_index, buffer_size, format, buffer); } private: LV2UI_Handle handle; LV2UI_Widget widget; const LV2UI_Descriptor* descriptor; LV2_Feature* features[lv2_feature_count+1]; const LV2_RDF_Descriptor* rdf_descriptor; const LV2_RDF_UI* rdf_ui_descriptor; const LV2_Programs_UI_Interface* programs; bool m_resizable; std::vector customURIDs; }; int CarlaBridgeOsc::handleMsgLv2TransferAtom(CARLA_BRIDGE_OSC_HANDLE_ARGS) { qDebug("CarlaBridgeOsc::handleMsgLv2TransferAtom()"); CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(3, "iss"); if (! kClient) return 1; const int32_t portIndex = argv[0]->i; const char* const typeStr = (const char*)&argv[1]->s; const char* const atomBuf = (const char*)&argv[2]->s; QByteArray chunk; chunk = QByteArray::fromBase64(atomBuf); LV2_Atom* const atom = (LV2_Atom*)chunk.constData(); CarlaLv2Client* const lv2Client = (CarlaLv2Client*)kClient; atom->type = lv2Client->getCustomURID(typeStr); lv2Client->handleTransferAtom(portIndex, atom); return 0; } int CarlaBridgeOsc::handleMsgLv2TransferEvent(CARLA_BRIDGE_OSC_HANDLE_ARGS) { qDebug("CarlaBridgeOsc::handleMsgLv2TransferEvent()"); CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(3, "iss"); if (! kClient) return 1; const int32_t portIndex = argv[0]->i; const char* const typeStr = (const char*)&argv[1]->s; const char* const atomBuf = (const char*)&argv[2]->s; QByteArray chunk; chunk = QByteArray::fromBase64(atomBuf); LV2_Atom* const atom = (LV2_Atom*)chunk.constData(); CarlaLv2Client* const lv2Client = (CarlaLv2Client*)kClient; atom->type = lv2Client->getCustomURID(typeStr); lv2Client->handleTransferEvent(portIndex, atom); return 0; } CARLA_BRIDGE_END_NAMESPACE int main(int argc, char* argv[]) { CARLA_BRIDGE_USE_NAMESPACE if (argc != 5) { carla_stderr("usage: %s ", argv[0]); return 1; } const char* oscUrl = argv[1]; const char* pluginURI = argv[2]; const char* uiURI = argv[3]; const char* uiTitle = argv[4]; const bool useOsc = std::strcmp(oscUrl, "null"); // try to get sampleRate value const char* const sampleRateStr = getenv("CARLA_SAMPLE_RATE"); if (sampleRateStr) sampleRate = atof(sampleRateStr); // Init LV2 client CarlaLv2Client client(uiTitle); // Init OSC if (useOsc) client.oscInit(oscUrl); // Load UI int ret; if (client.uiInit(pluginURI, uiURI)) { client.toolkitExec(!useOsc); ret = 0; } else { qCritical("Failed to load LV2 UI"); ret = 1; } // Close OSC if (useOsc) client.oscClose(); // Close LV2 client client.uiClose(); return ret; } #endif // BRIDGE_LV2