/* * Carla Plugin bridge code * Copyright (C) 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 */ #ifdef BUILD_BRIDGE_PLUGIN #include "carla_bridge_client.h" #include "carla_plugin.h" #include #include #include #include #include #ifdef Q_OS_UNIX #include #endif static int qargc = 0; static char** qargv = nullptr; bool qcloseNow = false; #if defined(Q_OS_UNIX) void closeSignalHandler(int) { qcloseNow = true; } #elif defined(Q_OS_WIN) BOOL WINAPI closeSignalHandler(DWORD dwCtrlType) { if (dwCtrlType == CTRL_C_EVENT) { qcloseNow = true; return TRUE; } return FALSE; } #endif void initSignalHandler() { #if defined(Q_OS_UNIX) struct sigaction sint, sterm; sint.sa_handler = closeSignalHandler; sigemptyset(&sint.sa_mask); sint.sa_flags |= SA_RESTART; sigaction(SIGINT, &sint, 0); sterm.sa_handler = closeSignalHandler; sigemptyset(&sterm.sa_mask); sterm.sa_flags |= SA_RESTART; sigaction(SIGTERM, &sterm, 0); #elif defined(Q_OS_WIN) SetConsoleCtrlHandler(closeSignalHandler, TRUE); #endif } CARLA_BRIDGE_START_NAMESPACE // ------------------------------------------------------------------------- // client class CarlaPluginClient : public CarlaClient { public: CarlaPluginClient(CarlaToolkit* const toolkit) : CarlaClient(toolkit) { engine = nullptr; plugin = nullptr; } ~CarlaPluginClient() { } void setStuff(CarlaBackend::CarlaEngine* const engine_, CarlaBackend::CarlaPlugin* const plugin_) { Q_ASSERT(engine_); Q_ASSERT(plugin_); engine = engine_; plugin = plugin_; } // --------------------------------------------------------------------- // processing void setParameter(const int32_t rindex, const double value) { qDebug("CarlaPluginClient::setParameter(%i, %g)", rindex, value); Q_ASSERT(plugin); if (! plugin) return; plugin->setParameterValueByRIndex(rindex, value, true, true, false); } void setProgram(const uint32_t index) { qDebug("CarlaPluginClient::setProgram(%i)", index); Q_ASSERT(engine); Q_ASSERT(plugin); Q_ASSERT(index < plugin->programCount()); if (! (plugin && engine)) return; if (index >= plugin->programCount()) return; plugin->setProgram(index, true, true, false, true); double value; for (uint32_t i=0; i < plugin->parameterCount(); i++) { value = plugin->getParameterValue(i); engine->osc_send_bridge_set_parameter_value(i, value); engine->osc_send_bridge_set_default_value(i, value); } } void setMidiProgram(const uint32_t index) { qDebug("CarlaPluginClient::setMidiProgram(%i)", index); Q_ASSERT(engine); Q_ASSERT(plugin); if (! (plugin && engine)) return; plugin->setMidiProgram(index, true, true, false, true); double value; for (uint32_t i=0; i < plugin->parameterCount(); i++) { value = plugin->getParameterValue(i); engine->osc_send_bridge_set_parameter_value(i, value); engine->osc_send_bridge_set_default_value(i, value); } } void noteOn(const uint8_t channel, const uint8_t note, const uint8_t velo) { qDebug("CarlaPluginClient::noteOn(%i, %i, %i)", channel, note, velo); Q_ASSERT(plugin); Q_ASSERT(velo > 0); if (! plugin) return; plugin->sendMidiSingleNote(channel, note, velo, true, true, false); } void noteOff(const uint8_t channel, const uint8_t note) { qDebug("CarlaPluginClient::noteOff(%i, %i)", channel, note); Q_ASSERT(plugin); if (! plugin) return; plugin->sendMidiSingleNote(channel, note, 0, true, true, false); } // --------------------------------------------------------------------- // plugin void saveNow() { qDebug("CarlaPluginClient::saveNow()"); Q_ASSERT(plugin); if (! plugin) return; plugin->prepareForSave(); #if 0 for (uint32_t i=0; i < CARLA_PLUGIN->customDataCount(); i++) { const CustomData* const cdata = CARLA_PLUGIN->customData(i); osc_send_bridge_custom_data(customdatatype2str(cdata->type), cdata->key, cdata->value); } if (CARLA_PLUGIN->hints() & PLUGIN_USES_CHUNKS) { void* data = nullptr; int32_t dataSize = CARLA_PLUGIN->chunkData(&data); if (data && dataSize >= 4) { QString filePath; filePath += "/tmp/.CarlaChunk_"; filePath += CARLA_PLUGIN->name(); QFile file(filePath); if (file.open(QIODevice::WriteOnly)) { QByteArray chunk((const char*)data, dataSize); file.write(chunk); file.close(); osc_send_bridge_chunk_data(filePath.toUtf8().constData()); } } } osc_send_configure(CARLA_BRIDGE_MSG_SAVED, ""); #endif } void setCustomData(const char* const type, const char* const key, const char* const value) { qDebug("CarlaPluginClient::setCustomData(\"%s\", \"%s\", \"%s\")", type, key, value); Q_ASSERT(plugin); if (! plugin) return; plugin->setCustomData(CarlaBackend::getCustomDataStringType(type), key, value, true); } void setChunkData(const char* const filePath) { qDebug("CarlaPluginClient::setChunkData(\"%s\")", filePath); Q_ASSERT(plugin); if (! plugin) return; Q_UNUSED(filePath); #if 0 nextChunkFilePath = strdup(filePath); while (nextChunkFilePath) carla_msleep(25); #endif } // --------------------------------------------------------------------- // idle void idle() { Q_ASSERT(plugin); if (! plugin) return; plugin->idleGui(); //plugin->showGui(true); } void showGui(const bool yesNo) { qDebug("CarlaPluginClient::showGui(%s)", bool2str(yesNo)); Q_ASSERT(plugin); if (! plugin) return; plugin->showGui(yesNo); } // --------------------------------------------------------------------- // callback void handleCallback(const CarlaBackend::CallbackType action, const int value1, const int value2, const double value3) { qDebug("CarlaPluginClient::handleCallback(%s, %i, %i, %g)", CarlaBackend::CallbackType2str(action), value1, value2, value3); // FIXME - those OSC calls should be on the engine switch (action) { case CarlaBackend::CALLBACK_PARAMETER_VALUE_CHANGED: engine->osc_send_bridge_set_parameter_value(value1, value3); break; case CarlaBackend::CALLBACK_PROGRAM_CHANGED: engine->osc_send_bridge_set_program(value1); break; case CarlaBackend::CALLBACK_MIDI_PROGRAM_CHANGED: engine->osc_send_bridge_set_midi_program(value1); break; case CarlaBackend::CALLBACK_NOTE_ON: { //uint8_t mdata[4] = { 0, MIDI_STATUS_NOTE_ON, (uint8_t)value1, (uint8_t)value2 }; //osc_send_midi(mdata); break; } case CarlaBackend::CALLBACK_NOTE_OFF: { //uint8_t mdata[4] = { 0, MIDI_STATUS_NOTE_OFF, (uint8_t)value1, (uint8_t)value2 }; //osc_send_midi(mdata); break; } case CarlaBackend::CALLBACK_SHOW_GUI: //if (value1 == 0) //sendOscConfigure(CarlaBackend::CARLA_BRIDGE_MSG_HIDE_GUI, ""); //quequeMessage(MESSAGE_QUIT, 0, 0, 0.0); break; case CarlaBackend::CALLBACK_RESIZE_GUI: qDebug("resize callback-------------------------------------------------------------------------------"); quequeMessage(MESSAGE_RESIZE_GUI, value1, value2, 0.0); //if (m_toolkit) // m_toolkit->resize(value1, value2); break; case CarlaBackend::CALLBACK_RELOAD_PARAMETERS: //if (CARLA_PLUGIN) //{ // for (uint32_t i=0; i < CARLA_PLUGIN->parameterCount(); i++) // { // osc_send_control(i, CARLA_PLUGIN->getParameterValue(i)); // } //} break; case CarlaBackend::CALLBACK_QUIT: //quequeMessage(MESSAGE_QUIT, 0, 0, 0.0); break; default: break; } Q_UNUSED(value1); Q_UNUSED(value2); Q_UNUSED(value3); } // --------------------------------------------------------------------- static void callback(void* const ptr, CarlaBackend::CallbackType const action, const unsigned short, const int value1, const int value2, const double value3) { Q_ASSERT(ptr); if (! ptr) return; CarlaPluginClient* const client = (CarlaPluginClient*)ptr; client->handleCallback(action, value1, value2, value3); } private: CarlaBackend::CarlaEngine* engine; CarlaBackend::CarlaPlugin* plugin; }; // ------------------------------------------------------------------------- // toolkit class BridgeApplication : public QApplication { public: BridgeApplication() : QApplication(qargc, qargv) { msgTimer = 0; m_client = nullptr; } void exec(CarlaPluginClient* const client) { m_client = client; msgTimer = startTimer(50); QApplication::exec(); } protected: void timerEvent(QTimerEvent* const event) { if (qcloseNow) return quit(); if (event->timerId() == msgTimer) { if (m_client) { m_client->idle(); if (! m_client->runMessages()) killTimer(msgTimer); } } QApplication::timerEvent(event); } private: int msgTimer; CarlaPluginClient* m_client; }; class CarlaToolkitPlugin : public CarlaToolkit { public: CarlaToolkitPlugin() : CarlaToolkit("carla-bridge-plugin") { qDebug("CarlaToolkitPlugin::CarlaToolkitPlugin()"); app = nullptr; dialog = nullptr; m_resizable = false; } ~CarlaToolkitPlugin() { qDebug("CarlaToolkitPlugin::~CarlaToolkitPlugin()"); Q_ASSERT(! app); } void init() { qDebug("CarlaToolkitPlugin::init()"); Q_ASSERT(! app); app = new BridgeApplication; } void exec(CarlaClient* const client, const bool showGui) { qDebug("CarlaToolkitPlugin::exec(%p)", client); Q_ASSERT(app); Q_ASSERT(client); m_client = client; if (showGui) { show(); } else { m_client->sendOscUpdate(); m_client->sendOscBridgeUpdate(); } app->exec((CarlaPluginClient*)client); } void quit() { qDebug("CarlaToolkitPlugin::quit()"); Q_ASSERT(app); if (dialog) { dialog->close(); delete dialog; dialog = nullptr; } if (app) { if (! app->closingDown()) app->quit(); delete app; app = nullptr; } } void show() { qDebug("CarlaToolkitPlugin::show()"); if (m_client) ((CarlaPluginClient*)m_client)->showGui(true); if (dialog) dialog->show(); } void hide() { qDebug("CarlaToolkitPlugin::hide()"); if (m_client) ((CarlaPluginClient*)m_client)->showGui(false); if (dialog) dialog->show(); } void resize(int width, int height) { qDebug("CarlaToolkitPlugin::resize(%i, %i)", width, height); Q_ASSERT(dialog); if (! dialog) return; if (m_resizable) dialog->resize(width, height); else dialog->setFixedSize(width, height); } // --------------------------------------------------------------------- void createWindow(const char* const pluginName, const bool createLayout, const bool resizable) { qDebug("CarlaToolkitPlugin::createWindow(%s, %s, %s)", pluginName, bool2str(createLayout), bool2str(resizable)); Q_ASSERT(pluginName); m_resizable = resizable; dialog = new QDialog(nullptr); resize(10, 10); if (createLayout) { QVBoxLayout* const layout = new QVBoxLayout(dialog); dialog->setContentsMargins(0, 0, 0, 0); dialog->setLayout(layout); } dialog->setWindowTitle(QString("%1 (GUI)").arg(pluginName)); #ifdef Q_OS_WIN if (! resizable) dialog->setWindowFlags(dialog->windowFlags() | Qt::MSWindowsFixedSizeDialogHint); #endif } CarlaBackend::GuiDataHandle getWindowHandle() const { return dialog; } // --------------------------------------------------------------------- private: BridgeApplication* app; QDialog* dialog; bool m_resizable; }; CarlaToolkit* CarlaToolkit::createNew(const char* const) { return new CarlaToolkitPlugin; } // ------------------------------------------------------------------------- CARLA_BRIDGE_END_NAMESPACE int main(int argc, char* argv[]) { if (argc != 6) { qWarning("usage: %s