Mercurial > hg > pub > prymula > com
diff DPF-Prymula-audioplugins/dpf/distrho/src/DistrhoUIDSSI.cpp @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DPF-Prymula-audioplugins/dpf/distrho/src/DistrhoUIDSSI.cpp Mon Oct 16 21:53:34 2023 +0200 @@ -0,0 +1,517 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "DistrhoUIInternal.hpp" + +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS +# error DSSI UIs do not support direct access! +#endif + +#include "../extra/Sleep.hpp" + +#include <lo/lo.h> + +START_NAMESPACE_DISTRHO + +#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT +static constexpr const sendNoteFunc sendNoteCallback = nullptr; +#endif + +// ----------------------------------------------------------------------- + +struct OscData { + lo_address addr; + const char* path; + lo_server server; + + OscData() + : addr(nullptr), + path(nullptr), + server(nullptr) {} + + void idle() const + { + if (server == nullptr) + return; + + while (lo_server_recv_noblock(server, 0) != 0) {} + } + + void send_configure(const char* const key, const char* const value) const + { + char targetPath[std::strlen(path)+11]; + std::strcpy(targetPath, path); + std::strcat(targetPath, "/configure"); + lo_send(addr, targetPath, "ss", key, value); + } + + void send_control(const int32_t index, const float value) const + { + char targetPath[std::strlen(path)+9]; + std::strcpy(targetPath, path); + std::strcat(targetPath, "/control"); + lo_send(addr, targetPath, "if", index, value); + } + + void send_midi(uchar data[4]) const + { + char targetPath[std::strlen(path)+6]; + std::strcpy(targetPath, path); + std::strcat(targetPath, "/midi"); + lo_send(addr, targetPath, "m", data); + } + + void send_update(const char* const url) const + { + char targetPath[std::strlen(path)+8]; + std::strcpy(targetPath, path); + std::strcat(targetPath, "/update"); + lo_send(addr, targetPath, "s", url); + } + + void send_exiting() const + { + char targetPath[std::strlen(path)+9]; + std::strcpy(targetPath, path); + std::strcat(targetPath, "/exiting"); + lo_send(addr, targetPath, ""); + } +}; + +// ----------------------------------------------------------------------- + +class UIDssi : public DGL_NAMESPACE::IdleCallback +{ +public: + UIDssi(const OscData& oscData, const char* const uiTitle, const double sampleRate) + : fUI(this, 0, sampleRate, nullptr, + setParameterCallback, setStateCallback, sendNoteCallback, nullptr, nullptr), + fHostClosed(false), + fOscData(oscData) + { + fUI.setWindowTitle(uiTitle); + } + + ~UIDssi() + { + if (fOscData.server != nullptr && ! fHostClosed) + fOscData.send_exiting(); + } + + void exec_start() + { + fUI.exec(this); + } + + void idleCallback() override + { + fOscData.idle(); + + if (fHostClosed) + return; + + fUI.exec_idle(); + } + + // ------------------------------------------------------------------- + +#if DISTRHO_PLUGIN_WANT_STATE + void dssiui_configure(const char* key, const char* value) + { + fUI.stateChanged(key, value); + } +#endif + + void dssiui_control(ulong index, float value) + { + fUI.parameterChanged(index, value); + } + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + void dssiui_program(ulong bank, ulong program) + { + fUI.programLoaded(bank * 128 + program); + } +#endif + + void dssiui_samplerate(const double sampleRate) + { + fUI.setSampleRate(sampleRate, true); + } + + void dssiui_show(const bool focus = false) + { + fUI.setWindowVisible(true); + + if (focus) + fUI.focus(); + } + + void dssiui_hide() + { + fUI.setWindowVisible(false); + } + + void dssiui_quit() + { + fHostClosed = true; + fUI.quit(); + } + + // ------------------------------------------------------------------- + +protected: + void setParameterValue(const uint32_t rindex, const float value) + { + if (fOscData.server == nullptr) + return; + + fOscData.send_control(rindex, value); + } + + void setState(const char* const key, const char* const value) + { + if (fOscData.server == nullptr) + return; + + fOscData.send_configure(key, value); + } + +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) + { + if (fOscData.server == nullptr) + return; + if (channel > 0xF) + return; + + uint8_t mdata[4] = { + 0, + static_cast<uint8_t>(channel + (velocity != 0 ? 0x90 : 0x80)), + note, + velocity + }; + fOscData.send_midi(mdata); + } +#endif + +private: + UIExporter fUI; + bool fHostClosed; + + const OscData& fOscData; + + // ------------------------------------------------------------------- + // Callbacks + + #define uiPtr ((UIDssi*)ptr) + + static void setParameterCallback(void* ptr, uint32_t rindex, float value) + { + uiPtr->setParameterValue(rindex, value); + } + + static void setStateCallback(void* ptr, const char* key, const char* value) + { + uiPtr->setState(key, value); + } + +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) + { + uiPtr->sendNote(channel, note, velocity); + } +#endif + + #undef uiPtr +}; + +// ----------------------------------------------------------------------- + +static OscData gOscData; +static const char* gUiTitle = nullptr; +static UIDssi* globalUI = nullptr; +static double sampleRate = 0.0; + +static void initUiIfNeeded() +{ + if (globalUI != nullptr) + return; + + if (sampleRate == 0.0) + sampleRate = 44100.0; + + globalUI = new UIDssi(gOscData, gUiTitle, sampleRate); +} + +// ----------------------------------------------------------------------- + +int osc_debug_handler(const char* path, const char*, lo_arg**, int, lo_message, void*) +{ + d_debug("osc_debug_handler(\"%s\")", path); + return 0; + +#ifndef DEBUG + // unused + (void)path; +#endif +} + +void osc_error_handler(int num, const char* msg, const char* path) +{ + d_stderr("osc_error_handler(%i, \"%s\", \"%s\")", num, msg, path); +} + +#if DISTRHO_PLUGIN_WANT_STATE +int osc_configure_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) +{ + const char* const key = &argv[0]->s; + const char* const value = &argv[1]->s; + d_debug("osc_configure_handler(\"%s\", \"%s\")", key, value); + + initUiIfNeeded(); + + globalUI->dssiui_configure(key, value); + + return 0; +} +#endif + +int osc_control_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) +{ + const int32_t rindex = argv[0]->i; + const float value = argv[1]->f; + d_debug("osc_control_handler(%i, %f)", rindex, value); + + int32_t index = rindex - DISTRHO_PLUGIN_NUM_INPUTS - DISTRHO_PLUGIN_NUM_OUTPUTS; + + // latency +#if DISTRHO_PLUGIN_WANT_LATENCY + index -= 1; +#endif + + if (index < 0) + return 0; + + initUiIfNeeded(); + + globalUI->dssiui_control(index, value); + + return 0; +} + +#if DISTRHO_PLUGIN_WANT_PROGRAMS +int osc_program_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) +{ + const int32_t bank = argv[0]->i; + const int32_t program = argv[1]->f; + d_debug("osc_program_handler(%i, %i)", bank, program); + + initUiIfNeeded(); + + globalUI->dssiui_program(bank, program); + + return 0; +} +#endif + +int osc_sample_rate_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) +{ + sampleRate = argv[0]->i; + d_debug("osc_sample_rate_handler(%f)", sampleRate); + + if (globalUI != nullptr) + globalUI->dssiui_samplerate(sampleRate); + + return 0; +} + +int osc_show_handler(const char*, const char*, lo_arg**, int, lo_message, void*) +{ + d_debug("osc_show_handler()"); + + initUiIfNeeded(); + + globalUI->dssiui_show(); + + return 0; +} + +int osc_hide_handler(const char*, const char*, lo_arg**, int, lo_message, void*) +{ + d_debug("osc_hide_handler()"); + + if (globalUI != nullptr) + globalUI->dssiui_hide(); + + return 0; +} + +int osc_quit_handler(const char*, const char*, lo_arg**, int, lo_message, void*) +{ + d_debug("osc_quit_handler()"); + + if (globalUI != nullptr) + globalUI->dssiui_quit(); + + return 0; +} + +END_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + USE_NAMESPACE_DISTRHO + + // dummy test mode + if (argc == 1) + { + gUiTitle = "DSSI UI Test"; + + initUiIfNeeded(); + globalUI->dssiui_show(true); + globalUI->exec_start(); + + delete globalUI; + globalUI = nullptr; + + return 0; + } + + if (argc != 5) + { + fprintf(stderr, "Usage: %s <osc-url> <plugin-dll> <plugin-label> <instance-name>\n", argv[0]); + return 1; + } + + const char* oscUrl = argv[1]; + const char* uiTitle = argv[4]; + + char* const oscHost = lo_url_get_hostname(oscUrl); + char* const oscPort = lo_url_get_port(oscUrl); + char* const oscPath = lo_url_get_path(oscUrl); + size_t oscPathSize = strlen(oscPath); + lo_address oscAddr = lo_address_new(oscHost, oscPort); + lo_server oscServer = lo_server_new_with_proto(nullptr, LO_UDP, osc_error_handler); + + char* const oscServerPath = lo_server_get_url(oscServer); + + char pluginPath[strlen(oscServerPath)+oscPathSize]; + strcpy(pluginPath, oscServerPath); + strcat(pluginPath, oscPath+1); + +#if DISTRHO_PLUGIN_WANT_STATE + char oscPathConfigure[oscPathSize+11]; + strcpy(oscPathConfigure, oscPath); + strcat(oscPathConfigure, "/configure"); + lo_server_add_method(oscServer, oscPathConfigure, "ss", osc_configure_handler, nullptr); +#endif + + char oscPathControl[oscPathSize+9]; + strcpy(oscPathControl, oscPath); + strcat(oscPathControl, "/control"); + lo_server_add_method(oscServer, oscPathControl, "if", osc_control_handler, nullptr); + + d_stdout("oscServerPath: \"%s\"", oscServerPath); + d_stdout("pluginPath: \"%s\"", pluginPath); + d_stdout("oscPathControl: \"%s\"", oscPathControl); + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + char oscPathProgram[oscPathSize+9]; + strcpy(oscPathProgram, oscPath); + strcat(oscPathProgram, "/program"); + lo_server_add_method(oscServer, oscPathProgram, "ii", osc_program_handler, nullptr); +#endif + + char oscPathSampleRate[oscPathSize+13]; + strcpy(oscPathSampleRate, oscPath); + strcat(oscPathSampleRate, "/sample-rate"); + lo_server_add_method(oscServer, oscPathSampleRate, "i", osc_sample_rate_handler, nullptr); + + char oscPathShow[oscPathSize+6]; + strcpy(oscPathShow, oscPath); + strcat(oscPathShow, "/show"); + lo_server_add_method(oscServer, oscPathShow, "", osc_show_handler, nullptr); + + char oscPathHide[oscPathSize+6]; + strcpy(oscPathHide, oscPath); + strcat(oscPathHide, "/hide"); + lo_server_add_method(oscServer, oscPathHide, "", osc_hide_handler, nullptr); + + char oscPathQuit[oscPathSize+6]; + strcpy(oscPathQuit, oscPath); + strcat(oscPathQuit, "/quit"); + lo_server_add_method(oscServer, oscPathQuit, "", osc_quit_handler, nullptr); + + lo_server_add_method(oscServer, nullptr, nullptr, osc_debug_handler, nullptr); + + gUiTitle = uiTitle; + + gOscData.addr = oscAddr; + gOscData.path = oscPath; + gOscData.server = oscServer; + gOscData.send_update(pluginPath); + + // wait for init + for (int i=0; i < 100; ++i) + { + lo_server_recv(oscServer); + + if (sampleRate != 0.0 || globalUI != nullptr) + break; + + d_msleep(50); + } + + int ret = 1; + + if (sampleRate != 0.0 || globalUI != nullptr) + { + initUiIfNeeded(); + + globalUI->exec_start(); + + delete globalUI; + globalUI = nullptr; + + ret = 0; + } + +#if DISTRHO_PLUGIN_WANT_STATE + lo_server_del_method(oscServer, oscPathConfigure, "ss"); +#endif + lo_server_del_method(oscServer, oscPathControl, "if"); +#if DISTRHO_PLUGIN_WANT_PROGRAMS + lo_server_del_method(oscServer, oscPathProgram, "ii"); +#endif + lo_server_del_method(oscServer, oscPathSampleRate, "i"); + lo_server_del_method(oscServer, oscPathShow, ""); + lo_server_del_method(oscServer, oscPathHide, ""); + lo_server_del_method(oscServer, oscPathQuit, ""); + lo_server_del_method(oscServer, nullptr, nullptr); + + std::free(oscServerPath); + std::free(oscHost); + std::free(oscPort); + std::free(oscPath); + + lo_address_free(oscAddr); + lo_server_free(oscServer); + + return ret; +}