| @@ -899,16 +899,6 @@ CARLA_EXPORT const char* carla_get_library_filename(); | |||||
| */ | */ | ||||
| CARLA_EXPORT const char* carla_get_library_folder(); | CARLA_EXPORT const char* carla_get_library_folder(); | ||||
| /*! | |||||
| * Initialize NSM (announce). | |||||
| */ | |||||
| CARLA_EXPORT bool carla_nsm_init(int pid, const char* executableName); | |||||
| /*! | |||||
| * Allow NSM callbacks. | |||||
| */ | |||||
| CARLA_EXPORT void carla_nsm_ready(int action); | |||||
| /** @} */ | /** @} */ | ||||
| #endif /* CARLA_HOST_H_INCLUDED */ | #endif /* CARLA_HOST_H_INCLUDED */ | ||||
| @@ -16,6 +16,7 @@ | |||||
| */ | */ | ||||
| #include "CarlaHost.h" | #include "CarlaHost.h" | ||||
| #include "CarlaUtils.hpp" | |||||
| #include "juce_core.h" | #include "juce_core.h" | ||||
| @@ -105,7 +106,7 @@ const char* carla_get_library_filename() | |||||
| if (ret.isEmpty()) | if (ret.isEmpty()) | ||||
| { | { | ||||
| using juce::File; | |||||
| using namespace juce; | |||||
| ret = File(File::getSpecialLocation(File::currentExecutableFile)).getFullPathName().toRawUTF8(); | ret = File(File::getSpecialLocation(File::currentExecutableFile)).getFullPathName().toRawUTF8(); | ||||
| } | } | ||||
| @@ -120,7 +121,7 @@ const char* carla_get_library_folder() | |||||
| if (ret.isEmpty()) | if (ret.isEmpty()) | ||||
| { | { | ||||
| using juce::File; | |||||
| using namespace juce; | |||||
| ret = File(File::getSpecialLocation(File::currentExecutableFile).getParentDirectory()).getFullPathName().toRawUTF8(); | ret = File(File::getSpecialLocation(File::currentExecutableFile).getParentDirectory()).getFullPathName().toRawUTF8(); | ||||
| } | } | ||||
| @@ -28,7 +28,6 @@ | |||||
| #include "CarlaBase64Utils.hpp" | #include "CarlaBase64Utils.hpp" | ||||
| #ifdef HAVE_LIBLO | #ifdef HAVE_LIBLO | ||||
| # include "CarlaOscUtils.hpp" | |||||
| #endif | #endif | ||||
| #include "juce_audio_formats.h" | #include "juce_audio_formats.h" | ||||
| @@ -80,656 +79,7 @@ struct CarlaBackendStandalone { | |||||
| CARLA_DECLARE_NON_COPY_STRUCT(CarlaBackendStandalone) | CARLA_DECLARE_NON_COPY_STRUCT(CarlaBackendStandalone) | ||||
| }; | }; | ||||
| static CarlaBackendStandalone gStandalone; | |||||
| #ifdef HAVE_LIBLO | |||||
| // ------------------------------------------------------------------------------------------------------------------- | |||||
| // NSM support | |||||
| #define NSM_API_VERSION_MAJOR 1 | |||||
| #define NSM_API_VERSION_MINOR 2 | |||||
| #define NSM_CLIENT_FEATURES ":switch:" | |||||
| //#define NSM_CLIENT_FEATURES ":switch:optional-gui:" | |||||
| class CarlaNSM | |||||
| { | |||||
| public: | |||||
| CarlaNSM() noexcept | |||||
| : fOscAddress(nullptr), | |||||
| fOscServer(nullptr), | |||||
| fOscServerThread(nullptr), | |||||
| fClientNameId(), | |||||
| fProjectPath(), | |||||
| fHasBroadcast(false), | |||||
| fHasOptionalGui(false), | |||||
| fHasServerControl(false), | |||||
| fStarted(), | |||||
| fReadyActionOpen(true), | |||||
| fReadyActionSave(true) {} | |||||
| ~CarlaNSM() | |||||
| { | |||||
| CARLA_SAFE_ASSERT(fReadyActionOpen); | |||||
| CARLA_SAFE_ASSERT(fReadyActionSave); | |||||
| if (fOscServerThread != nullptr) | |||||
| { | |||||
| lo_server_thread_stop(fOscServerThread); | |||||
| lo_server_thread_free(fOscServerThread); | |||||
| fOscServerThread = nullptr; | |||||
| fOscServer = nullptr; | |||||
| } | |||||
| if (fOscAddress != nullptr) | |||||
| { | |||||
| lo_address_free(fOscAddress); | |||||
| fOscAddress = nullptr; | |||||
| } | |||||
| } | |||||
| bool announce(const int pid, const char* const executableName) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(pid != 0, false); | |||||
| CARLA_SAFE_ASSERT_RETURN(executableName != nullptr && executableName[0] != '\0', false); | |||||
| const char* const NSM_URL(std::getenv("NSM_URL")); | |||||
| if (NSM_URL == nullptr) | |||||
| return false; | |||||
| const lo_address addr = lo_address_new_from_url(NSM_URL); | |||||
| CARLA_SAFE_ASSERT_RETURN(addr != nullptr, false); | |||||
| const int proto = lo_address_get_protocol(addr); | |||||
| if (fOscServerThread == nullptr) | |||||
| { | |||||
| // create new OSC server | |||||
| fOscServerThread = lo_server_thread_new_with_proto(nullptr, proto, _osc_error_handler); | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServerThread != nullptr, false); | |||||
| // register message handlers | |||||
| lo_server_thread_add_method(fOscServerThread, "/error", "sis", _error_handler, this); | |||||
| lo_server_thread_add_method(fOscServerThread, "/reply", "ssss", _reply_handler, this); | |||||
| lo_server_thread_add_method(fOscServerThread, "/nsm/client/open", "sss", _open_handler, this); | |||||
| lo_server_thread_add_method(fOscServerThread, "/nsm/client/save", "", _save_handler, this); | |||||
| lo_server_thread_add_method(fOscServerThread, "/nsm/client/session_is_loaded", "", _loaded_handler, this); | |||||
| lo_server_thread_add_method(fOscServerThread, "/nsm/client/show_optional_gui", "", _show_gui_handler, this); | |||||
| lo_server_thread_add_method(fOscServerThread, "/nsm/client/hide_optional_gui", "", _hide_gui_handler, this); | |||||
| lo_server_thread_add_method(fOscServerThread, nullptr, nullptr, _broadcast_handler, this); | |||||
| fOscServer = lo_server_thread_get_server(fOscServerThread); | |||||
| } | |||||
| #ifndef BUILD_ANSI_TEST | |||||
| lo_send_from(addr, fOscServer, LO_TT_IMMEDIATE, "/nsm/server/announce", "sssiii", | |||||
| "Carla", NSM_CLIENT_FEATURES, executableName, NSM_API_VERSION_MAJOR, NSM_API_VERSION_MINOR, pid); | |||||
| #endif | |||||
| lo_address_free(addr); | |||||
| return true; | |||||
| } | |||||
| void ready(const int action) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServerThread != nullptr,); | |||||
| switch (action) | |||||
| { | |||||
| case -1: // init | |||||
| CARLA_SAFE_ASSERT_BREAK(! fStarted); | |||||
| fStarted = true; | |||||
| lo_server_thread_start(fOscServerThread); | |||||
| break; | |||||
| case 0: // error | |||||
| break; | |||||
| case 1: // reply | |||||
| break; | |||||
| case 2: // open | |||||
| fReadyActionOpen = true; | |||||
| break; | |||||
| case 3: // save | |||||
| fReadyActionSave = true; | |||||
| break; | |||||
| case 4: // session loaded | |||||
| break; | |||||
| case 5: // show gui | |||||
| CARLA_SAFE_ASSERT_BREAK(fOscAddress != nullptr); | |||||
| CARLA_SAFE_ASSERT_BREAK(fOscServer != nullptr); | |||||
| lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_shown", ""); | |||||
| break; | |||||
| case 6: // hide gui | |||||
| CARLA_SAFE_ASSERT_BREAK(fOscAddress != nullptr); | |||||
| CARLA_SAFE_ASSERT_BREAK(fOscServer != nullptr); | |||||
| lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_hidden", ""); | |||||
| break; | |||||
| } | |||||
| } | |||||
| static CarlaNSM& getInstance() | |||||
| { | |||||
| static CarlaNSM sInstance; | |||||
| return sInstance; | |||||
| } | |||||
| protected: | |||||
| int handleError(const char* const method, const int code, const char* const message) | |||||
| { | |||||
| carla_stdout("CarlaNSM::handleError(\"%s\", %i, \"%s\")", method, code, message); | |||||
| if (gStandalone.engineCallback != nullptr) | |||||
| gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 0, code, 0.0f, message); | |||||
| return 1; | |||||
| // may be unused | |||||
| (void)method; | |||||
| } | |||||
| int handleReply(const char* const method, const char* const message, const char* const smName, const char* const features, | |||||
| const lo_message msg) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServerThread != nullptr, 1); | |||||
| carla_stdout("CarlaNSM::handleReply(\"%s\", \"%s\", \"%s\", \"%s\")", method, message, smName, features); | |||||
| if (std::strcmp(method, "/nsm/server/announce") == 0) | |||||
| { | |||||
| char* const addressURL(lo_address_get_url(lo_message_get_source(msg))); | |||||
| CARLA_SAFE_ASSERT_RETURN(addressURL != nullptr, 0); | |||||
| if (fOscAddress != nullptr) | |||||
| lo_address_free(fOscAddress); | |||||
| fOscAddress = lo_address_new_from_url(addressURL); | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 0); | |||||
| fHasBroadcast = std::strstr(features, ":broadcast:") != nullptr; | |||||
| fHasOptionalGui = std::strstr(features, ":optional-gui:") != nullptr; | |||||
| fHasServerControl = std::strstr(features, ":server_control:") != nullptr; | |||||
| // UI starts visible | |||||
| if (fHasOptionalGui) | |||||
| lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_shown", ""); | |||||
| carla_stdout("Carla started via '%s', message: %s", smName, message); | |||||
| if (gStandalone.engineCallback != nullptr) | |||||
| { | |||||
| int flags = 0; | |||||
| if (fHasBroadcast) | |||||
| flags |= 1 << 0; | |||||
| if (fHasOptionalGui) | |||||
| flags |= 1 << 1; | |||||
| if (fHasServerControl) | |||||
| flags |= 1 << 2; | |||||
| gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 1, flags, 0.0f, smName); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| carla_stdout("Got unknown NSM reply method '%s'", method); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| int handleOpen(const char* const projectPath, const char* const displayName, const char* const clientNameId) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1); | |||||
| carla_stdout("CarlaNSM::handleOpen(\"%s\", \"%s\", \"%s\")", projectPath, displayName, clientNameId); | |||||
| if (gStandalone.engineCallback != nullptr) | |||||
| { | |||||
| fReadyActionOpen = false; | |||||
| gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 2, 0, 0.0f, projectPath); | |||||
| for (; ! fReadyActionOpen;) | |||||
| carla_msleep(10); | |||||
| } | |||||
| else | |||||
| { | |||||
| using namespace juce; | |||||
| if (carla_is_engine_running()) | |||||
| carla_engine_close(); | |||||
| carla_engine_init("JACK", clientNameId); | |||||
| fProjectPath = projectPath; | |||||
| fProjectPath += ".carxp"; | |||||
| const String jfilename = String(CharPointer_UTF8(fProjectPath)); | |||||
| if (File(jfilename).existsAsFile()) | |||||
| carla_load_project(fProjectPath); | |||||
| } | |||||
| fClientNameId = clientNameId; | |||||
| lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "ss", "/nsm/client/open", "OK"); | |||||
| // Broadcast ourselves | |||||
| if (fHasBroadcast) | |||||
| { | |||||
| char* const oscServerAddress(lo_server_get_url(fOscServer)); | |||||
| if (lo_message msg = lo_message_new()) | |||||
| { | |||||
| lo_message_add(msg, "sssss", | |||||
| "/non/hello", | |||||
| oscServerAddress, | |||||
| "Carla", | |||||
| CARLA_VERSION_STRING, | |||||
| fClientNameId.buffer()); | |||||
| lo_send_message_from(fOscAddress, fOscServer, "/nsm/server/broadcast", msg); | |||||
| lo_message_free(msg); | |||||
| } | |||||
| std::free(oscServerAddress); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| int handleSave() | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1); | |||||
| carla_stdout("CarlaNSM::handleSave()"); | |||||
| if (gStandalone.engineCallback != nullptr) | |||||
| { | |||||
| fReadyActionSave = false; | |||||
| gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 3, 0, 0.0f, nullptr); | |||||
| for (; ! fReadyActionSave;) | |||||
| carla_msleep(10); | |||||
| } | |||||
| else | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fProjectPath.isNotEmpty(), 0); | |||||
| carla_save_project(fProjectPath); | |||||
| } | |||||
| lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "ss", "/nsm/client/save", "OK"); | |||||
| return 0; | |||||
| } | |||||
| int handleSessionIsLoaded() | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1); | |||||
| carla_stdout("CarlaNSM::handleSessionIsLoaded()"); | |||||
| if (gStandalone.engineCallback != nullptr) | |||||
| gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 4, 0, 0.0f, nullptr); | |||||
| return 0; | |||||
| } | |||||
| int handleShowOptionalGui() | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1); | |||||
| carla_stdout("CarlaNSM::handleShowOptionalGui()"); | |||||
| if (gStandalone.engineCallback != nullptr) | |||||
| gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 5, 0, 0.0f, nullptr); | |||||
| return 0; | |||||
| } | |||||
| int handleHideOptionalGui() | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1); | |||||
| carla_stdout("CarlaNSM::handleHideOptionalGui()"); | |||||
| if (gStandalone.engineCallback != nullptr) | |||||
| gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 6, 0, 0.0f, nullptr); | |||||
| return 0; | |||||
| } | |||||
| int handleBroadcast(const char* const path, const char* const types, lo_arg** const argv, const int argc, | |||||
| const lo_message msg) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(argc >= 0, 0); | |||||
| carla_stdout("CarlaNSM::handleBroadcast(%s, %s, %p, %i)", path, types, argv, argc); | |||||
| if (std::strcmp(path, "/non/hello") == 0) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 4, 0); | |||||
| CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "ssss") == 0, 0); | |||||
| const char* const url = &argv[0]->s; | |||||
| //const char* const name = &argv[1]->s; | |||||
| //const char* const version = &argv[2]->s; | |||||
| //const char* const clientId = &argv[3]->s; | |||||
| const lo_address targetAddress(lo_address_new_from_url(url)); | |||||
| CARLA_SAFE_ASSERT_RETURN(targetAddress != nullptr, 0); | |||||
| char* const oscServerAddress(lo_server_get_url(fOscServer)); | |||||
| CARLA_SAFE_ASSERT_RETURN(oscServerAddress != nullptr, 0); | |||||
| if (lo_message msg2 = lo_message_new()) | |||||
| { | |||||
| lo_message_add(msg2, "ss", | |||||
| fClientNameId.buffer(), | |||||
| oscServerAddress); | |||||
| lo_send_message_from(targetAddress, fOscServer, "/signal/hello", msg2); | |||||
| lo_message_free(msg2); | |||||
| carla_stdout("CarlaNSM::handleBroadcast - sent hello"); | |||||
| } | |||||
| lo_address_free(targetAddress); | |||||
| std::free(oscServerAddress); | |||||
| return 0; | |||||
| } | |||||
| if (std::strcmp(path, "/signal/hello") == 0) | |||||
| { | |||||
| carla_stdout("CarlaNSM::handleBroadcast - got hello"); | |||||
| //const char* const name = &argv[0]->s; | |||||
| const char* const url = &argv[1]->s; | |||||
| const lo_address targetAddress(lo_address_new_from_url(url)); | |||||
| CARLA_SAFE_ASSERT_RETURN(targetAddress != nullptr, 0); | |||||
| char* const oscServerAddress(lo_server_get_url(fOscServer)); | |||||
| CARLA_SAFE_ASSERT_RETURN(oscServerAddress != nullptr, 0); | |||||
| //lo_send_from(targetAddress, fOscServer, LO_TT_IMMEDIATE, "/signal/list", ""); | |||||
| if (lo_message msg2 = lo_message_new()) | |||||
| { | |||||
| lo_message_add(msg2, "ss", | |||||
| fClientNameId.buffer(), | |||||
| oscServerAddress); | |||||
| lo_send_message_from(targetAddress, fOscServer, "/signal/hello", msg2); | |||||
| lo_message_free(msg2); | |||||
| carla_stdout("CarlaNSM::handleBroadcast - sent hello"); | |||||
| } | |||||
| lo_address_free(targetAddress); | |||||
| std::free(oscServerAddress); | |||||
| return 0; | |||||
| } | |||||
| if (std::strcmp(path, "/signal/list") == 0) | |||||
| { | |||||
| carla_stdout("CarlaNSM::handleBroadcast - got list"); | |||||
| CARLA_SAFE_ASSERT_RETURN(carla_is_engine_running(), 0); | |||||
| //const char* prefix = nullptr; | |||||
| //if (argc > 0) | |||||
| //prefix = &argv[0]->s; | |||||
| const lo_address targetAddress(lo_message_get_source(msg)); | |||||
| CARLA_SAFE_ASSERT_RETURN(targetAddress != nullptr, 0); | |||||
| CarlaString stripName; | |||||
| for (uint32_t i = 0, pluginCount = carla_get_current_plugin_count(); i < pluginCount; ++i) | |||||
| { | |||||
| const CarlaPluginInfo* const pluginInfo(carla_get_plugin_info(i)); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(pluginInfo != nullptr); | |||||
| for (uint32_t j=0, paramCount = carla_get_parameter_count(i); j < paramCount; ++j) | |||||
| { | |||||
| const CarlaParameterInfo* const paramInfo(carla_get_parameter_info(i, j)); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(paramInfo != nullptr); | |||||
| const ParameterData* const paramData(carla_get_parameter_data(i, j)); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(paramData != nullptr); | |||||
| const ParameterRanges* const paramRanges(carla_get_parameter_ranges(i, j)); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(paramRanges != nullptr); | |||||
| if (paramData->type != CB::PARAMETER_INPUT && paramData->type != CB::PARAMETER_OUTPUT) | |||||
| continue; | |||||
| if ((paramData->hints & CB::PARAMETER_IS_ENABLED) == 0) | |||||
| continue; | |||||
| if ((paramData->hints & CB::PARAMETER_IS_AUTOMABLE) == 0) | |||||
| continue; | |||||
| if (paramData->hints & CB::PARAMETER_IS_READ_ONLY) | |||||
| continue; | |||||
| if (lo_message msg2 = lo_message_new()) | |||||
| { | |||||
| stripName = fClientNameId + "/strip/Unnamed/" + pluginInfo->name + "/" + paramInfo->name; | |||||
| stripName.replace(' ','_'); | |||||
| lo_message_add(msg2, "sssfff", | |||||
| path, | |||||
| stripName.buffer(), | |||||
| paramData->type == CB::PARAMETER_INPUT ? "in" : "out", | |||||
| paramRanges->min, | |||||
| paramRanges->max, | |||||
| paramRanges->def); | |||||
| lo_send_message_from(targetAddress, fOscServer, "/reply", msg2); | |||||
| lo_message_free(msg2); | |||||
| carla_stdout("CarlaNSM::handleBroadcast - sent list at %i : %i, path: %s", i+1, j+1, stripName.buffer()); | |||||
| } | |||||
| #if 0 | |||||
| if (prefix == nullptr /*|| strncmp(o->path(), prefix, std::strlen(prefix)) == 0*/) | |||||
| { | |||||
| lo_send_from(targetAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "sssfff", | |||||
| path, | |||||
| paramInfo->name, | |||||
| paramData->type == CB::PARAMETER_INPUT ? "in" : "out", | |||||
| paramRanges->min, | |||||
| paramRanges->max, | |||||
| paramRanges->def); | |||||
| } | |||||
| #endif | |||||
| } | |||||
| } | |||||
| if (lo_message msg2 = lo_message_new()) | |||||
| { | |||||
| lo_message_add(msg2, "s", path); | |||||
| lo_send_message_from(targetAddress, fOscServer, "/reply", msg2); | |||||
| lo_message_free(msg2); | |||||
| carla_stdout("CarlaNSM::handleBroadcast - sent list final"); | |||||
| } | |||||
| //lo_send_from(targetAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "s", path); | |||||
| //return 0; | |||||
| } | |||||
| #if 0 | |||||
| if (std::strcmp(path, "/reply") == 0) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc > 0, 0); | |||||
| const char* const method = &argv[0]->s; | |||||
| if (std::strcmp(method, "/signal/list") == 0) | |||||
| { | |||||
| carla_stdout("CarlaNSM::handleBroadcast - got new list"); | |||||
| if (argc == 1) | |||||
| return 0; | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 6, 0); | |||||
| const lo_address targetAddress(lo_message_get_source(msg)); | |||||
| CARLA_SAFE_ASSERT_RETURN(targetAddress != nullptr, 0); | |||||
| const char* const stripName = &argv[1]->s; | |||||
| const char* const orientation = &argv[2]->s; | |||||
| const float min = argv[3]->f; | |||||
| const float max = argv[4]->f; | |||||
| const float def = argv[5]->f; | |||||
| if (lo_message msg2 = lo_message_new()) | |||||
| { | |||||
| lo_message_add(msg2, "sssfff", | |||||
| method, | |||||
| stripName, | |||||
| orientation, | |||||
| min, | |||||
| max, | |||||
| def); | |||||
| lo_send_message_from(targetAddress, fOscServer, "/reply", msg2); | |||||
| lo_message_free(msg2); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| CARLA_SAFE_ASSERT(false); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| #endif | |||||
| // check if all args are strings | |||||
| for (int i=0; i<argc; ++i) | |||||
| { | |||||
| if (types[i] != 's') | |||||
| return 0; | |||||
| } | |||||
| for (int i=0; i<argc; ++i) | |||||
| carla_stdout("%i: %s", i+1, &argv[i]->s); | |||||
| return 0; | |||||
| } | |||||
| private: | |||||
| lo_address fOscAddress; | |||||
| lo_server fOscServer; | |||||
| lo_server_thread fOscServerThread; | |||||
| CarlaString fClientNameId; | |||||
| CarlaString fProjectPath; | |||||
| bool fHasBroadcast; | |||||
| bool fHasOptionalGui; | |||||
| bool fHasServerControl; | |||||
| bool fStarted; | |||||
| volatile bool fReadyActionOpen; | |||||
| volatile bool fReadyActionSave; | |||||
| #define handlePtr ((CarlaNSM*)data) | |||||
| static void _osc_error_handler(int num, const char* msg, const char* path) | |||||
| { | |||||
| carla_stderr2("CarlaNSM::_osc_error_handler(%i, \"%s\", \"%s\")", num, msg, path); | |||||
| } | |||||
| static int _error_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message, void* data) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 3, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "sis") == 0, 1); | |||||
| const char* const method = &argv[0]->s; | |||||
| const int code = argv[1]->i; | |||||
| const char* const message = &argv[2]->s; | |||||
| return handlePtr->handleError(method, code, message); | |||||
| } | |||||
| static int _reply_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message msg, void* data) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 4, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "ssss") == 0, 1); | |||||
| const char* const method = &argv[0]->s; | |||||
| const char* const message = &argv[1]->s; | |||||
| const char* const smName = &argv[2]->s; | |||||
| const char* const features = &argv[3]->s; | |||||
| return handlePtr->handleReply(method, message, smName, features, msg); | |||||
| } | |||||
| static int _open_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message, void* data) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 3, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "sss") == 0, 1); | |||||
| const char* const projectPath = &argv[0]->s; | |||||
| const char* const displayName = &argv[1]->s; | |||||
| const char* const clientNameId = &argv[2]->s; | |||||
| return handlePtr->handleOpen(projectPath, displayName, clientNameId); | |||||
| } | |||||
| static int _save_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 0, 1); | |||||
| return handlePtr->handleSave(); | |||||
| } | |||||
| static int _loaded_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 0, 1); | |||||
| return handlePtr->handleSessionIsLoaded(); | |||||
| } | |||||
| static int _show_gui_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 0, 1); | |||||
| return handlePtr->handleShowOptionalGui(); | |||||
| } | |||||
| static int _hide_gui_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 0, 1); | |||||
| return handlePtr->handleHideOptionalGui(); | |||||
| } | |||||
| static int _broadcast_handler(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* data) | |||||
| { | |||||
| return handlePtr->handleBroadcast(path, types, argv, argc, msg); | |||||
| } | |||||
| #undef handlePtr | |||||
| CARLA_PREVENT_HEAP_ALLOCATION | |||||
| CARLA_DECLARE_NON_COPY_CLASS(CarlaNSM) | |||||
| }; | |||||
| #endif // HAVE_LIBLO | |||||
| CarlaBackendStandalone gStandalone; | |||||
| // ------------------------------------------------------------------------------------------------------------------- | // ------------------------------------------------------------------------------------------------------------------- | ||||
| // API | // API | ||||
| @@ -2564,27 +1914,6 @@ const char* carla_get_host_osc_url_udp() | |||||
| // ------------------------------------------------------------------------------------------------------------------- | // ------------------------------------------------------------------------------------------------------------------- | ||||
| bool carla_nsm_init(int pid, const char* executableName) | |||||
| { | |||||
| #ifdef HAVE_LIBLO | |||||
| return CarlaNSM::getInstance().announce(pid, executableName); | |||||
| #else | |||||
| return false; | |||||
| // unused | |||||
| (void)pid; (void)executableName; | |||||
| #endif | |||||
| } | |||||
| void carla_nsm_ready(int action) | |||||
| { | |||||
| #ifdef HAVE_LIBLO | |||||
| CarlaNSM::getInstance().ready(action); | |||||
| #endif | |||||
| } | |||||
| // ------------------------------------------------------------------------------------------------------------------- | |||||
| #include "CarlaPluginUI.cpp" | #include "CarlaPluginUI.cpp" | ||||
| #include "CarlaDssiUtils.cpp" | #include "CarlaDssiUtils.cpp" | ||||
| #include "CarlaPatchbayUtils.cpp" | #include "CarlaPatchbayUtils.cpp" | ||||
| @@ -0,0 +1,712 @@ | |||||
| /* | |||||
| * Carla Standalone | |||||
| * Copyright (C) 2011-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * 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 doc/GPL.txt file. | |||||
| */ | |||||
| #include "CarlaDefines.h" | |||||
| #ifdef HAVE_LIBLO | |||||
| #define NSM_API_VERSION_MAJOR 1 | |||||
| #define NSM_API_VERSION_MINOR 2 | |||||
| #define NSM_CLIENT_FEATURES ":switch:" | |||||
| //#define NSM_CLIENT_FEATURES ":switch:optional-gui:" | |||||
| #include "CarlaHost.h" | |||||
| #include "CarlaOscUtils.hpp" | |||||
| #include "CarlaString.hpp" | |||||
| #include "juce_core.h" | |||||
| namespace CB = CarlaBackend; | |||||
| // ------------------------------------------------------------------------------------------------------------------- | |||||
| struct CarlaBackendStandalone { | |||||
| CarlaEngine* engine; | |||||
| EngineCallbackFunc engineCallback; | |||||
| void* engineCallbackPtr; | |||||
| // ... | |||||
| }; | |||||
| extern CarlaBackendStandalone gStandalone; | |||||
| // ------------------------------------------------------------------------------------------------------------------- | |||||
| class CarlaNSM | |||||
| { | |||||
| public: | |||||
| CarlaNSM() noexcept | |||||
| : fOscAddress(nullptr), | |||||
| fOscServer(nullptr), | |||||
| fOscServerThread(nullptr), | |||||
| fClientNameId(), | |||||
| fProjectPath(), | |||||
| fHasBroadcast(false), | |||||
| fHasOptionalGui(false), | |||||
| fHasServerControl(false), | |||||
| fStarted(), | |||||
| fReadyActionOpen(true), | |||||
| fReadyActionSave(true) {} | |||||
| ~CarlaNSM() | |||||
| { | |||||
| CARLA_SAFE_ASSERT(fReadyActionOpen); | |||||
| CARLA_SAFE_ASSERT(fReadyActionSave); | |||||
| if (fOscServerThread != nullptr) | |||||
| { | |||||
| lo_server_thread_stop(fOscServerThread); | |||||
| lo_server_thread_free(fOscServerThread); | |||||
| fOscServerThread = nullptr; | |||||
| fOscServer = nullptr; | |||||
| } | |||||
| if (fOscAddress != nullptr) | |||||
| { | |||||
| lo_address_free(fOscAddress); | |||||
| fOscAddress = nullptr; | |||||
| } | |||||
| } | |||||
| bool announce(const int pid, const char* const executableName) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(pid != 0, false); | |||||
| CARLA_SAFE_ASSERT_RETURN(executableName != nullptr && executableName[0] != '\0', false); | |||||
| const char* const NSM_URL(std::getenv("NSM_URL")); | |||||
| if (NSM_URL == nullptr) | |||||
| return false; | |||||
| const lo_address addr = lo_address_new_from_url(NSM_URL); | |||||
| CARLA_SAFE_ASSERT_RETURN(addr != nullptr, false); | |||||
| const int proto = lo_address_get_protocol(addr); | |||||
| if (fOscServerThread == nullptr) | |||||
| { | |||||
| // create new OSC server | |||||
| fOscServerThread = lo_server_thread_new_with_proto(nullptr, proto, _osc_error_handler); | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServerThread != nullptr, false); | |||||
| // register message handlers | |||||
| lo_server_thread_add_method(fOscServerThread, "/error", "sis", _error_handler, this); | |||||
| lo_server_thread_add_method(fOscServerThread, "/reply", "ssss", _reply_handler, this); | |||||
| lo_server_thread_add_method(fOscServerThread, "/nsm/client/open", "sss", _open_handler, this); | |||||
| lo_server_thread_add_method(fOscServerThread, "/nsm/client/save", "", _save_handler, this); | |||||
| lo_server_thread_add_method(fOscServerThread, "/nsm/client/session_is_loaded", "", _loaded_handler, this); | |||||
| lo_server_thread_add_method(fOscServerThread, "/nsm/client/show_optional_gui", "", _show_gui_handler, this); | |||||
| lo_server_thread_add_method(fOscServerThread, "/nsm/client/hide_optional_gui", "", _hide_gui_handler, this); | |||||
| lo_server_thread_add_method(fOscServerThread, nullptr, nullptr, _broadcast_handler, this); | |||||
| fOscServer = lo_server_thread_get_server(fOscServerThread); | |||||
| } | |||||
| lo_send_from(addr, fOscServer, LO_TT_IMMEDIATE, "/nsm/server/announce", "sssiii", | |||||
| "Carla", NSM_CLIENT_FEATURES, executableName, NSM_API_VERSION_MAJOR, NSM_API_VERSION_MINOR, pid); | |||||
| lo_address_free(addr); | |||||
| return true; | |||||
| } | |||||
| void ready(const int action) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServerThread != nullptr,); | |||||
| switch (action) | |||||
| { | |||||
| case -1: // init | |||||
| CARLA_SAFE_ASSERT_BREAK(! fStarted); | |||||
| fStarted = true; | |||||
| lo_server_thread_start(fOscServerThread); | |||||
| break; | |||||
| case 0: // error | |||||
| break; | |||||
| case 1: // reply | |||||
| break; | |||||
| case 2: // open | |||||
| fReadyActionOpen = true; | |||||
| break; | |||||
| case 3: // save | |||||
| fReadyActionSave = true; | |||||
| break; | |||||
| case 4: // session loaded | |||||
| break; | |||||
| case 5: // show gui | |||||
| CARLA_SAFE_ASSERT_BREAK(fOscAddress != nullptr); | |||||
| CARLA_SAFE_ASSERT_BREAK(fOscServer != nullptr); | |||||
| lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_shown", ""); | |||||
| break; | |||||
| case 6: // hide gui | |||||
| CARLA_SAFE_ASSERT_BREAK(fOscAddress != nullptr); | |||||
| CARLA_SAFE_ASSERT_BREAK(fOscServer != nullptr); | |||||
| lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_hidden", ""); | |||||
| break; | |||||
| } | |||||
| } | |||||
| static CarlaNSM& getInstance() | |||||
| { | |||||
| static CarlaNSM sInstance; | |||||
| return sInstance; | |||||
| } | |||||
| protected: | |||||
| int handleError(const char* const method, const int code, const char* const message) | |||||
| { | |||||
| carla_stdout("CarlaNSM::handleError(\"%s\", %i, \"%s\")", method, code, message); | |||||
| if (gStandalone.engineCallback != nullptr) | |||||
| gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 0, code, 0.0f, message); | |||||
| return 1; | |||||
| // may be unused | |||||
| (void)method; | |||||
| } | |||||
| int handleReply(const char* const method, const char* const message, const char* const smName, const char* const features, | |||||
| const lo_message msg) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServerThread != nullptr, 1); | |||||
| carla_stdout("CarlaNSM::handleReply(\"%s\", \"%s\", \"%s\", \"%s\")", method, message, smName, features); | |||||
| if (std::strcmp(method, "/nsm/server/announce") == 0) | |||||
| { | |||||
| char* const addressURL(lo_address_get_url(lo_message_get_source(msg))); | |||||
| CARLA_SAFE_ASSERT_RETURN(addressURL != nullptr, 0); | |||||
| if (fOscAddress != nullptr) | |||||
| lo_address_free(fOscAddress); | |||||
| fOscAddress = lo_address_new_from_url(addressURL); | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 0); | |||||
| fHasBroadcast = std::strstr(features, ":broadcast:") != nullptr; | |||||
| fHasOptionalGui = std::strstr(features, ":optional-gui:") != nullptr; | |||||
| fHasServerControl = std::strstr(features, ":server_control:") != nullptr; | |||||
| // UI starts visible | |||||
| if (fHasOptionalGui) | |||||
| lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_shown", ""); | |||||
| carla_stdout("Carla started via '%s', message: %s", smName, message); | |||||
| if (gStandalone.engineCallback != nullptr) | |||||
| { | |||||
| int flags = 0; | |||||
| if (fHasBroadcast) | |||||
| flags |= 1 << 0; | |||||
| if (fHasOptionalGui) | |||||
| flags |= 1 << 1; | |||||
| if (fHasServerControl) | |||||
| flags |= 1 << 2; | |||||
| gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 1, flags, 0.0f, smName); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| carla_stdout("Got unknown NSM reply method '%s'", method); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| int handleOpen(const char* const projectPath, const char* const displayName, const char* const clientNameId) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1); | |||||
| carla_stdout("CarlaNSM::handleOpen(\"%s\", \"%s\", \"%s\")", projectPath, displayName, clientNameId); | |||||
| if (gStandalone.engineCallback != nullptr) | |||||
| { | |||||
| fReadyActionOpen = false; | |||||
| gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 2, 0, 0.0f, projectPath); | |||||
| for (; ! fReadyActionOpen;) | |||||
| carla_msleep(10); | |||||
| } | |||||
| else | |||||
| { | |||||
| using namespace juce; | |||||
| if (carla_is_engine_running()) | |||||
| carla_engine_close(); | |||||
| carla_engine_init("JACK", clientNameId); | |||||
| fProjectPath = projectPath; | |||||
| fProjectPath += ".carxp"; | |||||
| const String jfilename = String(CharPointer_UTF8(fProjectPath)); | |||||
| if (File(jfilename).existsAsFile()) | |||||
| carla_load_project(fProjectPath); | |||||
| } | |||||
| fClientNameId = clientNameId; | |||||
| lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "ss", "/nsm/client/open", "OK"); | |||||
| // Broadcast ourselves | |||||
| if (fHasBroadcast) | |||||
| { | |||||
| char* const oscServerAddress(lo_server_get_url(fOscServer)); | |||||
| if (lo_message msg = lo_message_new()) | |||||
| { | |||||
| lo_message_add(msg, "sssss", | |||||
| "/non/hello", | |||||
| oscServerAddress, | |||||
| "Carla", | |||||
| CARLA_VERSION_STRING, | |||||
| fClientNameId.buffer()); | |||||
| lo_send_message_from(fOscAddress, fOscServer, "/nsm/server/broadcast", msg); | |||||
| lo_message_free(msg); | |||||
| } | |||||
| std::free(oscServerAddress); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| int handleSave() | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1); | |||||
| carla_stdout("CarlaNSM::handleSave()"); | |||||
| if (gStandalone.engineCallback != nullptr) | |||||
| { | |||||
| fReadyActionSave = false; | |||||
| gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 3, 0, 0.0f, nullptr); | |||||
| for (; ! fReadyActionSave;) | |||||
| carla_msleep(10); | |||||
| } | |||||
| else | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fProjectPath.isNotEmpty(), 0); | |||||
| carla_save_project(fProjectPath); | |||||
| } | |||||
| lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "ss", "/nsm/client/save", "OK"); | |||||
| return 0; | |||||
| } | |||||
| int handleSessionIsLoaded() | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1); | |||||
| carla_stdout("CarlaNSM::handleSessionIsLoaded()"); | |||||
| if (gStandalone.engineCallback != nullptr) | |||||
| gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 4, 0, 0.0f, nullptr); | |||||
| return 0; | |||||
| } | |||||
| int handleShowOptionalGui() | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1); | |||||
| carla_stdout("CarlaNSM::handleShowOptionalGui()"); | |||||
| if (gStandalone.engineCallback != nullptr) | |||||
| gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 5, 0, 0.0f, nullptr); | |||||
| return 0; | |||||
| } | |||||
| int handleHideOptionalGui() | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1); | |||||
| carla_stdout("CarlaNSM::handleHideOptionalGui()"); | |||||
| if (gStandalone.engineCallback != nullptr) | |||||
| gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 6, 0, 0.0f, nullptr); | |||||
| return 0; | |||||
| } | |||||
| int handleBroadcast(const char* const path, const char* const types, lo_arg** const argv, const int argc, | |||||
| const lo_message msg) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(argc >= 0, 0); | |||||
| carla_stdout("CarlaNSM::handleBroadcast(%s, %s, %p, %i)", path, types, argv, argc); | |||||
| if (std::strcmp(path, "/non/hello") == 0) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 4, 0); | |||||
| CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "ssss") == 0, 0); | |||||
| const char* const url = &argv[0]->s; | |||||
| //const char* const name = &argv[1]->s; | |||||
| //const char* const version = &argv[2]->s; | |||||
| //const char* const clientId = &argv[3]->s; | |||||
| const lo_address targetAddress(lo_address_new_from_url(url)); | |||||
| CARLA_SAFE_ASSERT_RETURN(targetAddress != nullptr, 0); | |||||
| char* const oscServerAddress(lo_server_get_url(fOscServer)); | |||||
| CARLA_SAFE_ASSERT_RETURN(oscServerAddress != nullptr, 0); | |||||
| if (lo_message msg2 = lo_message_new()) | |||||
| { | |||||
| lo_message_add(msg2, "ss", | |||||
| fClientNameId.buffer(), | |||||
| oscServerAddress); | |||||
| lo_send_message_from(targetAddress, fOscServer, "/signal/hello", msg2); | |||||
| lo_message_free(msg2); | |||||
| carla_stdout("CarlaNSM::handleBroadcast - sent hello"); | |||||
| } | |||||
| lo_address_free(targetAddress); | |||||
| std::free(oscServerAddress); | |||||
| return 0; | |||||
| } | |||||
| if (std::strcmp(path, "/signal/hello") == 0) | |||||
| { | |||||
| carla_stdout("CarlaNSM::handleBroadcast - got hello"); | |||||
| //const char* const name = &argv[0]->s; | |||||
| const char* const url = &argv[1]->s; | |||||
| const lo_address targetAddress(lo_address_new_from_url(url)); | |||||
| CARLA_SAFE_ASSERT_RETURN(targetAddress != nullptr, 0); | |||||
| char* const oscServerAddress(lo_server_get_url(fOscServer)); | |||||
| CARLA_SAFE_ASSERT_RETURN(oscServerAddress != nullptr, 0); | |||||
| //lo_send_from(targetAddress, fOscServer, LO_TT_IMMEDIATE, "/signal/list", ""); | |||||
| if (lo_message msg2 = lo_message_new()) | |||||
| { | |||||
| lo_message_add(msg2, "ss", | |||||
| fClientNameId.buffer(), | |||||
| oscServerAddress); | |||||
| lo_send_message_from(targetAddress, fOscServer, "/signal/hello", msg2); | |||||
| lo_message_free(msg2); | |||||
| carla_stdout("CarlaNSM::handleBroadcast - sent hello"); | |||||
| } | |||||
| lo_address_free(targetAddress); | |||||
| std::free(oscServerAddress); | |||||
| return 0; | |||||
| } | |||||
| if (std::strcmp(path, "/signal/list") == 0) | |||||
| { | |||||
| carla_stdout("CarlaNSM::handleBroadcast - got list"); | |||||
| CARLA_SAFE_ASSERT_RETURN(carla_is_engine_running(), 0); | |||||
| //const char* prefix = nullptr; | |||||
| //if (argc > 0) | |||||
| //prefix = &argv[0]->s; | |||||
| const lo_address targetAddress(lo_message_get_source(msg)); | |||||
| CARLA_SAFE_ASSERT_RETURN(targetAddress != nullptr, 0); | |||||
| CarlaString stripName; | |||||
| for (uint32_t i = 0, pluginCount = carla_get_current_plugin_count(); i < pluginCount; ++i) | |||||
| { | |||||
| const CarlaPluginInfo* const pluginInfo(carla_get_plugin_info(i)); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(pluginInfo != nullptr); | |||||
| for (uint32_t j=0, paramCount = carla_get_parameter_count(i); j < paramCount; ++j) | |||||
| { | |||||
| const CarlaParameterInfo* const paramInfo(carla_get_parameter_info(i, j)); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(paramInfo != nullptr); | |||||
| const ParameterData* const paramData(carla_get_parameter_data(i, j)); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(paramData != nullptr); | |||||
| const ParameterRanges* const paramRanges(carla_get_parameter_ranges(i, j)); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(paramRanges != nullptr); | |||||
| if (paramData->type != CB::PARAMETER_INPUT && paramData->type != CB::PARAMETER_OUTPUT) | |||||
| continue; | |||||
| if ((paramData->hints & CB::PARAMETER_IS_ENABLED) == 0) | |||||
| continue; | |||||
| if ((paramData->hints & CB::PARAMETER_IS_AUTOMABLE) == 0) | |||||
| continue; | |||||
| if (paramData->hints & CB::PARAMETER_IS_READ_ONLY) | |||||
| continue; | |||||
| if (lo_message msg2 = lo_message_new()) | |||||
| { | |||||
| stripName = fClientNameId + "/strip/Unnamed/" + pluginInfo->name + "/" + paramInfo->name; | |||||
| stripName.replace(' ','_'); | |||||
| lo_message_add(msg2, "sssfff", | |||||
| path, | |||||
| stripName.buffer(), | |||||
| paramData->type == CB::PARAMETER_INPUT ? "in" : "out", | |||||
| paramRanges->min, | |||||
| paramRanges->max, | |||||
| paramRanges->def); | |||||
| lo_send_message_from(targetAddress, fOscServer, "/reply", msg2); | |||||
| lo_message_free(msg2); | |||||
| carla_stdout("CarlaNSM::handleBroadcast - sent list at %i : %i, path: %s", i+1, j+1, stripName.buffer()); | |||||
| } | |||||
| #if 0 | |||||
| if (prefix == nullptr /*|| strncmp(o->path(), prefix, std::strlen(prefix)) == 0*/) | |||||
| { | |||||
| lo_send_from(targetAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "sssfff", | |||||
| path, | |||||
| paramInfo->name, | |||||
| paramData->type == CB::PARAMETER_INPUT ? "in" : "out", | |||||
| paramRanges->min, | |||||
| paramRanges->max, | |||||
| paramRanges->def); | |||||
| } | |||||
| #endif | |||||
| } | |||||
| } | |||||
| if (lo_message msg2 = lo_message_new()) | |||||
| { | |||||
| lo_message_add(msg2, "s", path); | |||||
| lo_send_message_from(targetAddress, fOscServer, "/reply", msg2); | |||||
| lo_message_free(msg2); | |||||
| carla_stdout("CarlaNSM::handleBroadcast - sent list final"); | |||||
| } | |||||
| //lo_send_from(targetAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "s", path); | |||||
| //return 0; | |||||
| } | |||||
| #if 0 | |||||
| if (std::strcmp(path, "/reply") == 0) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc > 0, 0); | |||||
| const char* const method = &argv[0]->s; | |||||
| if (std::strcmp(method, "/signal/list") == 0) | |||||
| { | |||||
| carla_stdout("CarlaNSM::handleBroadcast - got new list"); | |||||
| if (argc == 1) | |||||
| return 0; | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 6, 0); | |||||
| const lo_address targetAddress(lo_message_get_source(msg)); | |||||
| CARLA_SAFE_ASSERT_RETURN(targetAddress != nullptr, 0); | |||||
| const char* const stripName = &argv[1]->s; | |||||
| const char* const orientation = &argv[2]->s; | |||||
| const float min = argv[3]->f; | |||||
| const float max = argv[4]->f; | |||||
| const float def = argv[5]->f; | |||||
| if (lo_message msg2 = lo_message_new()) | |||||
| { | |||||
| lo_message_add(msg2, "sssfff", | |||||
| method, | |||||
| stripName, | |||||
| orientation, | |||||
| min, | |||||
| max, | |||||
| def); | |||||
| lo_send_message_from(targetAddress, fOscServer, "/reply", msg2); | |||||
| lo_message_free(msg2); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| CARLA_SAFE_ASSERT(false); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| #endif | |||||
| // check if all args are strings | |||||
| for (int i=0; i<argc; ++i) | |||||
| { | |||||
| if (types[i] != 's') | |||||
| return 0; | |||||
| } | |||||
| for (int i=0; i<argc; ++i) | |||||
| carla_stdout("%i: %s", i+1, &argv[i]->s); | |||||
| return 0; | |||||
| } | |||||
| private: | |||||
| lo_address fOscAddress; | |||||
| lo_server fOscServer; | |||||
| lo_server_thread fOscServerThread; | |||||
| CarlaString fClientNameId; | |||||
| CarlaString fProjectPath; | |||||
| bool fHasBroadcast; | |||||
| bool fHasOptionalGui; | |||||
| bool fHasServerControl; | |||||
| bool fStarted; | |||||
| volatile bool fReadyActionOpen; | |||||
| volatile bool fReadyActionSave; | |||||
| #define handlePtr ((CarlaNSM*)data) | |||||
| static void _osc_error_handler(int num, const char* msg, const char* path) | |||||
| { | |||||
| carla_stderr2("CarlaNSM::_osc_error_handler(%i, \"%s\", \"%s\")", num, msg, path); | |||||
| } | |||||
| static int _error_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message, void* data) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 3, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "sis") == 0, 1); | |||||
| const char* const method = &argv[0]->s; | |||||
| const int code = argv[1]->i; | |||||
| const char* const message = &argv[2]->s; | |||||
| return handlePtr->handleError(method, code, message); | |||||
| } | |||||
| static int _reply_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message msg, void* data) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 4, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "ssss") == 0, 1); | |||||
| const char* const method = &argv[0]->s; | |||||
| const char* const message = &argv[1]->s; | |||||
| const char* const smName = &argv[2]->s; | |||||
| const char* const features = &argv[3]->s; | |||||
| return handlePtr->handleReply(method, message, smName, features, msg); | |||||
| } | |||||
| static int _open_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message, void* data) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 3, 1); | |||||
| CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "sss") == 0, 1); | |||||
| const char* const projectPath = &argv[0]->s; | |||||
| const char* const displayName = &argv[1]->s; | |||||
| const char* const clientNameId = &argv[2]->s; | |||||
| return handlePtr->handleOpen(projectPath, displayName, clientNameId); | |||||
| } | |||||
| static int _save_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 0, 1); | |||||
| return handlePtr->handleSave(); | |||||
| } | |||||
| static int _loaded_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 0, 1); | |||||
| return handlePtr->handleSessionIsLoaded(); | |||||
| } | |||||
| static int _show_gui_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 0, 1); | |||||
| return handlePtr->handleShowOptionalGui(); | |||||
| } | |||||
| static int _hide_gui_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(argc == 0, 1); | |||||
| return handlePtr->handleHideOptionalGui(); | |||||
| } | |||||
| static int _broadcast_handler(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* data) | |||||
| { | |||||
| return handlePtr->handleBroadcast(path, types, argv, argc, msg); | |||||
| } | |||||
| #undef handlePtr | |||||
| CARLA_PREVENT_HEAP_ALLOCATION | |||||
| CARLA_DECLARE_NON_COPY_CLASS(CarlaNSM) | |||||
| }; | |||||
| #endif // HAVE_LIBLO | |||||
| // ------------------------------------------------------------------------------------------------------------------- | |||||
| CARLA_EXPORT | |||||
| bool carla_nsm_init(int pid, const char* executableName); | |||||
| bool carla_nsm_init(int pid, const char* executableName) | |||||
| { | |||||
| #ifdef HAVE_LIBLO | |||||
| return CarlaNSM::getInstance().announce(pid, executableName); | |||||
| #else | |||||
| return false; | |||||
| // unused | |||||
| (void)pid; (void)executableName; | |||||
| #endif | |||||
| } | |||||
| CARLA_EXPORT | |||||
| void carla_nsm_ready(int action); | |||||
| void carla_nsm_ready(int action) | |||||
| { | |||||
| #ifdef HAVE_LIBLO | |||||
| CarlaNSM::getInstance().ready(action); | |||||
| #endif | |||||
| } | |||||
| // ------------------------------------------------------------------------------------------------------------------- | |||||
| @@ -9,8 +9,11 @@ include Makefile.mk | |||||
| # ---------------------------------------------------------------------------------------------------------------------------- | # ---------------------------------------------------------------------------------------------------------------------------- | ||||
| OBJS = \ | |||||
| OBJS_standalone = \ | |||||
| $(OBJDIR)/CarlaStandalone.cpp.o \ | $(OBJDIR)/CarlaStandalone.cpp.o \ | ||||
| $(OBJDIR)/CarlaStandaloneNSM.cpp.o | |||||
| OBJS_utils = \ | |||||
| $(OBJDIR)/CarlaUtils.cpp.o | $(OBJDIR)/CarlaUtils.cpp.o | ||||
| TARGETS = \ | TARGETS = \ | ||||
| @@ -123,7 +126,7 @@ all: $(TARGETS) | |||||
| # ---------------------------------------------------------------------------------------------------------------------------- | # ---------------------------------------------------------------------------------------------------------------------------- | ||||
| clean: | clean: | ||||
| rm -f $(OBJS) $(TARGETS) | |||||
| rm -f $(OBJS_standalone) $(OBJS_utils) $(TARGETS) | |||||
| $(MAKE) clean -C engine | $(MAKE) clean -C engine | ||||
| $(MAKE) clean -C plugin | $(MAKE) clean -C plugin | ||||
| @@ -135,15 +138,15 @@ doxygen: CarlaBackend.doxygen | |||||
| # ---------------------------------------------------------------------------------------------------------------------------- | # ---------------------------------------------------------------------------------------------------------------------------- | ||||
| $(BINDIR)/libcarla_standalone2$(LIB_EXT): $(OBJDIR)/CarlaStandalone.cpp.o $(STANDALONE_LIBS) | |||||
| $(BINDIR)/libcarla_standalone2$(LIB_EXT): $(OBJS_standalone) $(STANDALONE_LIBS) | |||||
| -@mkdir -p $(BINDIR) | -@mkdir -p $(BINDIR) | ||||
| @echo "Linking libcarla_standalone2$(LIB_EXT)" | @echo "Linking libcarla_standalone2$(LIB_EXT)" | ||||
| @$(CXX) $< $(LIBS_START) $(STANDALONE_LIBS) $(LIBS_END) $(LINK_FLAGS) $(STANDALONE_LINK_FLAGS) $(SHARED) -o $@ | |||||
| @$(CXX) $(OBJS_standalone) $(LIBS_START) $(STANDALONE_LIBS) $(LIBS_END) $(LINK_FLAGS) $(STANDALONE_LINK_FLAGS) $(SHARED) -o $@ | |||||
| $(BINDIR)/libcarla_utils$(LIB_EXT): $(OBJDIR)/CarlaUtils.cpp.o $(UTILS_LIBS) | |||||
| $(BINDIR)/libcarla_utils$(LIB_EXT): $(OBJS_utils) $(UTILS_LIBS) | |||||
| -@mkdir -p $(BINDIR) | -@mkdir -p $(BINDIR) | ||||
| @echo "Linking libcarla_utils$(LIB_EXT)" | @echo "Linking libcarla_utils$(LIB_EXT)" | ||||
| @$(CXX) $< $(LIBS_START) $(UTILS_LIBS) $(LIBS_END) $(LINK_FLAGS) $(UTILS_LINK_FLAGS) $(SHARED) -o $@ | |||||
| @$(CXX) $(OBJS_utils) $(LIBS_START) $(UTILS_LIBS) $(LIBS_END) $(LINK_FLAGS) $(UTILS_LINK_FLAGS) $(SHARED) -o $@ | |||||
| # ---------------------------------------------------------------------------------------------------------------------------- | # ---------------------------------------------------------------------------------------------------------------------------- | ||||