From 4fd815b2d840db7fce73808e5c9a3fd234d0e2fd Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 8 Oct 2017 02:46:02 +0200 Subject: [PATCH] libjack can now control window show/hide, lots more libjack work --- resources/ui/carla_add_jack.ui | 6 +- source/backend/plugin/CarlaPluginJack.cpp | 91 +++++++---- source/interposer/interposer-jack-x11.cpp | 182 ++++++++++++++++++---- source/libjack/libjack.cpp | 134 ++++++++++------ source/libjack/libjack.hpp | 26 +++- source/libjack/libjack_latency.cpp | 52 ++++++- source/libjack/libjack_midi.cpp | 22 ++- source/libjack/libjack_port-searching.cpp | 11 +- source/libjack/libjack_ports.cpp | 79 ++++++++-- source/libjack/libjack_time.cpp | 32 +++- 10 files changed, 487 insertions(+), 148 deletions(-) diff --git a/resources/ui/carla_add_jack.ui b/resources/ui/carla_add_jack.ui index a2bf64d2c..1c19624bf 100644 --- a/resources/ui/carla_add_jack.ui +++ b/resources/ui/carla_add_jack.ui @@ -159,12 +159,12 @@ - - false - Take control of main applicaton window + + true + diff --git a/source/backend/plugin/CarlaPluginJack.cpp b/source/backend/plugin/CarlaPluginJack.cpp index 91f224a2a..472e0e495 100644 --- a/source/backend/plugin/CarlaPluginJack.cpp +++ b/source/backend/plugin/CarlaPluginJack.cpp @@ -130,7 +130,11 @@ protected: const ScopedEnvVar sev2("LD_LIBRARY_PATH", libjackdir.buffer()); 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_SHM_IDS", fShmIds.buffer()); @@ -154,12 +158,12 @@ protected: 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(); } else { - carla_stdout("CarlaPluginJackThread::run() - bridge auto-closed successfully"); + carla_stdout("CarlaPluginJackThread::run() - application auto-closed successfully"); } } else @@ -167,7 +171,7 @@ protected: // forced quit, may have crashed 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" "Saving now will lose its current settings.\n" @@ -176,7 +180,7 @@ protected: } else { - carla_stderr("CarlaPluginJackThread::run() - bridge closed itself"); + carla_stderr("CarlaPluginJackThread::run() - application closed itself"); } } @@ -364,6 +368,18 @@ public: // ------------------------------------------------------------------- // 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 { if (fBridgeThread.isThreadRunning()) @@ -395,9 +411,12 @@ public: fTimedOut = true; fTimedError = true; 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(); @@ -941,7 +960,6 @@ public: if (fShmRtClientControl.data->procFlags) { - carla_stdout("PROC Flags active, disabling plugin"); fInitiated = false; fProcCanceled = true; } @@ -1126,17 +1144,11 @@ public: break; 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; case kPluginBridgeNonRtServerError: { @@ -1220,6 +1232,8 @@ public: fInfo.setupLabel = label; + const int setupHints = label[4] - '0'; + // --------------------------------------------------------------- // set info @@ -1265,6 +1279,17 @@ public: 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 @@ -1283,14 +1308,6 @@ public: if (! restartBridgeThread()) 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 @@ -1344,6 +1361,24 @@ private: CARLA_DECLARE_NON_COPY_STRUCT(Info) } 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) { fShmAudioPool.resize(bufferSize, fInfo.aIns+fInfo.aOuts, 0); diff --git a/source/interposer/interposer-jack-x11.cpp b/source/interposer/interposer-jack-x11.cpp index 4143cc9de..9479d3580 100644 --- a/source/interposer/interposer-jack-x11.cpp +++ b/source/interposer/interposer-jack-x11.cpp @@ -22,9 +22,22 @@ struct ScopedLibOpen { void* handle; + Window winId; 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(winIdLL); + } + } ~ScopedLibOpen() { @@ -33,18 +46,23 @@ struct ScopedLibOpen { } }; -// ----------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- // Function typedefs typedef int (*XMapWindowFunc)(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 static int real_XMapWindow(Display* display, Window window) @@ -63,7 +81,7 @@ static int real_XUnmapWindow(Display* display, Window window) return func(display, window); } -// ----------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- // Our custom functions CARLA_EXPORT @@ -73,7 +91,7 @@ int XMapWindow(Display* display, Window window) for (;;) { - if (sCurrentlyMappedWindow != 0) + if (slo.winId == 0) break; Atom atom; @@ -84,40 +102,96 @@ int XMapWindow(Display* display, Window window) const Atom wmWindowType = XInternAtom(display, "_NET_WM_WINDOW_TYPE", True); 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 %s", atomValue); } } - if (sCurrentlyMappedWindow == 0) + if (! isMainWindow) + { + // this has always bothered me... + if (gCurrentlyMappedWindow != 0 && gCurrentWindowMapped && gCurrentWindowVisible) + XSetTransientForHint(display, window, gCurrentlyMappedWindow); 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(winIdLL)); - XSetTransientForHint(display, window, static_cast(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); @@ -126,10 +200,56 @@ int XMapWindow(Display* display, Window window) CARLA_EXPORT 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); } -// ----------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- + +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; +} + +// --------------------------------------------------------------------------------------------------------------------- diff --git a/source/libjack/libjack.cpp b/source/libjack/libjack.cpp index 343418b95..95ca50f7f 100644 --- a/source/libjack/libjack.cpp +++ b/source/libjack/libjack.cpp @@ -24,6 +24,15 @@ using juce::FloatVectorOperations; using juce::Thread; 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 // --------------------------------------------------------------------------------------------------------------------- @@ -76,6 +85,8 @@ private: Callback* const fCallback; }; +static int carla_interposed_callback(int, void*); + // --------------------------------------------------------------------------------------------------------------------- class CarlaJackAppClient : public CarlaJackRealtimeThread::Callback, @@ -90,7 +101,7 @@ public: fAudioPoolCopy(nullptr), fAudioTmpBuf(nullptr), fDummyMidiInBuffer(true, "ignored"), - fDummyMidiOutBuffer(true, "ignored"), + fDummyMidiOutBuffer(false, "ignored"), fMidiInBuffers(nullptr), fMidiOutBuffers(nullptr), fIsOffline(false), @@ -127,10 +138,12 @@ public: fBaseNameNonRtClientControl[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(); } @@ -181,6 +194,23 @@ public: 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: @@ -215,19 +245,6 @@ private: bool fIsOffline; 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; CarlaJackNonRealtimeThread fNonRealtimeThread; @@ -477,7 +494,7 @@ bool CarlaJackAppClient::handleRtData() const uint8_t size(fShmRtClientControl.readByte()); 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; iprocessCb == 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) fShmRtClientControl.data->procFlags = 1; @@ -581,7 +598,7 @@ bool CarlaJackAppClient::handleRtData() JackPortState* const jport = it.getValue(nullptr); CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr); - if (i++ < fNumPorts.audioIns) + if (i++ < fServer.numAudioIns) { jport->buffer = fdataReal; fdataReal += fServer.bufferSize; @@ -594,7 +611,7 @@ bool CarlaJackAppClient::handleRtData() } } // FIXME one single "if" - for (; i++ < fNumPorts.audioIns;) + for (; i++ < fServer.numAudioIns;) { fdataReal += fServer.bufferSize; fdataCopy += fServer.bufferSize; @@ -610,7 +627,7 @@ bool CarlaJackAppClient::handleRtData() JackPortState* const jport = it.getValue(nullptr); CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr); - if (i++ < fNumPorts.audioOuts) + if (i++ < fServer.numAudioOuts) { jport->buffer = fdataCopy; fdataCopy += fServer.bufferSize; @@ -622,7 +639,7 @@ bool CarlaJackAppClient::handleRtData() } } // FIXME one single "if" - for (; i++ < fNumPorts.audioOuts;) + for (; i++ < fServer.numAudioOuts;) { FloatVectorOperations::clear(fdataCopy, fServer.bufferSize); fdataCopy += fServer.bufferSize; @@ -635,7 +652,7 @@ bool CarlaJackAppClient::handleRtData() JackPortState* const jport = it.getValue(nullptr); CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr); - if (i++ < fNumPorts.midiIns) + if (i++ < fServer.numMidiIns) jport->buffer = &fMidiInBuffers[i-1]; else jport->buffer = &fDummyMidiInBuffer; @@ -648,7 +665,7 @@ bool CarlaJackAppClient::handleRtData() JackPortState* const jport = it.getValue(nullptr); CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr); - if (i++ < fNumPorts.midiOuts) + if (i++ < fServer.numMidiOuts) jport->buffer = &fMidiOutBuffers[i-1]; else jport->buffer = &fDummyMidiOutBuffer; @@ -659,19 +676,19 @@ bool CarlaJackAppClient::handleRtData() jclient->processCb(fServer.bufferSize, jclient->processCbPtr); - if (fNumPorts.audioOuts > 0) + if (fServer.numAudioOuts > 0) { if (++numClientOutputsProcessed == 1) { // first client, we can copy stuff over FloatVectorOperations::copy(fdataRealOuts, fdataCopyOuts, - fServer.bufferSize*fNumPorts.audioOuts); + fServer.bufferSize*fServer.numAudioOuts); } else { // subsequent clients, add data (then divide by number of clients later on) 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 FloatVectorOperations::multiply(fdataRealOuts, 1.0f/static_cast(numClientOutputsProcessed), - fServer.bufferSize*fNumPorts.audioOuts); + fServer.bufferSize*fServer.numAudioOuts); } } // 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 0) + if (fServer.numMidiOuts > 0) { uint8_t* midiData(fShmRtClientControl.data->midiOut); carla_zeroBytes(midiData, kBridgeRtClientDataMidiOutSize); std::size_t curMidiDataPos = 0; - for (uint8_t i=0; i 0) + if (fServer.numMidiIns > 0) { - fMidiInBuffers = new JackMidiPortBuffer[fNumPorts.midiIns]; + fMidiInBuffers = new JackMidiPortBuffer[fServer.numMidiIns]; - for (uint8_t i=0; i 0) + if (fServer.numMidiOuts > 0) { - fMidiOutBuffers = new JackMidiPortBuffer[fNumPorts.midiOuts]; + fMidiOutBuffers = new JackMidiPortBuffer[fServer.numMidiOuts]; - for (uint8_t i=0; iprocFlags = 1; + } clearSharedMemory(); fRealtimeThread.stopThread(5000); - carla_stderr("CarlaJackAppClient run FINISHED"); + carla_stderr("CarlaJackAppClient runNonRealtimeThread FINISHED"); } // --------------------------------------------------------------------------------------------------------------------- static CarlaJackAppClient gClient; +static int carla_interposed_callback(int cb_action, void* ptr) +{ + return gClient.handleInterposerCallback(cb_action, ptr); +} + +// --------------------------------------------------------------------------------------------------------------------- + CARLA_EXPORT jack_client_t* jack_client_open(const char* client_name, jack_options_t options, jack_status_t* status, ...) { diff --git a/source/libjack/libjack.hpp b/source/libjack/libjack.hpp index 0f7935e68..45d2756f0 100644 --- a/source/libjack/libjack.hpp +++ b/source/libjack/libjack.hpp @@ -83,7 +83,7 @@ struct JackMidiPortBuffer { : count(0), isInput(input), events(nullptr), - bufferPoolPos(0), + bufferPoolPos(kBufferPoolSize), bufferPool(nullptr) {} ~JackMidiPortBuffer() @@ -99,8 +99,10 @@ struct JackPortState { void* buffer; uint index; uint flags; - bool isMidi; - bool isSystem; + bool isMidi : 1; + bool isSystem : 1; + bool isConnected : 1; + bool unused : 1; JackPortState() : name(nullptr), @@ -109,16 +111,19 @@ struct JackPortState { index(0), flags(0), 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)), fullname(nullptr), buffer(nullptr), index(i), flags(f), isMidi(midi), - isSystem(sys) + isSystem(sys), + isConnected(con) { char strBuf[STR_MAX+1]; snprintf(strBuf, STR_MAX, "%s:%s", cn, pn); @@ -235,6 +240,11 @@ struct JackServerState { uint32_t bufferSize; double sampleRate; + uint8_t numAudioIns; + uint8_t numAudioOuts; + uint8_t numMidiIns; + uint8_t numMidiOuts; + bool playing; jack_position_t position; @@ -242,6 +252,10 @@ struct JackServerState { : jackAppPtr(app), bufferSize(0), sampleRate(0.0), + numAudioIns(0), + numAudioOuts(0), + numMidiIns(0), + numMidiOuts(0), playing(false) { carla_zeroStruct(position); diff --git a/source/libjack/libjack_latency.cpp b/source/libjack/libjack_latency.cpp index 7904a8770..aa36edea5 100644 --- a/source/libjack/libjack_latency.cpp +++ b/source/libjack/libjack_latency.cpp @@ -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; 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; } -//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; +} // -------------------------------------------------------------------------------------------------------------------- diff --git a/source/libjack/libjack_midi.cpp b/source/libjack/libjack_midi.cpp index dd60cbdf9..237a87f50 100644 --- a/source/libjack/libjack_midi.cpp +++ b/source/libjack/libjack_midi.cpp @@ -53,8 +53,12 @@ void jack_midi_clear_buffer(void* buf) CARLA_SAFE_ASSERT_RETURN(! jmidibuf->isInput,); 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 @@ -75,7 +79,11 @@ jack_midi_data_t* jack_midi_event_reserve(void* buf, jack_nframes_t frame, size_ JackMidiPortBuffer* const jmidibuf((JackMidiPortBuffer*)buf); CARLA_SAFE_ASSERT_RETURN(jmidibuf != nullptr, 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) 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); 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) return ENOBUFS; diff --git a/source/libjack/libjack_port-searching.cpp b/source/libjack/libjack_port-searching.cpp index bfed11182..b3aee4cd3 100644 --- a/source/libjack/libjack_port-searching.cpp +++ b/source/libjack/libjack_port-searching.cpp @@ -81,13 +81,16 @@ jack_port_t* jack_port_by_name(jack_client_t* client, const char* name) JackClientState* const jclient = (JackClientState*)client; CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0); + const JackServerState& jserver(jclient->server); + const int commonFlags = JackPortIsPhysical|JackPortIsTerminal; + 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[] = { - 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) diff --git a/source/libjack/libjack_ports.cpp b/source/libjack/libjack_ports.cpp index fdc6d0773..e1d629cf8 100644 --- a/source/libjack/libjack_ports.cpp +++ b/source/libjack/libjack_ports.cpp @@ -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_type != nullptr && port_type[0] != '\0', nullptr); + const JackServerState& jserver(jclient->server); + if (std::strcmp(port_type, JACK_DEFAULT_AUDIO_TYPE) == 0) { 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); @@ -47,7 +51,9 @@ jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, co 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); @@ -63,7 +69,9 @@ jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, co { 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); @@ -73,7 +81,9 @@ jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, co 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); @@ -216,19 +226,21 @@ uint32_t jack_port_type_id(const jack_port_t* port) // -------------------------------------------------------------------------------------------------------------------- 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 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 @@ -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; +} // -------------------------------------------------------------------------------------------------------------------- diff --git a/source/libjack/libjack_time.cpp b/source/libjack/libjack_time.cpp index 277c56de7..2f8b7e3ad 100644 --- a/source/libjack/libjack_time.cpp +++ b/source/libjack/libjack_time.cpp @@ -26,13 +26,22 @@ CARLA_BACKEND_USE_NAMESPACE CARLA_EXPORT 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); + // FIXME 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, // 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_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; +} // --------------------------------------------------------------------------------------------------------------------