@@ -159,12 +159,12 @@ | |||
</item> | |||
<item row="3" column="1" colspan="3"> | |||
<widget class="QCheckBox" name="cb_manage_window"> | |||
<property name="enabled"> | |||
<bool>false</bool> | |||
</property> | |||
<property name="text"> | |||
<string>Take control of main applicaton window</string> | |||
</property> | |||
<property name="checked"> | |||
<bool>true</bool> | |||
</property> | |||
</widget> | |||
</item> | |||
<item row="2" column="1" colspan="2"> | |||
@@ -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); | |||
@@ -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<Window>(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<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; | |||
} | |||
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); | |||
@@ -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; | |||
} | |||
// --------------------------------------------------------------------------------------------------------------------- |
@@ -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; i<size; ++i) | |||
fShmRtClientControl.readByte(); | |||
@@ -513,7 +530,7 @@ bool CarlaJackAppClient::handleRtData() | |||
CARLA_SAFE_ASSERT_BREAK(fShmAudioPool.data != nullptr); | |||
// 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()) | |||
{ | |||
@@ -558,8 +575,8 @@ bool CarlaJackAppClient::handleRtData() | |||
// check if we can process | |||
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) | |||
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<float>(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<fNumPorts.midiIns; ++i) | |||
for (uint8_t i=0; i<fServer.numMidiIns; ++i) | |||
{ | |||
fMidiInBuffers[i].count = 0; | |||
fMidiInBuffers[i].bufferPoolPos = 0; | |||
} | |||
if (fNumPorts.midiOuts > 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<fNumPorts.midiOuts; ++i) | |||
for (uint8_t i=0; i<fServer.numMidiOuts; ++i) | |||
{ | |||
JackMidiPortBuffer& midiPortBuf(fMidiOutBuffers[i]); | |||
@@ -837,7 +854,13 @@ bool CarlaJackAppClient::handleNonRtData() | |||
break; | |||
case kPluginBridgeNonRtClientShowUI: | |||
jack_carla_interposed_action(2, nullptr); | |||
break; | |||
case kPluginBridgeNonRtClientHideUI: | |||
jack_carla_interposed_action(3, nullptr); | |||
break; | |||
case kPluginBridgeNonRtClientUiParameterChange: | |||
case kPluginBridgeNonRtClientUiProgramChange: | |||
case kPluginBridgeNonRtClientUiMidiProgramChange: | |||
@@ -898,19 +921,19 @@ void CarlaJackAppClient::runNonRealtimeThread() | |||
if (! initSharedMemmory()) | |||
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; | |||
} | |||
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; | |||
} | |||
@@ -999,9 +1022,9 @@ void CarlaJackAppClient::runNonRealtimeThread() | |||
{ | |||
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(); | |||
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, ...) | |||
{ | |||
@@ -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); | |||
@@ -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; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
@@ -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; | |||
@@ -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) | |||
@@ -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; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
@@ -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; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- |