/* * Carla Bridge OSC * Copyright (C) 2011-2012 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 COPYING file */ #include "carla_bridge_osc.hpp" #include "carla_bridge_client.hpp" #include "carla_midi.h" #include "carla_utils.hpp" #include #include CARLA_BRIDGE_START_NAMESPACE // ----------------------------------------------------------------------- CarlaBridgeOsc::CarlaBridgeOsc(CarlaBridgeClient* const client_) : client(client_) { qDebug("CarlaBridgeOsc::CarlaBridgeOsc(%p)", client); CARLA_ASSERT(client); m_name = nullptr; m_nameSize = 0; m_server = nullptr; m_serverPath = nullptr; } CarlaBridgeOsc::~CarlaBridgeOsc() { qDebug("CarlaBridgeOsc::~CarlaBridgeOsc()"); CARLA_ASSERT(! m_name); CARLA_ASSERT(! m_server); CARLA_ASSERT(! m_serverPath); } bool CarlaBridgeOsc::init(const char* const url) { qDebug("CarlaBridgeOsc::init(\"%s\")", url); CARLA_ASSERT(! m_name); CARLA_ASSERT(! m_server); CARLA_ASSERT(! m_serverPath); CARLA_ASSERT(m_nameSize == 0); CARLA_ASSERT(url); if (! url) { qWarning("CarlaBridgeOsc::init(\"%s\") - invalid url", url); return false; } #ifdef BUILD_BRIDGE_PLUGIN m_name = strdup("carla-bridge-plugin"); #else m_name = strdup("carla-bridge-ui"); #endif m_nameSize = strlen(m_name); char* const host = lo_url_get_hostname(url); char* const path = lo_url_get_path(url); char* const port = lo_url_get_port(url); if (! host) { qCritical("CarlaBridgeOsc::init(\"%s\") - failed to get url hostname", url); return false; } if (! path) { free(host); qCritical("CarlaBridgeOsc::init(\"%s\") - failed to get url path", url); return false; } if (! port) { free(host); free(path); qCritical("CarlaBridgeOsc::init(\"%s\") - failed to get url port", url); return false; } m_controlData.path = path; m_controlData.target = lo_address_new_with_proto(LO_TCP, host, port); free(host); free(port); if (! m_controlData.target) { qCritical("CarlaBridgeOsc::init(\"%s\") - failed to get new url address for host '%s' and port '%s'", url, host, port); return false; } m_server = lo_server_new_with_proto(nullptr, LO_TCP, osc_error_handler); if (! m_server) { qCritical("CarlaBridgeOsc::init(\"%s\") - failed to create new OSC server", url); return false; } if (char* const serverUrl = lo_server_get_url(m_server)) { m_serverPath = strdup(QString("%1%2").arg(serverUrl).arg(m_name).toUtf8().constData()); free(serverUrl); } else m_serverPath = strdup(QString("%1carla-bridge").arg(serverUrl).toUtf8().constData()); lo_server_add_method(m_server, nullptr, nullptr, osc_message_handler, this); return true; } void CarlaBridgeOsc::idle() { CARLA_ASSERT(m_server); if (m_server) { while (lo_server_recv_noblock(m_server, 0) != 0) {} } } void CarlaBridgeOsc::close() { qDebug("CarlaBridgeOsc::close()"); CARLA_ASSERT(m_name); CARLA_ASSERT(m_server); CARLA_ASSERT(m_serverPath); m_nameSize = 0; if (m_name) { free(m_name); m_name = nullptr; } if (m_server) { lo_server_del_method(m_server, nullptr, nullptr); lo_server_free(m_server); m_server = nullptr; } if (m_serverPath) { free(m_serverPath); m_serverPath = nullptr; } m_controlData.free(); } // ----------------------------------------------------------------------- int CarlaBridgeOsc::handleMessage(const char* const path, const int argc, const lo_arg* const* const argv, const char* const types, const lo_message msg) { qDebug("CarlaBridgeOsc::handleMessage(\"%s\", %i, %p, \"%s\", %p)", path, argc, argv, types, msg); CARLA_ASSERT(m_name); CARLA_ASSERT(m_server); CARLA_ASSERT(m_serverPath); CARLA_ASSERT(path); if (! path) { qCritical("CarlaBridgeOsc::handleMessage() - got invalid path"); return 1; } if (! (m_name && m_server && m_serverPath)) { qCritical("CarlaBridgeOsc::handleMessage(\"%s\", ...) - received message but client is offline", path); return 1; } if (strlen(path) <= m_nameSize || strncmp(path+1, m_name, m_nameSize) != 0) { qWarning("CarlaBridgeOsc::handleMessage() - message not for this client: '%s' != '/%s/'", path, m_name); return 1; } char method[32] = { 0 }; strncpy(method, path + (m_nameSize + 2), 31); if (method[0] == '\0') { qWarning("CarlaBridgeOsc::handleMessage(\"%s\", ...) - received message without method", path); return 1; } // Common OSC methods if (strcmp(method, "configure") == 0) return handleMsgConfigure(argc, argv, types); if (strcmp(method, "control") == 0) return handleMsgControl(argc, argv, types); if (strcmp(method, "program") == 0) return handleMsgProgram(argc, argv, types); if (strcmp(method, "midi_program") == 0) return handleMsgMidiProgram(argc, argv, types); if (strcmp(method, "midi") == 0) return handleMsgMidi(argc, argv, types); if (strcmp(method, "sample-rate") == 0) return 0; // unused if (strcmp(method, "show") == 0) return handleMsgShow(); if (strcmp(method, "hide") == 0) return handleMsgHide(); if (strcmp(method, "quit") == 0) return handleMsgQuit(); #ifdef BRIDGE_LV2 // LV2 UI methods if (strcmp(method, "lv2_atom_transfer") == 0) return handleMsgLv2TransferAtom(argc, argv, types); if (strcmp(method, "lv2_event_transfer") == 0) return handleMsgLv2TransferEvent(argc, argv, types); #endif #ifdef BUILD_BRIDGE_PLUGIN // Plugin methods if (strcmp(method, "plugin_save_now") == 0) return handleMsgPluginSaveNow(); if (strcmp(method, "plugin_set_chunk") == 0) return handleMsgPluginSetChunk(argc, argv, types); if (strcmp(method, "plugin_set_custom_data") == 0) return handleMsgPluginSetCustomData(argc, argv, types); #endif #if 0 // TODO else if (strcmp(method, "set_parameter_midi_channel") == 0) return osc_set_parameter_midi_channel_handler(argv); else if (strcmp(method, "set_parameter_midi_cc") == 0) return osc_set_parameter_midi_channel_handler(argv); #endif qWarning("CarlaBridgeOsc::handleMessage(\"%s\", ...) - received unsupported OSC method '%s'", path, method); return 1; } int CarlaBridgeOsc::handleMsgConfigure(CARLA_BRIDGE_OSC_HANDLE_ARGS) { qDebug("CarlaBridgeOsc::handleMsgConfigure()"); CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(2, "ss"); if (! client) return 1; // nothing here for now return 0; Q_UNUSED(argv); } int CarlaBridgeOsc::handleMsgControl(CARLA_BRIDGE_OSC_HANDLE_ARGS) { qDebug("CarlaBridgeOsc::handleMsgControl()"); CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(2, "if"); if (! client) return 1; const int32_t index = argv[0]->i; const float value = argv[1]->f; client->setParameter(index, value); return 0; } int CarlaBridgeOsc::handleMsgProgram(CARLA_BRIDGE_OSC_HANDLE_ARGS) { qDebug("CarlaBridgeOsc::handleMsgProgram()"); CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(1, "i"); if (! client) return 1; const int32_t index = argv[0]->i; client->setProgram(index); return 0; } #ifdef BUILD_BRIDGE_PLUGIN int CarlaBridgeOsc::handleMsgMidiProgram(CARLA_BRIDGE_OSC_HANDLE_ARGS) { qDebug("CarlaBridgeOsc::handleMsgMidiProgram()"); CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(1, "i"); if (! client) return 1; const int32_t index = argv[0]->i; client->setMidiProgram(index); return 0; } #else int CarlaBridgeOsc::handleMsgMidiProgram(CARLA_BRIDGE_OSC_HANDLE_ARGS) { qDebug("CarlaBridgeOsc::handleMsgMidiProgram()"); CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(2, "ii"); if (! client) return 1; const int32_t bank = argv[0]->i; const int32_t program = argv[1]->i; client->setMidiProgram(bank, program); return 0; } #endif int CarlaBridgeOsc::handleMsgMidi(CARLA_BRIDGE_OSC_HANDLE_ARGS) { qDebug("CarlaBridgeOsc::handleMsgMidi()"); CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(1, "m"); if (! client) return 1; const uint8_t* const mdata = argv[0]->m; const uint8_t data[4] = { mdata[0], mdata[1], mdata[2], mdata[3] }; uint8_t status = data[1]; uint8_t channel = status & 0x0F; // Fix bad note-off if (MIDI_IS_STATUS_NOTE_ON(status) && data[3] == 0) status -= 0x10; if (MIDI_IS_STATUS_NOTE_OFF(status)) { const uint8_t note = data[2]; client->noteOff(channel, note); } else if (MIDI_IS_STATUS_NOTE_ON(status)) { const uint8_t note = data[2]; const uint8_t velo = data[3]; client->noteOn(channel, note, velo); } return 0; } int CarlaBridgeOsc::handleMsgShow() { qDebug("CarlaBridgeOsc::handleMsgShow()"); if (! client) return 1; client->toolkitShow(); return 0; } int CarlaBridgeOsc::handleMsgHide() { qDebug("CarlaBridgeOsc::handleMsgHide()"); if (! client) return 1; client->toolkitHide(); return 0; } int CarlaBridgeOsc::handleMsgQuit() { qDebug("CarlaBridgeOsc::handleMsgQuit()"); if (! client) return 1; client->toolkitQuit(); return 0; } CARLA_BRIDGE_END_NAMESPACE