| @@ -159,12 +159,12 @@ | |||||
| </item> | </item> | ||||
| <item row="3" column="1" colspan="3"> | <item row="3" column="1" colspan="3"> | ||||
| <widget class="QCheckBox" name="cb_manage_window"> | <widget class="QCheckBox" name="cb_manage_window"> | ||||
| <property name="enabled"> | |||||
| <bool>false</bool> | |||||
| </property> | |||||
| <property name="text"> | <property name="text"> | ||||
| <string>Take control of main applicaton window</string> | <string>Take control of main applicaton window</string> | ||||
| </property> | </property> | ||||
| <property name="checked"> | |||||
| <bool>true</bool> | |||||
| </property> | |||||
| </widget> | </widget> | ||||
| </item> | </item> | ||||
| <item row="2" column="1" colspan="2"> | <item row="2" column="1" colspan="2"> | ||||
| @@ -130,7 +130,11 @@ protected: | |||||
| const ScopedEnvVar sev2("LD_LIBRARY_PATH", libjackdir.buffer()); | const ScopedEnvVar sev2("LD_LIBRARY_PATH", libjackdir.buffer()); | ||||
| const ScopedEnvVar sev1("LD_PRELOAD", ldpreload.isNotEmpty() ? ldpreload.buffer() : nullptr); | const ScopedEnvVar sev1("LD_PRELOAD", ldpreload.isNotEmpty() ? ldpreload.buffer() : nullptr); | ||||
| carla_setenv("CARLA_FRONTEND_WIN_ID", strBuf); | |||||
| if (kPlugin->getHints() & PLUGIN_HAS_CUSTOM_UI) | |||||
| carla_setenv("CARLA_FRONTEND_WIN_ID", strBuf); | |||||
| else | |||||
| carla_unsetenv("CARLA_FRONTEND_WIN_ID"); | |||||
| carla_setenv("CARLA_LIBJACK_SETUP", fNumPorts.buffer()); | carla_setenv("CARLA_LIBJACK_SETUP", fNumPorts.buffer()); | ||||
| carla_setenv("CARLA_SHM_IDS", fShmIds.buffer()); | carla_setenv("CARLA_SHM_IDS", fShmIds.buffer()); | ||||
| @@ -154,12 +158,12 @@ protected: | |||||
| if (fProcess->isRunning()) | if (fProcess->isRunning()) | ||||
| { | { | ||||
| carla_stdout("CarlaPluginJackThread::run() - bridge refused to close, force kill now"); | |||||
| carla_stdout("CarlaPluginJackThread::run() - application refused to close, force kill now"); | |||||
| fProcess->kill(); | fProcess->kill(); | ||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| carla_stdout("CarlaPluginJackThread::run() - bridge auto-closed successfully"); | |||||
| carla_stdout("CarlaPluginJackThread::run() - application auto-closed successfully"); | |||||
| } | } | ||||
| } | } | ||||
| else | else | ||||
| @@ -167,7 +171,7 @@ protected: | |||||
| // forced quit, may have crashed | // forced quit, may have crashed | ||||
| if (fProcess->getExitCode() != 0 /*|| fProcess->exitStatus() == QProcess::CrashExit*/) | if (fProcess->getExitCode() != 0 /*|| fProcess->exitStatus() == QProcess::CrashExit*/) | ||||
| { | { | ||||
| carla_stderr("CarlaPluginJackThread::run() - bridge crashed"); | |||||
| carla_stderr("CarlaPluginJackThread::run() - application crashed"); | |||||
| CarlaString errorString("Plugin '" + CarlaString(kPlugin->getName()) + "' has crashed!\n" | CarlaString errorString("Plugin '" + CarlaString(kPlugin->getName()) + "' has crashed!\n" | ||||
| "Saving now will lose its current settings.\n" | "Saving now will lose its current settings.\n" | ||||
| @@ -176,7 +180,7 @@ protected: | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| carla_stderr("CarlaPluginJackThread::run() - bridge closed itself"); | |||||
| carla_stderr("CarlaPluginJackThread::run() - application closed itself"); | |||||
| } | } | ||||
| } | } | ||||
| @@ -364,6 +368,18 @@ public: | |||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| // Set ui stuff | // Set ui stuff | ||||
| void showCustomUI(const bool yesNo) override | |||||
| { | |||||
| if (yesNo && ! fBridgeThread.isThreadRunning()) { | |||||
| CARLA_SAFE_ASSERT_RETURN(restartBridgeThread(),); | |||||
| } | |||||
| const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex); | |||||
| fShmNonRtClientControl.writeOpcode(yesNo ? kPluginBridgeNonRtClientShowUI : kPluginBridgeNonRtClientHideUI); | |||||
| fShmNonRtClientControl.commitWrite(); | |||||
| } | |||||
| void idle() override | void idle() override | ||||
| { | { | ||||
| if (fBridgeThread.isThreadRunning()) | if (fBridgeThread.isThreadRunning()) | ||||
| @@ -395,9 +411,12 @@ public: | |||||
| fTimedOut = true; | fTimedOut = true; | ||||
| fTimedError = true; | fTimedError = true; | ||||
| fInitiated = false; | fInitiated = false; | ||||
| carla_stderr2("Plugin bridge has been stopped or crashed"); | |||||
| pData->engine->callback(ENGINE_CALLBACK_PLUGIN_UNAVAILABLE, pData->id, 0, 0, 0.0f, | |||||
| "Plugin bridge has been stopped or crashed"); | |||||
| handleProcessStopped(); | |||||
| } | |||||
| else if (fProcCanceled) | |||||
| { | |||||
| handleProcessStopped(); | |||||
| fProcCanceled = false; | |||||
| } | } | ||||
| CarlaPlugin::idle(); | CarlaPlugin::idle(); | ||||
| @@ -941,7 +960,6 @@ public: | |||||
| if (fShmRtClientControl.data->procFlags) | if (fShmRtClientControl.data->procFlags) | ||||
| { | { | ||||
| carla_stdout("PROC Flags active, disabling plugin"); | |||||
| fInitiated = false; | fInitiated = false; | ||||
| fProcCanceled = true; | fProcCanceled = true; | ||||
| } | } | ||||
| @@ -1126,17 +1144,11 @@ public: | |||||
| break; | break; | ||||
| case kPluginBridgeNonRtServerUiClosed: | case kPluginBridgeNonRtServerUiClosed: | ||||
| carla_stdout("bridge closed cleanly?"); | |||||
| pData->active = false; | |||||
| #ifdef HAVE_LIBLO | |||||
| if (pData->engine->isOscControlRegistered()) | |||||
| pData->engine->oscSend_control_set_parameter_value(pData->id, PARAMETER_ACTIVE, 0.0f); | |||||
| #endif | |||||
| pData->engine->callback(ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED, pData->id, PARAMETER_ACTIVE, 0, 0.0f, nullptr); | |||||
| fBridgeThread.stopThread(1000); | |||||
| carla_stdout("got kPluginBridgeNonRtServerUiClosed, bridge closed cleanly?"); | |||||
| pData->engine->callback(ENGINE_CALLBACK_UI_STATE_CHANGED, pData->id, 0, 0, 0.0f, nullptr); | |||||
| //fBridgeThread.signalThreadShouldExit(); | |||||
| //handleProcessStopped(); | |||||
| //fBridgeThread.stopThread(5000); | |||||
| break; | break; | ||||
| case kPluginBridgeNonRtServerError: { | case kPluginBridgeNonRtServerError: { | ||||
| @@ -1220,6 +1232,8 @@ public: | |||||
| fInfo.setupLabel = label; | fInfo.setupLabel = label; | ||||
| const int setupHints = label[4] - '0'; | |||||
| // --------------------------------------------------------------- | // --------------------------------------------------------------- | ||||
| // set info | // set info | ||||
| @@ -1265,6 +1279,17 @@ public: | |||||
| return false; | return false; | ||||
| } | } | ||||
| // --------------------------------------------------------------- | |||||
| // setup hints and options | |||||
| // FIXME dryWet broken | |||||
| pData->hints = PLUGIN_IS_BRIDGE | /*PLUGIN_CAN_DRYWET |*/ PLUGIN_CAN_VOLUME | PLUGIN_CAN_BALANCE | PLUGIN_NEEDS_FIXED_BUFFERS; | |||||
| pData->options = PLUGIN_OPTION_FIXED_BUFFERS; | |||||
| //fInfo.optionsAvailable = optionAv; | |||||
| if (setupHints & 0x10) | |||||
| pData->hints |= PLUGIN_HAS_CUSTOM_UI; | |||||
| // --------------------------------------------------------------- | // --------------------------------------------------------------- | ||||
| // init bridge thread | // init bridge thread | ||||
| @@ -1283,14 +1308,6 @@ public: | |||||
| if (! restartBridgeThread()) | if (! restartBridgeThread()) | ||||
| return false; | return false; | ||||
| // --------------------------------------------------------------- | |||||
| // setup hints and options | |||||
| // FIXME dryWet broken | |||||
| pData->hints = PLUGIN_IS_BRIDGE | /*PLUGIN_CAN_DRYWET |*/ PLUGIN_CAN_VOLUME | PLUGIN_CAN_BALANCE | PLUGIN_NEEDS_FIXED_BUFFERS; | |||||
| pData->options = PLUGIN_OPTION_FIXED_BUFFERS; | |||||
| //fInfo.optionsAvailable = optionAv; | |||||
| // --------------------------------------------------------------- | // --------------------------------------------------------------- | ||||
| // register client | // register client | ||||
| @@ -1344,6 +1361,24 @@ private: | |||||
| CARLA_DECLARE_NON_COPY_STRUCT(Info) | CARLA_DECLARE_NON_COPY_STRUCT(Info) | ||||
| } fInfo; | } fInfo; | ||||
| void handleProcessStopped() noexcept | |||||
| { | |||||
| const bool wasActive = pData->active; | |||||
| pData->active = false; | |||||
| if (wasActive) | |||||
| { | |||||
| #ifdef HAVE_LIBLO | |||||
| if (pData->engine->isOscControlRegistered()) | |||||
| pData->engine->oscSend_control_set_parameter_value(pData->id, PARAMETER_ACTIVE, 0.0f); | |||||
| #endif | |||||
| pData->engine->callback(ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED, pData->id, PARAMETER_ACTIVE, 0, 0.0f, nullptr); | |||||
| } | |||||
| if (pData->hints & PLUGIN_HAS_CUSTOM_UI) | |||||
| pData->engine->callback(ENGINE_CALLBACK_UI_STATE_CHANGED, pData->id, 0, 0, 0.0f, nullptr); | |||||
| } | |||||
| void resizeAudioPool(const uint32_t bufferSize) | void resizeAudioPool(const uint32_t bufferSize) | ||||
| { | { | ||||
| fShmAudioPool.resize(bufferSize, fInfo.aIns+fInfo.aOuts, 0); | fShmAudioPool.resize(bufferSize, fInfo.aIns+fInfo.aOuts, 0); | ||||
| @@ -22,9 +22,22 @@ | |||||
| struct ScopedLibOpen { | struct ScopedLibOpen { | ||||
| void* handle; | void* handle; | ||||
| Window winId; | |||||
| ScopedLibOpen() | ScopedLibOpen() | ||||
| : handle(dlopen("libjack.so.0", RTLD_NOW|RTLD_LOCAL)) {} | |||||
| : handle(dlopen("libjack.so.0", RTLD_NOW|RTLD_LOCAL)), | |||||
| winId(0) | |||||
| { | |||||
| if (const char* const winIdStr = std::getenv("CARLA_FRONTEND_WIN_ID")) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(winIdStr[0] != '\0',); | |||||
| const long long winIdLL(std::strtoll(winIdStr, nullptr, 16)); | |||||
| CARLA_SAFE_ASSERT_RETURN(winIdLL > 0,); | |||||
| winId = static_cast<Window>(winIdLL); | |||||
| } | |||||
| } | |||||
| ~ScopedLibOpen() | ~ScopedLibOpen() | ||||
| { | { | ||||
| @@ -33,18 +46,23 @@ struct ScopedLibOpen { | |||||
| } | } | ||||
| }; | }; | ||||
| // ----------------------------------------------------------------------- | |||||
| // --------------------------------------------------------------------------------------------------------------------- | |||||
| // Function typedefs | // Function typedefs | ||||
| typedef int (*XMapWindowFunc)(Display*, Window); | typedef int (*XMapWindowFunc)(Display*, Window); | ||||
| typedef int (*XUnmapWindowFunc)(Display*, Window); | typedef int (*XUnmapWindowFunc)(Display*, Window); | ||||
| typedef int (*CarlaInterposedCallback)(int, void*); | |||||
| // ----------------------------------------------------------------------- | |||||
| // Current mapped window | |||||
| // --------------------------------------------------------------------------------------------------------------------- | |||||
| // Current state | |||||
| static Window sCurrentlyMappedWindow = 0; | |||||
| static Display* gCurrentlyMappedDisplay = nullptr; | |||||
| static Window gCurrentlyMappedWindow = 0; | |||||
| static CarlaInterposedCallback gInterposedCallback = nullptr; | |||||
| static bool gCurrentWindowMapped = false; | |||||
| static bool gCurrentWindowVisible = false; | |||||
| // ----------------------------------------------------------------------- | |||||
| // --------------------------------------------------------------------------------------------------------------------- | |||||
| // Calling the real functions | // Calling the real functions | ||||
| static int real_XMapWindow(Display* display, Window window) | static int real_XMapWindow(Display* display, Window window) | ||||
| @@ -63,7 +81,7 @@ static int real_XUnmapWindow(Display* display, Window window) | |||||
| return func(display, window); | return func(display, window); | ||||
| } | } | ||||
| // ----------------------------------------------------------------------- | |||||
| // --------------------------------------------------------------------------------------------------------------------- | |||||
| // Our custom functions | // Our custom functions | ||||
| CARLA_EXPORT | CARLA_EXPORT | ||||
| @@ -73,7 +91,7 @@ int XMapWindow(Display* display, Window window) | |||||
| for (;;) | for (;;) | ||||
| { | { | ||||
| if (sCurrentlyMappedWindow != 0) | |||||
| if (slo.winId == 0) | |||||
| break; | break; | ||||
| Atom atom; | Atom atom; | ||||
| @@ -84,40 +102,96 @@ int XMapWindow(Display* display, Window window) | |||||
| const Atom wmWindowType = XInternAtom(display, "_NET_WM_WINDOW_TYPE", True); | const Atom wmWindowType = XInternAtom(display, "_NET_WM_WINDOW_TYPE", True); | ||||
| if (XGetWindowProperty(display, window, wmWindowType, 0, ~0L, False, AnyPropertyType, | if (XGetWindowProperty(display, window, wmWindowType, 0, ~0L, False, AnyPropertyType, | ||||
| &atom, &atomFormat, &numItems, &ignored, &atomPtrs) == Success) | |||||
| &atom, &atomFormat, &numItems, &ignored, &atomPtrs) != Success) | |||||
| break; | |||||
| const Atom* const atomValues = (const Atom*)atomPtrs; | |||||
| bool isMainWindow = (numItems == 0); | |||||
| for (ulong i=0; i<numItems; ++i) | |||||
| { | { | ||||
| const Atom* const atomValues = (const Atom*)atomPtrs; | |||||
| const char* const atomValue(XGetAtomName(display, atomValues[i])); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(atomValue != nullptr && atomValue[0] != '\0'); | |||||
| if (std::strcmp(atomValue, "_NET_WM_WINDOW_TYPE_COMBO" ) == 0 || | |||||
| std::strcmp(atomValue, "_NET_WM_WINDOW_TYPE_DIALOG" ) == 0 || | |||||
| std::strcmp(atomValue, "_NET_WM_WINDOW_TYPE_DND" ) == 0 || | |||||
| std::strcmp(atomValue, "_NET_WM_WINDOW_TYPE_DOCK" ) == 0 || | |||||
| std::strcmp(atomValue, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU") == 0 || | |||||
| std::strcmp(atomValue, "_NET_WM_WINDOW_TYPE_MENU" ) == 0 || | |||||
| std::strcmp(atomValue, "_NET_WM_WINDOW_TYPE_NOTIFICATION" ) == 0 || | |||||
| std::strcmp(atomValue, "_NET_WM_WINDOW_TYPE_POPUP_MENU" ) == 0 || | |||||
| std::strcmp(atomValue, "_NET_WM_WINDOW_TYPE_SPLASH" ) == 0 || | |||||
| std::strcmp(atomValue, "_NET_WM_WINDOW_TYPE_TOOLBAR" ) == 0 || | |||||
| std::strcmp(atomValue, "_NET_WM_WINDOW_TYPE_TOOLTIP" ) == 0 || | |||||
| std::strcmp(atomValue, "_NET_WM_WINDOW_TYPE_UTILITY" ) == 0) | |||||
| { | |||||
| isMainWindow = false; | |||||
| break; | |||||
| } | |||||
| for (ulong i=0; i<numItems; ++i) | |||||
| if (std::strcmp(atomValue, "_NET_WM_WINDOW_TYPE_NORMAL") == 0) | |||||
| { | |||||
| // window is good, use it if no other types are set | |||||
| isMainWindow = true; | |||||
| } | |||||
| else | |||||
| { | { | ||||
| const char* const atomValue(XGetAtomName(display, atomValues[i])); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(atomValue != nullptr && atomValue[0] != '\0'); | |||||
| if (std::strcmp(atomValue, "_NET_WM_WINDOW_TYPE_NORMAL") == 0) | |||||
| { | |||||
| sCurrentlyMappedWindow = window; | |||||
| break; | |||||
| } | |||||
| carla_stdout("=======================================> %s", atomValue); | |||||
| } | } | ||||
| } | } | ||||
| if (sCurrentlyMappedWindow == 0) | |||||
| if (! isMainWindow) | |||||
| { | |||||
| // this has always bothered me... | |||||
| if (gCurrentlyMappedWindow != 0 && gCurrentWindowMapped && gCurrentWindowVisible) | |||||
| XSetTransientForHint(display, window, gCurrentlyMappedWindow); | |||||
| break; | break; | ||||
| } | |||||
| if (const char* const winIdStr = std::getenv("CARLA_FRONTEND_WIN_ID")) | |||||
| Window transientWindow = 0; | |||||
| if (XGetTransientForHint(display, window, &transientWindow) == Success && transientWindow != 0) | |||||
| { | { | ||||
| CARLA_SAFE_ASSERT_BREAK(winIdStr[0] != '\0'); | |||||
| carla_stdout("Window has transient set already, ignoring it"); | |||||
| break; | |||||
| } | |||||
| const long long winIdLL(std::strtoll(winIdStr, nullptr, 16)); | |||||
| CARLA_SAFE_ASSERT_BREAK(winIdLL > 0); | |||||
| // got a new window, we may need to forget last one | |||||
| if (gCurrentlyMappedDisplay != nullptr && gCurrentlyMappedWindow != 0) | |||||
| { | |||||
| // igonre requests against the current mapped window | |||||
| if (gCurrentlyMappedWindow == window) | |||||
| return 0; | |||||
| // we already have a mapped window, with carla visible button on, should be a dialog of sorts.. | |||||
| if (gCurrentWindowMapped && gCurrentWindowVisible) | |||||
| { | |||||
| XSetTransientForHint(display, window, gCurrentlyMappedWindow); | |||||
| break; | |||||
| } | |||||
| // ignore empty windows created after the main one | |||||
| if (numItems == 0) | |||||
| break; | |||||
| carla_stdout("NOTICE: XMapWindow now showing previous window"); | |||||
| real_XMapWindow(gCurrentlyMappedDisplay, gCurrentlyMappedWindow); | |||||
| } | |||||
| const Window winId(static_cast<Window>(winIdLL)); | |||||
| XSetTransientForHint(display, window, static_cast<Window>(winId)); | |||||
| gCurrentlyMappedDisplay = display; | |||||
| gCurrentlyMappedWindow = window; | |||||
| gCurrentWindowMapped = true; | |||||
| carla_stdout("Transient hint correctly applied before mapping window"); | |||||
| XSetTransientForHint(display, window, slo.winId); | |||||
| if (gCurrentWindowVisible) | |||||
| { | |||||
| carla_stdout("JACK application window found, showing it now"); | |||||
| break; | |||||
| } | } | ||||
| break; | |||||
| gCurrentWindowMapped = false; | |||||
| carla_stdout("JACK application window found and captured"); | |||||
| return 0; | |||||
| } | } | ||||
| return real_XMapWindow(display, window); | return real_XMapWindow(display, window); | ||||
| @@ -126,10 +200,56 @@ int XMapWindow(Display* display, Window window) | |||||
| CARLA_EXPORT | CARLA_EXPORT | ||||
| int XUnmapWindow(Display* display, Window window) | int XUnmapWindow(Display* display, Window window) | ||||
| { | { | ||||
| if (sCurrentlyMappedWindow == window) | |||||
| sCurrentlyMappedWindow = 0; | |||||
| if (gCurrentlyMappedWindow == window) | |||||
| { | |||||
| gCurrentlyMappedDisplay = nullptr; | |||||
| gCurrentlyMappedWindow = 0; | |||||
| gCurrentWindowMapped = false; | |||||
| gCurrentWindowVisible = false; | |||||
| if (gInterposedCallback != nullptr) | |||||
| gInterposedCallback(1, nullptr); | |||||
| } | |||||
| return real_XUnmapWindow(display, window); | return real_XUnmapWindow(display, window); | ||||
| } | } | ||||
| // ----------------------------------------------------------------------- | |||||
| // --------------------------------------------------------------------------------------------------------------------- | |||||
| CARLA_EXPORT | |||||
| int jack_carla_interposed_action(int action, void* ptr) | |||||
| { | |||||
| carla_stdout("jack_carla_interposed_action(%i, %p)", action, ptr); | |||||
| switch (action) | |||||
| { | |||||
| case 1: // set callback | |||||
| gInterposedCallback = (CarlaInterposedCallback)ptr; | |||||
| break; | |||||
| case 2: // show gui | |||||
| gCurrentWindowVisible = true; | |||||
| if (gCurrentlyMappedDisplay == nullptr || gCurrentlyMappedWindow == 0) | |||||
| break; | |||||
| gCurrentWindowMapped = true; | |||||
| return real_XMapWindow(gCurrentlyMappedDisplay, gCurrentlyMappedWindow); | |||||
| case 3: // hide gui | |||||
| gCurrentWindowVisible = false; | |||||
| if (gCurrentlyMappedDisplay == nullptr || gCurrentlyMappedWindow == 0) | |||||
| break; | |||||
| gCurrentWindowMapped = false; | |||||
| return real_XUnmapWindow(gCurrentlyMappedDisplay, gCurrentlyMappedWindow); | |||||
| case 4: // close everything | |||||
| gCurrentWindowMapped = false; | |||||
| gCurrentWindowVisible = false; | |||||
| gCurrentlyMappedDisplay = nullptr; | |||||
| gCurrentlyMappedWindow = 0; | |||||
| return 0; | |||||
| } | |||||
| return -1; | |||||
| } | |||||
| // --------------------------------------------------------------------------------------------------------------------- | |||||
| @@ -24,6 +24,15 @@ using juce::FloatVectorOperations; | |||||
| using juce::Thread; | using juce::Thread; | ||||
| using juce::Time; | using juce::Time; | ||||
| typedef int (*CarlaInterposedCallback)(int, void*); | |||||
| CARLA_EXPORT | |||||
| int jack_carla_interposed_action(int, void*) | |||||
| { | |||||
| carla_stderr2("Non-export jack_carla_interposed_action called, this should not happen!!"); | |||||
| return 0; | |||||
| } | |||||
| CARLA_BACKEND_START_NAMESPACE | CARLA_BACKEND_START_NAMESPACE | ||||
| // --------------------------------------------------------------------------------------------------------------------- | // --------------------------------------------------------------------------------------------------------------------- | ||||
| @@ -76,6 +85,8 @@ private: | |||||
| Callback* const fCallback; | Callback* const fCallback; | ||||
| }; | }; | ||||
| static int carla_interposed_callback(int, void*); | |||||
| // --------------------------------------------------------------------------------------------------------------------- | // --------------------------------------------------------------------------------------------------------------------- | ||||
| class CarlaJackAppClient : public CarlaJackRealtimeThread::Callback, | class CarlaJackAppClient : public CarlaJackRealtimeThread::Callback, | ||||
| @@ -90,7 +101,7 @@ public: | |||||
| fAudioPoolCopy(nullptr), | fAudioPoolCopy(nullptr), | ||||
| fAudioTmpBuf(nullptr), | fAudioTmpBuf(nullptr), | ||||
| fDummyMidiInBuffer(true, "ignored"), | fDummyMidiInBuffer(true, "ignored"), | ||||
| fDummyMidiOutBuffer(true, "ignored"), | |||||
| fDummyMidiOutBuffer(false, "ignored"), | |||||
| fMidiInBuffers(nullptr), | fMidiInBuffers(nullptr), | ||||
| fMidiOutBuffers(nullptr), | fMidiOutBuffers(nullptr), | ||||
| fIsOffline(false), | fIsOffline(false), | ||||
| @@ -127,10 +138,12 @@ public: | |||||
| fBaseNameNonRtClientControl[6] = '\0'; | fBaseNameNonRtClientControl[6] = '\0'; | ||||
| fBaseNameNonRtServerControl[6] = '\0'; | fBaseNameNonRtServerControl[6] = '\0'; | ||||
| fNumPorts.audioIns = libjackSetup[0] - '0'; | |||||
| fNumPorts.audioOuts = libjackSetup[1] - '0'; | |||||
| fNumPorts.midiIns = libjackSetup[2] - '0'; | |||||
| fNumPorts.midiOuts = libjackSetup[3] - '0'; | |||||
| fServer.numAudioIns = libjackSetup[0] - '0'; | |||||
| fServer.numAudioOuts = libjackSetup[1] - '0'; | |||||
| fServer.numMidiIns = libjackSetup[2] - '0'; | |||||
| fServer.numMidiOuts = libjackSetup[3] - '0'; | |||||
| jack_carla_interposed_action(1, (void*)carla_interposed_callback); | |||||
| fNonRealtimeThread.startThread(); | fNonRealtimeThread.startThread(); | ||||
| } | } | ||||
| @@ -181,6 +194,23 @@ public: | |||||
| return (pthread_t)fRealtimeThread.getThreadId(); | return (pthread_t)fRealtimeThread.getThreadId(); | ||||
| } | } | ||||
| int handleInterposerCallback(const int cb_action, void* const ptr) | |||||
| { | |||||
| carla_stdout("handleInterposerCallback(%o, %p)", cb_action, ptr); | |||||
| switch (cb_action) | |||||
| { | |||||
| case 1: { | |||||
| const CarlaMutexLocker cml(fShmNonRtServerControl.mutex); | |||||
| fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerUiClosed); | |||||
| fShmNonRtServerControl.commitWrite(); | |||||
| break; | |||||
| } | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| protected: | protected: | ||||
| @@ -215,19 +245,6 @@ private: | |||||
| bool fIsOffline; | bool fIsOffline; | ||||
| int64_t fLastPingTime; | int64_t fLastPingTime; | ||||
| struct NumPorts { | |||||
| uint8_t audioIns; | |||||
| uint8_t audioOuts; | |||||
| uint8_t midiIns; | |||||
| uint8_t midiOuts; | |||||
| NumPorts() | |||||
| : audioIns(0), | |||||
| audioOuts(0), | |||||
| midiIns(0), | |||||
| midiOuts(0) {} | |||||
| } fNumPorts; | |||||
| CarlaJackRealtimeThread fRealtimeThread; | CarlaJackRealtimeThread fRealtimeThread; | ||||
| CarlaJackNonRealtimeThread fNonRealtimeThread; | CarlaJackNonRealtimeThread fNonRealtimeThread; | ||||
| @@ -477,7 +494,7 @@ bool CarlaJackAppClient::handleRtData() | |||||
| const uint8_t size(fShmRtClientControl.readByte()); | const uint8_t size(fShmRtClientControl.readByte()); | ||||
| CARLA_SAFE_ASSERT_BREAK(size > 0); | CARLA_SAFE_ASSERT_BREAK(size > 0); | ||||
| if (port >= fNumPorts.midiIns || size > JackMidiPortBuffer::kMaxEventSize || ! fRealtimeThreadMutex.tryLock()) | |||||
| if (port >= fServer.numMidiIns || size > JackMidiPortBuffer::kMaxEventSize || ! fRealtimeThreadMutex.tryLock()) | |||||
| { | { | ||||
| for (uint8_t i=0; i<size; ++i) | for (uint8_t i=0; i<size; ++i) | ||||
| fShmRtClientControl.readByte(); | fShmRtClientControl.readByte(); | ||||
| @@ -513,7 +530,7 @@ bool CarlaJackAppClient::handleRtData() | |||||
| CARLA_SAFE_ASSERT_BREAK(fShmAudioPool.data != nullptr); | CARLA_SAFE_ASSERT_BREAK(fShmAudioPool.data != nullptr); | ||||
| // location to start of audio outputs (shm buffer) | // location to start of audio outputs (shm buffer) | ||||
| float* const fdataRealOuts = fShmAudioPool.data+(fServer.bufferSize*fNumPorts.audioIns); | |||||
| float* const fdataRealOuts = fShmAudioPool.data+(fServer.bufferSize*fServer.numAudioIns); | |||||
| if (! fClients.isEmpty()) | if (! fClients.isEmpty()) | ||||
| { | { | ||||
| @@ -558,8 +575,8 @@ bool CarlaJackAppClient::handleRtData() | |||||
| // check if we can process | // check if we can process | ||||
| if (cmtl2.wasNotLocked() || jclient->processCb == nullptr || ! jclient->activated) | if (cmtl2.wasNotLocked() || jclient->processCb == nullptr || ! jclient->activated) | ||||
| { | { | ||||
| if (fNumPorts.audioOuts > 0) | |||||
| FloatVectorOperations::clear(fdataRealOuts, fServer.bufferSize*fNumPorts.audioOuts); | |||||
| if (fServer.numAudioOuts > 0) | |||||
| FloatVectorOperations::clear(fdataRealOuts, fServer.bufferSize*fServer.numAudioOuts); | |||||
| if (jclient->deactivated) | if (jclient->deactivated) | ||||
| fShmRtClientControl.data->procFlags = 1; | fShmRtClientControl.data->procFlags = 1; | ||||
| @@ -581,7 +598,7 @@ bool CarlaJackAppClient::handleRtData() | |||||
| JackPortState* const jport = it.getValue(nullptr); | JackPortState* const jport = it.getValue(nullptr); | ||||
| CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr); | CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr); | ||||
| if (i++ < fNumPorts.audioIns) | |||||
| if (i++ < fServer.numAudioIns) | |||||
| { | { | ||||
| jport->buffer = fdataReal; | jport->buffer = fdataReal; | ||||
| fdataReal += fServer.bufferSize; | fdataReal += fServer.bufferSize; | ||||
| @@ -594,7 +611,7 @@ bool CarlaJackAppClient::handleRtData() | |||||
| } | } | ||||
| } | } | ||||
| // FIXME one single "if" | // FIXME one single "if" | ||||
| for (; i++ < fNumPorts.audioIns;) | |||||
| for (; i++ < fServer.numAudioIns;) | |||||
| { | { | ||||
| fdataReal += fServer.bufferSize; | fdataReal += fServer.bufferSize; | ||||
| fdataCopy += fServer.bufferSize; | fdataCopy += fServer.bufferSize; | ||||
| @@ -610,7 +627,7 @@ bool CarlaJackAppClient::handleRtData() | |||||
| JackPortState* const jport = it.getValue(nullptr); | JackPortState* const jport = it.getValue(nullptr); | ||||
| CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr); | CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr); | ||||
| if (i++ < fNumPorts.audioOuts) | |||||
| if (i++ < fServer.numAudioOuts) | |||||
| { | { | ||||
| jport->buffer = fdataCopy; | jport->buffer = fdataCopy; | ||||
| fdataCopy += fServer.bufferSize; | fdataCopy += fServer.bufferSize; | ||||
| @@ -622,7 +639,7 @@ bool CarlaJackAppClient::handleRtData() | |||||
| } | } | ||||
| } | } | ||||
| // FIXME one single "if" | // FIXME one single "if" | ||||
| for (; i++ < fNumPorts.audioOuts;) | |||||
| for (; i++ < fServer.numAudioOuts;) | |||||
| { | { | ||||
| FloatVectorOperations::clear(fdataCopy, fServer.bufferSize); | FloatVectorOperations::clear(fdataCopy, fServer.bufferSize); | ||||
| fdataCopy += fServer.bufferSize; | fdataCopy += fServer.bufferSize; | ||||
| @@ -635,7 +652,7 @@ bool CarlaJackAppClient::handleRtData() | |||||
| JackPortState* const jport = it.getValue(nullptr); | JackPortState* const jport = it.getValue(nullptr); | ||||
| CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr); | CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr); | ||||
| if (i++ < fNumPorts.midiIns) | |||||
| if (i++ < fServer.numMidiIns) | |||||
| jport->buffer = &fMidiInBuffers[i-1]; | jport->buffer = &fMidiInBuffers[i-1]; | ||||
| else | else | ||||
| jport->buffer = &fDummyMidiInBuffer; | jport->buffer = &fDummyMidiInBuffer; | ||||
| @@ -648,7 +665,7 @@ bool CarlaJackAppClient::handleRtData() | |||||
| JackPortState* const jport = it.getValue(nullptr); | JackPortState* const jport = it.getValue(nullptr); | ||||
| CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr); | CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr); | ||||
| if (i++ < fNumPorts.midiOuts) | |||||
| if (i++ < fServer.numMidiOuts) | |||||
| jport->buffer = &fMidiOutBuffers[i-1]; | jport->buffer = &fMidiOutBuffers[i-1]; | ||||
| else | else | ||||
| jport->buffer = &fDummyMidiOutBuffer; | jport->buffer = &fDummyMidiOutBuffer; | ||||
| @@ -659,19 +676,19 @@ bool CarlaJackAppClient::handleRtData() | |||||
| jclient->processCb(fServer.bufferSize, jclient->processCbPtr); | jclient->processCb(fServer.bufferSize, jclient->processCbPtr); | ||||
| if (fNumPorts.audioOuts > 0) | |||||
| if (fServer.numAudioOuts > 0) | |||||
| { | { | ||||
| if (++numClientOutputsProcessed == 1) | if (++numClientOutputsProcessed == 1) | ||||
| { | { | ||||
| // first client, we can copy stuff over | // first client, we can copy stuff over | ||||
| FloatVectorOperations::copy(fdataRealOuts, fdataCopyOuts, | FloatVectorOperations::copy(fdataRealOuts, fdataCopyOuts, | ||||
| fServer.bufferSize*fNumPorts.audioOuts); | |||||
| fServer.bufferSize*fServer.numAudioOuts); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| // subsequent clients, add data (then divide by number of clients later on) | // subsequent clients, add data (then divide by number of clients later on) | ||||
| FloatVectorOperations::add(fdataRealOuts, fdataCopyOuts, | FloatVectorOperations::add(fdataRealOuts, fdataCopyOuts, | ||||
| fServer.bufferSize*fNumPorts.audioOuts); | |||||
| fServer.bufferSize*fServer.numAudioOuts); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -682,28 +699,28 @@ bool CarlaJackAppClient::handleRtData() | |||||
| // more than 1 client active, need to divide buffers | // more than 1 client active, need to divide buffers | ||||
| FloatVectorOperations::multiply(fdataRealOuts, | FloatVectorOperations::multiply(fdataRealOuts, | ||||
| 1.0f/static_cast<float>(numClientOutputsProcessed), | 1.0f/static_cast<float>(numClientOutputsProcessed), | ||||
| fServer.bufferSize*fNumPorts.audioOuts); | |||||
| fServer.bufferSize*fServer.numAudioOuts); | |||||
| } | } | ||||
| } | } | ||||
| // fClients.isEmpty() | // fClients.isEmpty() | ||||
| else if (fNumPorts.audioOuts > 0) | |||||
| else if (fServer.numAudioOuts > 0) | |||||
| { | { | ||||
| FloatVectorOperations::clear(fdataRealOuts, fServer.bufferSize*fNumPorts.audioOuts); | |||||
| FloatVectorOperations::clear(fdataRealOuts, fServer.bufferSize*fServer.numAudioOuts); | |||||
| } | } | ||||
| for (uint8_t i=0; i<fNumPorts.midiIns; ++i) | |||||
| for (uint8_t i=0; i<fServer.numMidiIns; ++i) | |||||
| { | { | ||||
| fMidiInBuffers[i].count = 0; | fMidiInBuffers[i].count = 0; | ||||
| fMidiInBuffers[i].bufferPoolPos = 0; | fMidiInBuffers[i].bufferPoolPos = 0; | ||||
| } | } | ||||
| if (fNumPorts.midiOuts > 0) | |||||
| if (fServer.numMidiOuts > 0) | |||||
| { | { | ||||
| uint8_t* midiData(fShmRtClientControl.data->midiOut); | uint8_t* midiData(fShmRtClientControl.data->midiOut); | ||||
| carla_zeroBytes(midiData, kBridgeRtClientDataMidiOutSize); | carla_zeroBytes(midiData, kBridgeRtClientDataMidiOutSize); | ||||
| std::size_t curMidiDataPos = 0; | std::size_t curMidiDataPos = 0; | ||||
| for (uint8_t i=0; i<fNumPorts.midiOuts; ++i) | |||||
| for (uint8_t i=0; i<fServer.numMidiOuts; ++i) | |||||
| { | { | ||||
| JackMidiPortBuffer& midiPortBuf(fMidiOutBuffers[i]); | JackMidiPortBuffer& midiPortBuf(fMidiOutBuffers[i]); | ||||
| @@ -837,7 +854,13 @@ bool CarlaJackAppClient::handleNonRtData() | |||||
| break; | break; | ||||
| case kPluginBridgeNonRtClientShowUI: | case kPluginBridgeNonRtClientShowUI: | ||||
| jack_carla_interposed_action(2, nullptr); | |||||
| break; | |||||
| case kPluginBridgeNonRtClientHideUI: | case kPluginBridgeNonRtClientHideUI: | ||||
| jack_carla_interposed_action(3, nullptr); | |||||
| break; | |||||
| case kPluginBridgeNonRtClientUiParameterChange: | case kPluginBridgeNonRtClientUiParameterChange: | ||||
| case kPluginBridgeNonRtClientUiProgramChange: | case kPluginBridgeNonRtClientUiProgramChange: | ||||
| case kPluginBridgeNonRtClientUiMidiProgramChange: | case kPluginBridgeNonRtClientUiMidiProgramChange: | ||||
| @@ -898,19 +921,19 @@ void CarlaJackAppClient::runNonRealtimeThread() | |||||
| if (! initSharedMemmory()) | if (! initSharedMemmory()) | ||||
| return; | return; | ||||
| if (fNumPorts.midiIns > 0) | |||||
| if (fServer.numMidiIns > 0) | |||||
| { | { | ||||
| fMidiInBuffers = new JackMidiPortBuffer[fNumPorts.midiIns]; | |||||
| fMidiInBuffers = new JackMidiPortBuffer[fServer.numMidiIns]; | |||||
| for (uint8_t i=0; i<fNumPorts.midiIns; ++i) | |||||
| for (uint8_t i=0; i<fServer.numMidiIns; ++i) | |||||
| fMidiInBuffers[i].isInput = true; | fMidiInBuffers[i].isInput = true; | ||||
| } | } | ||||
| if (fNumPorts.midiOuts > 0) | |||||
| if (fServer.numMidiOuts > 0) | |||||
| { | { | ||||
| fMidiOutBuffers = new JackMidiPortBuffer[fNumPorts.midiOuts]; | |||||
| fMidiOutBuffers = new JackMidiPortBuffer[fServer.numMidiOuts]; | |||||
| for (uint8_t i=0; i<fNumPorts.midiOuts; ++i) | |||||
| for (uint8_t i=0; i<fServer.numMidiOuts; ++i) | |||||
| fMidiOutBuffers[i].isInput = false; | fMidiOutBuffers[i].isInput = false; | ||||
| } | } | ||||
| @@ -999,9 +1022,9 @@ void CarlaJackAppClient::runNonRealtimeThread() | |||||
| { | { | ||||
| carla_stderr("CarlaJackAppClient runNonRealtimeThread END - quit itself"); | carla_stderr("CarlaJackAppClient runNonRealtimeThread END - quit itself"); | ||||
| const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex); | |||||
| fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerUiClosed); | |||||
| fShmNonRtServerControl.commitWrite(); | |||||
| //const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex); | |||||
| //fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerUiClosed); | |||||
| //fShmNonRtServerControl.commitWrite(); | |||||
| } | } | ||||
| /* | /* | ||||
| @@ -1014,19 +1037,34 @@ void CarlaJackAppClient::runNonRealtimeThread() | |||||
| */ | */ | ||||
| } | } | ||||
| fRealtimeThread.signalThreadShouldExit(); | |||||
| if (fRealtimeThread.isThreadRunning()) | |||||
| { | |||||
| fRealtimeThread.signalThreadShouldExit(); | |||||
| const CarlaMutexLocker cml(fRealtimeThreadMutex); | |||||
| if (fShmRtClientControl.data != nullptr) | |||||
| fShmRtClientControl.data->procFlags = 1; | |||||
| } | |||||
| clearSharedMemory(); | clearSharedMemory(); | ||||
| fRealtimeThread.stopThread(5000); | fRealtimeThread.stopThread(5000); | ||||
| carla_stderr("CarlaJackAppClient run FINISHED"); | |||||
| carla_stderr("CarlaJackAppClient runNonRealtimeThread FINISHED"); | |||||
| } | } | ||||
| // --------------------------------------------------------------------------------------------------------------------- | // --------------------------------------------------------------------------------------------------------------------- | ||||
| static CarlaJackAppClient gClient; | static CarlaJackAppClient gClient; | ||||
| static int carla_interposed_callback(int cb_action, void* ptr) | |||||
| { | |||||
| return gClient.handleInterposerCallback(cb_action, ptr); | |||||
| } | |||||
| // --------------------------------------------------------------------------------------------------------------------- | |||||
| CARLA_EXPORT | CARLA_EXPORT | ||||
| jack_client_t* jack_client_open(const char* client_name, jack_options_t options, jack_status_t* status, ...) | jack_client_t* jack_client_open(const char* client_name, jack_options_t options, jack_status_t* status, ...) | ||||
| { | { | ||||
| @@ -83,7 +83,7 @@ struct JackMidiPortBuffer { | |||||
| : count(0), | : count(0), | ||||
| isInput(input), | isInput(input), | ||||
| events(nullptr), | events(nullptr), | ||||
| bufferPoolPos(0), | |||||
| bufferPoolPos(kBufferPoolSize), | |||||
| bufferPool(nullptr) {} | bufferPool(nullptr) {} | ||||
| ~JackMidiPortBuffer() | ~JackMidiPortBuffer() | ||||
| @@ -99,8 +99,10 @@ struct JackPortState { | |||||
| void* buffer; | void* buffer; | ||||
| uint index; | uint index; | ||||
| uint flags; | uint flags; | ||||
| bool isMidi; | |||||
| bool isSystem; | |||||
| bool isMidi : 1; | |||||
| bool isSystem : 1; | |||||
| bool isConnected : 1; | |||||
| bool unused : 1; | |||||
| JackPortState() | JackPortState() | ||||
| : name(nullptr), | : name(nullptr), | ||||
| @@ -109,16 +111,19 @@ struct JackPortState { | |||||
| index(0), | index(0), | ||||
| flags(0), | flags(0), | ||||
| isMidi(false), | isMidi(false), | ||||
| isSystem(false) {} | |||||
| isSystem(false), | |||||
| isConnected(false) {} | |||||
| JackPortState(const char* const cn, const char* const pn, const uint i, const uint f, const bool midi, const bool sys) | |||||
| JackPortState(const char* const cn, const char* const pn, const uint i, const uint f, | |||||
| const bool midi, const bool sys, const bool con) | |||||
| : name(strdup(pn)), | : name(strdup(pn)), | ||||
| fullname(nullptr), | fullname(nullptr), | ||||
| buffer(nullptr), | buffer(nullptr), | ||||
| index(i), | index(i), | ||||
| flags(f), | flags(f), | ||||
| isMidi(midi), | isMidi(midi), | ||||
| isSystem(sys) | |||||
| isSystem(sys), | |||||
| isConnected(con) | |||||
| { | { | ||||
| char strBuf[STR_MAX+1]; | char strBuf[STR_MAX+1]; | ||||
| snprintf(strBuf, STR_MAX, "%s:%s", cn, pn); | snprintf(strBuf, STR_MAX, "%s:%s", cn, pn); | ||||
| @@ -235,6 +240,11 @@ struct JackServerState { | |||||
| uint32_t bufferSize; | uint32_t bufferSize; | ||||
| double sampleRate; | double sampleRate; | ||||
| uint8_t numAudioIns; | |||||
| uint8_t numAudioOuts; | |||||
| uint8_t numMidiIns; | |||||
| uint8_t numMidiOuts; | |||||
| bool playing; | bool playing; | ||||
| jack_position_t position; | jack_position_t position; | ||||
| @@ -242,6 +252,10 @@ struct JackServerState { | |||||
| : jackAppPtr(app), | : jackAppPtr(app), | ||||
| bufferSize(0), | bufferSize(0), | ||||
| sampleRate(0.0), | sampleRate(0.0), | ||||
| numAudioIns(0), | |||||
| numAudioOuts(0), | |||||
| numMidiIns(0), | |||||
| numMidiOuts(0), | |||||
| playing(false) | playing(false) | ||||
| { | { | ||||
| carla_zeroStruct(position); | carla_zeroStruct(position); | ||||
| @@ -45,13 +45,59 @@ void jack_port_get_latency_range(jack_port_t*, jack_latency_callback_mode_t, jac | |||||
| //int jack_recompute_total_latencies (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; | //int jack_recompute_total_latencies (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; | ||||
| CARLA_EXPORT | CARLA_EXPORT | ||||
| jack_nframes_t jack_port_get_latency(jack_port_t*) | |||||
| jack_nframes_t jack_port_get_latency(jack_port_t* port) | |||||
| { | { | ||||
| JackPortState* const jport = (JackPortState*)port; | |||||
| CARLA_SAFE_ASSERT_RETURN(jport != nullptr, 0); | |||||
| if (jport->isMidi || ! jport->isSystem) | |||||
| return 0; | |||||
| // TODO | |||||
| const uint32_t bufferSize = 128; | |||||
| const uint32_t latencyMultiplier = 3; | |||||
| if (jport->flags & JackPortIsInput) | |||||
| return bufferSize*latencyMultiplier; | |||||
| if (jport->flags & JackPortIsOutput) | |||||
| return bufferSize; | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| //jack_nframes_t jack_port_get_total_latency (jack_client_t *client, | |||||
| // jack_port_t *port) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; | |||||
| CARLA_EXPORT | |||||
| jack_nframes_t jack_port_get_total_latency(jack_client_t* client, jack_port_t* port) | |||||
| { | |||||
| JackClientState* const jclient = (JackClientState*)client; | |||||
| CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1); | |||||
| JackPortState* const jport = (JackPortState*)port; | |||||
| CARLA_SAFE_ASSERT_RETURN(jport != nullptr, 0); | |||||
| if (jport->isMidi) | |||||
| return 0; | |||||
| // TODO | |||||
| const uint32_t bufferSize = jclient->server.bufferSize; | |||||
| const uint32_t latencyMultiplier = 3; | |||||
| if (jport->isSystem) | |||||
| { | |||||
| if (jport->flags & JackPortIsInput) | |||||
| return bufferSize*latencyMultiplier; | |||||
| if (jport->flags & JackPortIsOutput) | |||||
| return bufferSize; | |||||
| } | |||||
| else | |||||
| { | |||||
| if (jport->flags & JackPortIsInput) | |||||
| return bufferSize; | |||||
| if (jport->flags & JackPortIsOutput) | |||||
| return bufferSize*latencyMultiplier; | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| @@ -53,8 +53,12 @@ void jack_midi_clear_buffer(void* buf) | |||||
| CARLA_SAFE_ASSERT_RETURN(! jmidibuf->isInput,); | CARLA_SAFE_ASSERT_RETURN(! jmidibuf->isInput,); | ||||
| jmidibuf->count = 0; | jmidibuf->count = 0; | ||||
| jmidibuf->bufferPoolPos = 0; | |||||
| std::memset(jmidibuf->bufferPool, 0, JackMidiPortBuffer::kBufferPoolSize); | |||||
| if (jmidibuf->bufferPool != nullptr) | |||||
| { | |||||
| jmidibuf->bufferPoolPos = 0; | |||||
| std::memset(jmidibuf->bufferPool, 0, JackMidiPortBuffer::kBufferPoolSize); | |||||
| } | |||||
| } | } | ||||
| CARLA_EXPORT | CARLA_EXPORT | ||||
| @@ -75,7 +79,11 @@ jack_midi_data_t* jack_midi_event_reserve(void* buf, jack_nframes_t frame, size_ | |||||
| JackMidiPortBuffer* const jmidibuf((JackMidiPortBuffer*)buf); | JackMidiPortBuffer* const jmidibuf((JackMidiPortBuffer*)buf); | ||||
| CARLA_SAFE_ASSERT_RETURN(jmidibuf != nullptr, nullptr); | CARLA_SAFE_ASSERT_RETURN(jmidibuf != nullptr, nullptr); | ||||
| CARLA_SAFE_ASSERT_RETURN(! jmidibuf->isInput, nullptr); | CARLA_SAFE_ASSERT_RETURN(! jmidibuf->isInput, nullptr); | ||||
| CARLA_SAFE_ASSERT_RETURN(size > 0 && size < JackMidiPortBuffer::kMaxEventSize, nullptr); | |||||
| CARLA_SAFE_ASSERT_RETURN(size < JackMidiPortBuffer::kMaxEventSize, nullptr); | |||||
| // back jack applicatons, wow... | |||||
| if (size == 0) | |||||
| return nullptr; | |||||
| if (jmidibuf->count >= JackMidiPortBuffer::kMaxEventCount) | if (jmidibuf->count >= JackMidiPortBuffer::kMaxEventCount) | ||||
| return nullptr; | return nullptr; | ||||
| @@ -95,8 +103,12 @@ int jack_midi_event_write(void* buf, jack_nframes_t frame, const jack_midi_data_ | |||||
| { | { | ||||
| JackMidiPortBuffer* const jmidibuf((JackMidiPortBuffer*)buf); | JackMidiPortBuffer* const jmidibuf((JackMidiPortBuffer*)buf); | ||||
| CARLA_SAFE_ASSERT_RETURN(jmidibuf != nullptr, EFAULT); | CARLA_SAFE_ASSERT_RETURN(jmidibuf != nullptr, EFAULT); | ||||
| CARLA_SAFE_ASSERT_RETURN(! jmidibuf->isInput, EFAULT); | |||||
| CARLA_SAFE_ASSERT_RETURN(size > 0 && size < JackMidiPortBuffer::kMaxEventSize, ENOBUFS); | |||||
| CARLA_SAFE_ASSERT_RETURN(! jmidibuf->isInput, EINVAL); | |||||
| CARLA_SAFE_ASSERT_RETURN(size < JackMidiPortBuffer::kMaxEventSize, ENOBUFS); | |||||
| // back jack applicatons, wow... | |||||
| if (size == 0) | |||||
| return EINVAL; | |||||
| if (jmidibuf->count >= JackMidiPortBuffer::kMaxEventCount) | if (jmidibuf->count >= JackMidiPortBuffer::kMaxEventCount) | ||||
| return ENOBUFS; | return ENOBUFS; | ||||
| @@ -81,13 +81,16 @@ jack_port_t* jack_port_by_name(jack_client_t* client, const char* name) | |||||
| JackClientState* const jclient = (JackClientState*)client; | JackClientState* const jclient = (JackClientState*)client; | ||||
| CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0); | CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0); | ||||
| const JackServerState& jserver(jclient->server); | |||||
| const int commonFlags = JackPortIsPhysical|JackPortIsTerminal; | |||||
| static const JackPortState capturePorts[] = { | static const JackPortState capturePorts[] = { | ||||
| JackPortState("system", "capture_1", 0, JackPortIsOutput|JackPortIsPhysical|JackPortIsTerminal, false, true), | |||||
| JackPortState("system", "capture_2", 1, JackPortIsOutput|JackPortIsPhysical|JackPortIsTerminal, false, true), | |||||
| JackPortState("system", "capture_1", 0, JackPortIsOutput|commonFlags, false, true, jserver.numAudioIns > 0), | |||||
| JackPortState("system", "capture_2", 1, JackPortIsOutput|commonFlags, false, true, jserver.numAudioIns > 1), | |||||
| }; | }; | ||||
| static const JackPortState playbackPorts[] = { | static const JackPortState playbackPorts[] = { | ||||
| JackPortState("system", "playback_1", 3, JackPortIsInput|JackPortIsPhysical|JackPortIsTerminal, false, true), | |||||
| JackPortState("system", "playback_2", 4, JackPortIsInput|JackPortIsPhysical|JackPortIsTerminal, false, true), | |||||
| JackPortState("system", "playback_1", 3, JackPortIsInput|commonFlags, false, true, jserver.numAudioOuts > 0), | |||||
| JackPortState("system", "playback_2", 4, JackPortIsInput|commonFlags, false, true, jserver.numAudioOuts > 1), | |||||
| }; | }; | ||||
| if (std::strncmp(name, "system:", 7) == 0) | if (std::strncmp(name, "system:", 7) == 0) | ||||
| @@ -33,11 +33,15 @@ jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, co | |||||
| CARLA_SAFE_ASSERT_RETURN(port_name != nullptr && port_name[0] != '\0', nullptr); | CARLA_SAFE_ASSERT_RETURN(port_name != nullptr && port_name[0] != '\0', nullptr); | ||||
| CARLA_SAFE_ASSERT_RETURN(port_type != nullptr && port_type[0] != '\0', nullptr); | CARLA_SAFE_ASSERT_RETURN(port_type != nullptr && port_type[0] != '\0', nullptr); | ||||
| const JackServerState& jserver(jclient->server); | |||||
| if (std::strcmp(port_type, JACK_DEFAULT_AUDIO_TYPE) == 0) | if (std::strcmp(port_type, JACK_DEFAULT_AUDIO_TYPE) == 0) | ||||
| { | { | ||||
| if (flags & JackPortIsInput) | if (flags & JackPortIsInput) | ||||
| { | { | ||||
| JackPortState* const port = new JackPortState(jclient->name, port_name, jclient->audioIns.count(), flags, false, false); | |||||
| const std::size_t index = jclient->audioIns.count(); | |||||
| JackPortState* const port = new JackPortState(jclient->name, port_name, index, flags, | |||||
| false, false, index < jserver.numAudioIns); | |||||
| const CarlaMutexLocker cms(jclient->mutex); | const CarlaMutexLocker cms(jclient->mutex); | ||||
| @@ -47,7 +51,9 @@ jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, co | |||||
| if (flags & JackPortIsOutput) | if (flags & JackPortIsOutput) | ||||
| { | { | ||||
| JackPortState* const port = new JackPortState(jclient->name, port_name, jclient->audioOuts.count(), flags, false, false); | |||||
| const std::size_t index = jclient->audioOuts.count(); | |||||
| JackPortState* const port = new JackPortState(jclient->name, port_name, index, flags, | |||||
| false, false, index < jserver.numAudioOuts); | |||||
| const CarlaMutexLocker cms(jclient->mutex); | const CarlaMutexLocker cms(jclient->mutex); | ||||
| @@ -63,7 +69,9 @@ jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, co | |||||
| { | { | ||||
| if (flags & JackPortIsInput) | if (flags & JackPortIsInput) | ||||
| { | { | ||||
| JackPortState* const port = new JackPortState(jclient->name, port_name, jclient->midiIns.count(), flags, true, false); | |||||
| const std::size_t index = jclient->midiIns.count(); | |||||
| JackPortState* const port = new JackPortState(jclient->name, port_name, index, flags, | |||||
| true, false, index < jserver.numMidiIns); | |||||
| const CarlaMutexLocker cms(jclient->mutex); | const CarlaMutexLocker cms(jclient->mutex); | ||||
| @@ -73,7 +81,9 @@ jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, co | |||||
| if (flags & JackPortIsOutput) | if (flags & JackPortIsOutput) | ||||
| { | { | ||||
| JackPortState* const port = new JackPortState(jclient->name, port_name, jclient->midiOuts.count(), flags, true, false); | |||||
| const std::size_t index = jclient->midiOuts.count(); | |||||
| JackPortState* const port = new JackPortState(jclient->name, port_name, index, flags, | |||||
| true, false, index < jserver.numMidiOuts); | |||||
| const CarlaMutexLocker cms(jclient->mutex); | const CarlaMutexLocker cms(jclient->mutex); | ||||
| @@ -216,19 +226,21 @@ uint32_t jack_port_type_id(const jack_port_t* port) | |||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| CARLA_EXPORT | CARLA_EXPORT | ||||
| int jack_port_is_mine(const jack_client_t* client, const jack_port_t* port) | |||||
| int jack_port_is_mine(const jack_client_t*, const jack_port_t* port) | |||||
| { | { | ||||
| carla_stderr2("%s(%p, %p)", __FUNCTION__, client, port); | |||||
| JackPortState* const jport = (JackPortState*)port; | |||||
| CARLA_SAFE_ASSERT_RETURN(jport != nullptr, 0); | |||||
| return 0; | |||||
| return jport->isSystem ? 0 : 1; | |||||
| } | } | ||||
| CARLA_EXPORT | CARLA_EXPORT | ||||
| int jack_port_connected(const jack_port_t* port) | int jack_port_connected(const jack_port_t* port) | ||||
| { | { | ||||
| carla_stderr2("%s(%p)", __FUNCTION__, port); | |||||
| JackPortState* const jport = (JackPortState*)port; | |||||
| CARLA_SAFE_ASSERT_RETURN(jport != nullptr, 0); | |||||
| return 1; | |||||
| return jport->isConnected ? 1 : 0; | |||||
| } | } | ||||
| CARLA_EXPORT | CARLA_EXPORT | ||||
| @@ -259,21 +271,56 @@ const char** jack_port_get_all_connections(const jack_client_t* client, const ja | |||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| //int jack_port_tie (jack_port_t *src, jack_port_t *dst) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; | |||||
| CARLA_EXPORT | |||||
| int jack_port_tie(jack_port_t* src, jack_port_t* dst) | |||||
| { | |||||
| carla_stderr2("%s(%p, %p)", __FUNCTION__, src, dst); | |||||
| return ENOSYS; | |||||
| } | |||||
| //int jack_port_untie (jack_port_t *port) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; | |||||
| CARLA_EXPORT | |||||
| int jack_port_untie(jack_port_t* port) | |||||
| { | |||||
| carla_stderr2("%s(%p)", __FUNCTION__, port); | |||||
| return ENOSYS; | |||||
| } | |||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| //int jack_port_set_name (jack_port_t *port, const char *port_name) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; | |||||
| CARLA_EXPORT | |||||
| int jack_port_set_name(jack_port_t *port, const char *port_name) | |||||
| { | |||||
| carla_stderr2("%s(%p, %s)", __FUNCTION__, port, port_name); | |||||
| return ENOSYS; | |||||
| } | |||||
| //int jack_port_rename (jack_client_t* client, jack_port_t *port, const char *port_name) JACK_OPTIONAL_WEAK_EXPORT; | |||||
| CARLA_EXPORT | |||||
| int jack_port_rename(jack_client_t* client, jack_port_t *port, const char *port_name) | |||||
| { | |||||
| carla_stderr2("%s(%p, %p, %s)", __FUNCTION__, client, port, port_name); | |||||
| return ENOSYS; | |||||
| } | |||||
| //int jack_port_set_alias (jack_port_t *port, const char *alias) JACK_OPTIONAL_WEAK_EXPORT; | |||||
| CARLA_EXPORT | |||||
| int jack_port_set_alias(jack_port_t* port, const char* alias) | |||||
| { | |||||
| carla_stderr2("%s(%p, %s)", __FUNCTION__, port, alias); | |||||
| return ENOSYS; | |||||
| } | |||||
| //int jack_port_unset_alias (jack_port_t *port, const char *alias) JACK_OPTIONAL_WEAK_EXPORT; | |||||
| CARLA_EXPORT | |||||
| int jack_port_unset_alias(jack_port_t* port, const char* alias) | |||||
| { | |||||
| carla_stderr2("%s(%p, %s)", __FUNCTION__, port, alias); | |||||
| return ENOSYS; | |||||
| } | |||||
| //int jack_port_get_aliases (const jack_port_t *port, char* const aliases[2]) JACK_OPTIONAL_WEAK_EXPORT; | |||||
| CARLA_EXPORT | |||||
| int jack_port_get_aliases(const jack_port_t*, char* aliases[2]) | |||||
| { | |||||
| aliases[0] = aliases[1] = nullptr; | |||||
| return 0; | |||||
| } | |||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| @@ -26,13 +26,22 @@ CARLA_BACKEND_USE_NAMESPACE | |||||
| CARLA_EXPORT | CARLA_EXPORT | ||||
| jack_nframes_t jack_frame_time(const jack_client_t* client) | jack_nframes_t jack_frame_time(const jack_client_t* client) | ||||
| { | { | ||||
| JackClientState* const jclient = (JackClientState*)client; | |||||
| const JackClientState* const jclient = (const JackClientState*)client; | |||||
| CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0); | CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0); | ||||
| // FIXME | |||||
| return jclient->server.position.usecs; | return jclient->server.position.usecs; | ||||
| } | } | ||||
| // jack_nframes_t jack_last_frame_time (const jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; | |||||
| CARLA_EXPORT | |||||
| jack_nframes_t jack_last_frame_time(const jack_client_t* client) | |||||
| { | |||||
| const JackClientState* const jclient = (const JackClientState*)client; | |||||
| CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0); | |||||
| // FIXME | |||||
| return jclient->server.position.usecs; | |||||
| } | |||||
| // int jack_get_cycle_times(const jack_client_t *client, | // int jack_get_cycle_times(const jack_client_t *client, | ||||
| // jack_nframes_t *current_frames, | // jack_nframes_t *current_frames, | ||||
| @@ -42,8 +51,23 @@ jack_nframes_t jack_frame_time(const jack_client_t* client) | |||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| // jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t) JACK_OPTIONAL_WEAK_EXPORT; | |||||
| CARLA_EXPORT | |||||
| jack_time_t jack_frames_to_time(const jack_client_t* client, jack_nframes_t frames) | |||||
| { | |||||
| const JackClientState* const jclient = (const JackClientState*)client; | |||||
| CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0); | |||||
| return frames / jclient->server.sampleRate; | |||||
| } | |||||
| // jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t) JACK_OPTIONAL_WEAK_EXPORT; | // jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t) JACK_OPTIONAL_WEAK_EXPORT; | ||||
| // jack_time_t jack_get_time(void) JACK_OPTIONAL_WEAK_EXPORT; | |||||
| CARLA_EXPORT | |||||
| jack_time_t jack_get_time(void) | |||||
| { | |||||
| timespec t; | |||||
| clock_gettime(CLOCK_MONOTONIC, &t); | |||||
| return t.tv_sec * 1000000 + t.tv_nsec / 1000; | |||||
| } | |||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||