/* * Carla Bridge Plugin * Copyright (C) 2012-2017 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 doc/GPL.txt file. */ #include "CarlaEngine.hpp" #include "CarlaHost.h" #include "CarlaBackendUtils.hpp" #include "CarlaMIDI.h" #ifdef CARLA_OS_UNIX # include #endif #ifdef CARLA_OS_LINUX # include #endif #ifdef HAVE_X11 # include #endif #include "jackbridge/JackBridge.hpp" #include "AppConfig.h" #include "juce_core/juce_core.h" #if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN) # include "juce_gui_basics/juce_gui_basics.h" using juce::JUCEApplication; using juce::JUCEApplicationBase; using juce::Timer; #else # include "juce_events/juce_events.h" #endif using CarlaBackend::CarlaEngine; using CarlaBackend::EngineCallbackOpcode; using CarlaBackend::EngineCallbackOpcode2Str; using juce::CharPointer_UTF8; using juce::File; using juce::ScopedJuceInitialiser_GUI; using juce::String; // ------------------------------------------------------------------------- static bool gIsInitiated = false; static volatile bool gCloseNow = false; static volatile bool gSaveNow = false; #ifdef CARLA_OS_WIN static BOOL WINAPI winSignalHandler(DWORD dwCtrlType) noexcept { if (dwCtrlType == CTRL_C_EVENT) { gCloseNow = true; return TRUE; } return FALSE; } #elif defined(CARLA_OS_LINUX) static void closeSignalHandler(int) noexcept { gCloseNow = true; } static void saveSignalHandler(int) noexcept { gSaveNow = true; } #endif static void initSignalHandler() { #ifdef CARLA_OS_WIN SetConsoleCtrlHandler(winSignalHandler, TRUE); #elif defined(CARLA_OS_LINUX) struct sigaction sig; carla_zeroStruct(sig); sig.sa_handler = closeSignalHandler; sig.sa_flags = SA_RESTART; sigemptyset(&sig.sa_mask); sigaction(SIGTERM, &sig, nullptr); sigaction(SIGINT, &sig, nullptr); sig.sa_handler = saveSignalHandler; sig.sa_flags = SA_RESTART; sigemptyset(&sig.sa_mask); sigaction(SIGUSR1, &sig, nullptr); #endif } // ------------------------------------------------------------------------- static String gProjectFilename; static void gIdle() { carla_engine_idle(); if (gSaveNow) { gSaveNow = false; if (gProjectFilename.isNotEmpty()) { if (! carla_save_plugin_state(0, gProjectFilename.toRawUTF8())) carla_stderr("Plugin preset save failed, error was:\n%s", carla_get_last_error()); } } } // ------------------------------------------------------------------------- #if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN) class CarlaJuceApp : public JUCEApplication, private Timer { public: CarlaJuceApp() {} ~CarlaJuceApp() {} void initialise(const String&) override { startTimer(8); } void shutdown() override { gCloseNow = true; stopTimer(); } const String getApplicationName() override { return "CarlaPlugin"; } const String getApplicationVersion() override { return CARLA_VERSION_STRING; } void timerCallback() override { gIdle(); if (gCloseNow) { quit(); gCloseNow = false; } } }; static JUCEApplicationBase* juce_CreateApplication() { return new CarlaJuceApp(); } #endif // ------------------------------------------------------------------------- class CarlaBridgePlugin { public: CarlaBridgePlugin(const bool useBridge, const char* const clientName, const char* const audioPoolBaseName, const char* const rtClientBaseName, const char* const nonRtClientBaseName, const char* const nonRtServerBaseName) : fEngine(nullptr), fUsingBridge(false) { CARLA_ASSERT(clientName != nullptr && clientName[0] != '\0'); carla_debug("CarlaBridgePlugin::CarlaBridgePlugin(%s, \"%s\", %s, %s, %s, %s)", bool2str(useBridge), clientName, audioPoolBaseName, rtClientBaseName, nonRtClientBaseName, nonRtServerBaseName); carla_set_engine_callback(callback, this); if (useBridge) carla_engine_init_bridge(audioPoolBaseName, rtClientBaseName, nonRtClientBaseName, nonRtServerBaseName, clientName); else carla_engine_init("JACK", clientName); fEngine = carla_get_engine(); } ~CarlaBridgePlugin() { carla_debug("CarlaBridgePlugin::~CarlaBridgePlugin()"); carla_engine_close(); } bool isOk() const noexcept { return (fEngine != nullptr); } // --------------------------------------------------------------------- void exec(const bool useBridge, int argc, char* argv[]) { fUsingBridge = useBridge; if (! useBridge) { const CarlaPluginInfo* const pInfo(carla_get_plugin_info(0)); CARLA_SAFE_ASSERT_RETURN(pInfo != nullptr,); gProjectFilename = CharPointer_UTF8(pInfo->name); gProjectFilename += ".carxs"; if (! File::isAbsolutePath(gProjectFilename)) gProjectFilename = File::getCurrentWorkingDirectory().getChildFile(gProjectFilename).getFullPathName(); if (File(gProjectFilename).existsAsFile() && ! carla_load_plugin_state(0, gProjectFilename.toRawUTF8())) carla_stderr("Plugin preset load failed, error was:\n%s", carla_get_last_error()); } gIsInitiated = true; #if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN) JUCEApplicationBase::createInstance = &juce_CreateApplication; JUCEApplicationBase::main(JUCE_MAIN_FUNCTION_ARGS); #else for (; ! gCloseNow;) { gIdle(); carla_msleep(8); } #endif carla_set_engine_about_to_close(); carla_remove_plugin(0); // may be unused return; (void)argc; (void)argv; } // --------------------------------------------------------------------- protected: void handleCallback(const EngineCallbackOpcode action, const int value1, const int, const float, const char* const) { CARLA_BACKEND_USE_NAMESPACE; switch (action) { case ENGINE_CALLBACK_ENGINE_STOPPED: case ENGINE_CALLBACK_PLUGIN_REMOVED: case ENGINE_CALLBACK_QUIT: gCloseNow = true; break; case ENGINE_CALLBACK_UI_STATE_CHANGED: if (gIsInitiated && value1 != 1 && ! fUsingBridge) gCloseNow = true; break; default: break; } } private: const CarlaEngine* fEngine; bool fUsingBridge; const ScopedJuceInitialiser_GUI kJuceInitialiser; static void callback(void* ptr, EngineCallbackOpcode action, unsigned int pluginId, int value1, int value2, float value3, const char* valueStr) { carla_debug("CarlaBridgePlugin::callback(%p, %i:%s, %i, %i, %i, %f, \"%s\")", ptr, action, EngineCallbackOpcode2Str(action), pluginId, value1, value2, value3, valueStr); CARLA_SAFE_ASSERT_RETURN(ptr != nullptr,); CARLA_SAFE_ASSERT_RETURN(pluginId == 0,); return ((CarlaBridgePlugin*)ptr)->handleCallback(action, value1, value2, value3, valueStr); } CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaBridgePlugin) }; // ------------------------------------------------------------------------- int main(int argc, char* argv[]) { // --------------------------------------------------------------------- // Check argument count if (argc != 4 && argc != 5) { carla_stdout("usage: %s