via special env var CARLA_BRIDGE_DUMMY Signed-off-by: falkTX <falktx@falktx.com>tags/v2.1-rc1
@@ -69,7 +69,12 @@ enum EngineType { | |||
/*! | |||
* Bridge engine type, used in BridgePlugin class. | |||
*/ | |||
kEngineTypeBridge = 5 | |||
kEngineTypeBridge = 5, | |||
/*! | |||
* Dummy engine type, does not send audio or MIDI anywhere. | |||
*/ | |||
kEngineTypeDummy = 6, | |||
}; | |||
/*! | |||
@@ -1263,6 +1268,9 @@ public: | |||
// ------------------------------------------------------------------- | |||
// Engine initializers | |||
// Dummy | |||
static CarlaEngine* newDummy(); | |||
// JACK | |||
static CarlaEngine* newJack(); | |||
@@ -338,8 +338,20 @@ bool carla_engine_init(const char* driverName, const char* clientName) | |||
gStandalone.engine = engine; | |||
#ifdef BUILD_BRIDGE | |||
engine->setOption(CB::ENGINE_OPTION_PROCESS_MODE, CB::ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS, nullptr); | |||
engine->setOption(CB::ENGINE_OPTION_TRANSPORT_MODE, CB::ENGINE_TRANSPORT_MODE_JACK, nullptr); | |||
if (std::getenv("CARLA_BRIDGE_DUMMY") != nullptr) | |||
{ | |||
// engine->setOption(CB::ENGINE_OPTION_PROCESS_MODE, CB::ENGINE_PROCESS_MODE_PATCHBAY, nullptr); | |||
engine->setOption(CB::ENGINE_OPTION_PROCESS_MODE, CB::ENGINE_PROCESS_MODE_CONTINUOUS_RACK, nullptr); | |||
engine->setOption(CB::ENGINE_OPTION_TRANSPORT_MODE, CB::ENGINE_TRANSPORT_MODE_INTERNAL, nullptr); | |||
engine->setOption(CB::ENGINE_OPTION_AUDIO_BUFFER_SIZE, 4096, nullptr); | |||
engine->setOption(CB::ENGINE_OPTION_AUDIO_SAMPLE_RATE, 48000, nullptr); | |||
} | |||
else | |||
{ | |||
engine->setOption(CB::ENGINE_OPTION_PROCESS_MODE, CB::ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS, nullptr); | |||
engine->setOption(CB::ENGINE_OPTION_TRANSPORT_MODE, CB::ENGINE_TRANSPORT_MODE_JACK, nullptr); | |||
} | |||
engine->setOption(CB::ENGINE_OPTION_FORCE_STEREO, false, nullptr); | |||
engine->setOption(CB::ENGINE_OPTION_PREFER_PLUGIN_BRIDGES, false, nullptr); | |||
engine->setOption(CB::ENGINE_OPTION_PREFER_UI_BRIDGES, false, nullptr); | |||
@@ -198,6 +198,9 @@ CarlaEngine* CarlaEngine::newDriverByName(const char* const driverName) | |||
CARLA_SAFE_ASSERT_RETURN(driverName != nullptr && driverName[0] != '\0', nullptr); | |||
carla_debug("CarlaEngine::newDriverByName(\"%s\")", driverName); | |||
if (std::strcmp(driverName, "Dummy") == 0) | |||
return newDummy(); | |||
if (std::strcmp(driverName, "JACK") == 0) | |||
return newJack(); | |||
@@ -226,8 +229,6 @@ CarlaEngine* CarlaEngine::newDriverByName(const char* const driverName) | |||
// ------------------------------------------------------------------- | |||
// common | |||
if (std::strcmp(driverName, "Dummy") == 0) | |||
return newRtAudio(AUDIO_API_NULL); | |||
if (std::strncmp(driverName, "JACK ", 5) == 0) | |||
return newRtAudio(AUDIO_API_JACK); | |||
if (std::strcmp(driverName, "OSS") == 0) | |||
@@ -0,0 +1,287 @@ | |||
/* | |||
* Carla Plugin Host | |||
* Copyright (C) 2011-2019 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU General Public License as | |||
* published by the Free Software Foundation; either version 2 of | |||
* the License, or any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* For a full copy of the GNU General Public License see the GPL.txt file | |||
*/ | |||
#include "CarlaEngineGraph.hpp" | |||
#include "CarlaEngineInternal.hpp" | |||
// #include "CarlaBackendUtils.hpp" | |||
CARLA_BACKEND_START_NAMESPACE | |||
// ------------------------------------------------------------------------------------------------------------------- | |||
// Dummy Engine | |||
class CarlaEngineDummy : public CarlaEngine, | |||
public CarlaThread | |||
{ | |||
public: | |||
CarlaEngineDummy() | |||
: CarlaEngine(), | |||
CarlaThread("CarlaEngineDummy"), | |||
fRunning(false) | |||
{ | |||
carla_debug("CarlaEngineDummy::CarlaEngineDummy()"); | |||
// just to make sure | |||
pData->options.transportMode = ENGINE_TRANSPORT_MODE_INTERNAL; | |||
} | |||
~CarlaEngineDummy() override | |||
{ | |||
carla_debug("CarlaEngineDummy::~CarlaEngineDummy()"); | |||
} | |||
// ------------------------------------- | |||
bool init(const char* const clientName) override | |||
{ | |||
CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false); | |||
carla_debug("CarlaEngineDummy::init(\"%s\")", clientName); | |||
if (pData->options.processMode != ENGINE_PROCESS_MODE_CONTINUOUS_RACK) | |||
{ | |||
setLastError("Invalid process mode"); | |||
return false; | |||
} | |||
fRunning = true; | |||
if (! pData->init(clientName)) | |||
{ | |||
close(); | |||
setLastError("Failed to init internal data"); | |||
return false; | |||
} | |||
pData->bufferSize = pData->options.audioBufferSize; | |||
pData->sampleRate = pData->options.audioSampleRate; | |||
pData->initTime(pData->options.transportExtra); | |||
pData->graph.create(2, 2); | |||
if (! startThread(true)) | |||
{ | |||
close(); | |||
setLastError("Failed to start dummy audio thread"); | |||
return false; | |||
} | |||
patchbayRefresh(true, false, false); | |||
callback(true, true, | |||
ENGINE_CALLBACK_ENGINE_STARTED, | |||
0, | |||
pData->options.processMode, | |||
pData->options.transportMode, | |||
static_cast<int>(pData->bufferSize), | |||
static_cast<float>(pData->sampleRate), | |||
getCurrentDriverName()); | |||
return true; | |||
} | |||
bool close() override | |||
{ | |||
carla_debug("CarlaEngineDummy::close()"); | |||
fRunning = false; | |||
stopThread(-1); | |||
CarlaEngine::close(); | |||
pData->graph.destroy(); | |||
return true; | |||
} | |||
bool isRunning() const noexcept override | |||
{ | |||
return fRunning; | |||
} | |||
bool isOffline() const noexcept override | |||
{ | |||
return false; | |||
} | |||
EngineType getType() const noexcept override | |||
{ | |||
return kEngineTypeDummy; | |||
} | |||
const char* getCurrentDriverName() const noexcept override | |||
{ | |||
return "Dummy"; | |||
} | |||
// ------------------------------------------------------------------- | |||
// Patchbay | |||
bool patchbayRefresh(const bool sendHost, const bool sendOSC, const bool) override | |||
{ | |||
CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false); | |||
RackGraph* const graph = pData->graph.getRackGraph(); | |||
CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false); | |||
ExternalGraph& extGraph(graph->extGraph); | |||
// --------------------------------------------------------------- | |||
// clear last ports | |||
extGraph.clear(); | |||
// --------------------------------------------------------------- | |||
// fill in new ones | |||
{ | |||
PortNameToId portNameToId; | |||
portNameToId.setData(kExternalGraphGroupAudioIn, 1, "capture_1", ""); | |||
extGraph.audioPorts.ins.append(portNameToId); | |||
} | |||
{ | |||
PortNameToId portNameToId; | |||
portNameToId.setData(kExternalGraphGroupAudioIn, 2, "capture_2", ""); | |||
extGraph.audioPorts.ins.append(portNameToId); | |||
} | |||
{ | |||
PortNameToId portNameToId; | |||
portNameToId.setData(kExternalGraphGroupAudioOut, 1, "playback_1", ""); | |||
extGraph.audioPorts.outs.append(portNameToId); | |||
} | |||
{ | |||
PortNameToId portNameToId; | |||
portNameToId.setData(kExternalGraphGroupAudioOut, 2, "playback_2", ""); | |||
extGraph.audioPorts.outs.append(portNameToId); | |||
} | |||
// --------------------------------------------------------------- | |||
// now refresh | |||
if (sendHost || sendOSC) | |||
graph->refresh(sendHost, sendOSC, false, "Dummy"); | |||
return true; | |||
} | |||
// ------------------------------------------------------------------- | |||
protected: | |||
static int64_t getTimeInMicroseconds() noexcept | |||
{ | |||
#if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN) | |||
struct timeval tv; | |||
gettimeofday(&tv, nullptr); | |||
return (tv.tv_sec * 1000000) + tv.tv_usec; | |||
#else | |||
struct timespec ts; | |||
# ifdef CLOCK_MONOTONIC_RAW | |||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts); | |||
# else | |||
clock_gettime(CLOCK_MONOTONIC, &ts); | |||
# endif | |||
return (ts.tv_sec * 1000000) + (ts.tv_nsec / 1000); | |||
#endif | |||
} | |||
void run() override | |||
{ | |||
const uint32_t bufferSize = pData->bufferSize; | |||
const int64_t cycleTime = static_cast<int64_t>( | |||
static_cast<double>(bufferSize) / pData->sampleRate * 1000000 + 0.5); | |||
carla_stdout("CarlaEngineDummy audio thread started, cycle time: " P_INT64 "ms", cycleTime / 1000); | |||
float* audioIns[2] = { | |||
(float*)std::malloc(sizeof(float)*bufferSize), | |||
(float*)std::malloc(sizeof(float)*bufferSize), | |||
}; | |||
CARLA_SAFE_ASSERT_RETURN(audioIns[0] != nullptr,); | |||
CARLA_SAFE_ASSERT_RETURN(audioIns[1] != nullptr,); | |||
float* audioOuts[2] = { | |||
(float*)std::malloc(sizeof(float)*bufferSize), | |||
(float*)std::malloc(sizeof(float)*bufferSize), | |||
}; | |||
CARLA_SAFE_ASSERT_RETURN(audioOuts[0] != nullptr,); | |||
CARLA_SAFE_ASSERT_RETURN(audioOuts[1] != nullptr,); | |||
carla_zeroFloats(audioIns[0], bufferSize); | |||
carla_zeroFloats(audioIns[1], bufferSize); | |||
carla_zeroStructs(pData->events.in, kMaxEngineEventInternalCount); | |||
while (! shouldThreadExit()) | |||
{ | |||
const int64_t oldTime = getTimeInMicroseconds(); | |||
const PendingRtEventsRunner prt(this, bufferSize, true); | |||
carla_zeroFloats(audioOuts[0], bufferSize); | |||
carla_zeroFloats(audioOuts[1], bufferSize); | |||
carla_zeroStructs(pData->events.out, kMaxEngineEventInternalCount); | |||
pData->graph.process(pData, audioIns, audioOuts, bufferSize); | |||
const int64_t newTime = getTimeInMicroseconds(); | |||
CARLA_SAFE_ASSERT_CONTINUE(newTime >= oldTime); | |||
const int64_t remainingTime = cycleTime - (newTime - oldTime); | |||
if (remainingTime <= 0) | |||
{ | |||
++pData->xruns; | |||
carla_stdout("XRUN! remaining time: " P_INT64 ", old: " P_INT64 ", new: " P_INT64 ")", | |||
remainingTime, oldTime, newTime); | |||
} | |||
else | |||
{ | |||
CARLA_SAFE_ASSERT_CONTINUE(remainingTime < 1000000); // 1 sec | |||
carla_msleep(static_cast<uint>(remainingTime / 1000)); | |||
} | |||
} | |||
std::free(audioIns[0]); | |||
std::free(audioIns[1]); | |||
std::free(audioOuts[0]); | |||
std::free(audioOuts[1]); | |||
carla_stdout("CarlaEngineDummy audio thread finished"); | |||
} | |||
// ------------------------------------------------------------------- | |||
private: | |||
bool fRunning; | |||
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineDummy) | |||
}; | |||
// ----------------------------------------- | |||
CarlaEngine* CarlaEngine::newDummy() | |||
{ | |||
return new CarlaEngineDummy(); | |||
} | |||
// ----------------------------------------- | |||
CARLA_BACKEND_END_NAMESPACE |
@@ -1138,7 +1138,7 @@ CarlaEngine* CarlaEngine::newRtAudio(const AudioApi api) | |||
{ | |||
initRtAudioAPIsIfNeeded(); | |||
RtAudio::Api rtApi(RtAudio::UNSPECIFIED); | |||
RtAudio::Api rtApi = RtAudio::UNSPECIFIED; | |||
switch (api) | |||
{ | |||
@@ -20,6 +20,7 @@ OBJS = \ | |||
$(OBJDIR)/CarlaEngine.cpp.o \ | |||
$(OBJDIR)/CarlaEngineClient.cpp.o \ | |||
$(OBJDIR)/CarlaEngineData.cpp.o \ | |||
$(OBJDIR)/CarlaEngineDummy.cpp.o \ | |||
$(OBJDIR)/CarlaEngineGraph.cpp.o \ | |||
$(OBJDIR)/CarlaEngineInternal.cpp.o \ | |||
$(OBJDIR)/CarlaEnginePorts.cpp.o \ | |||
@@ -212,9 +212,21 @@ public: | |||
carla_set_engine_callback(callback, this); | |||
if (useBridge) | |||
carla_engine_init_bridge(audioPoolBaseName, rtClientBaseName, nonRtClientBaseName, nonRtServerBaseName, clientName); | |||
{ | |||
carla_engine_init_bridge(audioPoolBaseName, | |||
rtClientBaseName, | |||
nonRtClientBaseName, | |||
nonRtServerBaseName, | |||
clientName); | |||
} | |||
else if (std::getenv("CARLA_BRIDGE_DUMMY") != nullptr) | |||
{ | |||
carla_engine_init("Dummy", clientName); | |||
} | |||
else | |||
{ | |||
carla_engine_init("JACK", clientName); | |||
} | |||
fEngine = carla_get_engine(); | |||
} | |||
@@ -333,8 +345,15 @@ private: | |||
carla_debug("CarlaBridgePlugin::callback(%p, %i:%s, %i, %i, %i, %i, %f, \"%s\")", | |||
ptr, action, EngineCallbackOpcode2Str(action), | |||
pluginId, value1, value2, value3, valuef, valueStr); | |||
// ptr must not be null | |||
CARLA_SAFE_ASSERT_RETURN(ptr != nullptr,); | |||
CARLA_SAFE_ASSERT_RETURN(pluginId == 0,); | |||
// pluginId must be 0 (first), except for patchbay things | |||
if (action < CarlaBackend::ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED || | |||
action > CarlaBackend::ENGINE_CALLBACK_PATCHBAY_CONNECTION_REMOVED) { | |||
CARLA_SAFE_ASSERT_UINT_RETURN(pluginId == 0, pluginId,); | |||
} | |||
return ((CarlaBridgePlugin*)ptr)->handleCallback(action, value1, value2, value3, valuef, valueStr); | |||
} | |||
@@ -167,6 +167,7 @@ OBJS_native = \ | |||
$(OBJDIR)/CarlaEngine.cpp.o \ | |||
$(OBJDIR)/CarlaEngineClient.cpp.o \ | |||
$(OBJDIR)/CarlaEngineData.cpp.o \ | |||
$(OBJDIR)/CarlaEngineDummy.cpp.o \ | |||
$(OBJDIR)/CarlaEngineGraph.cpp.o \ | |||
$(OBJDIR)/CarlaEngineInternal.cpp.o \ | |||
$(OBJDIR)/CarlaEngineNative.cpp.o \ | |||
@@ -199,6 +200,7 @@ OBJS_arch = \ | |||
$(OBJDIR)/CarlaEngine.cpp.arch.o \ | |||
$(OBJDIR)/CarlaEngineClient.cpp.arch.o \ | |||
$(OBJDIR)/CarlaEngineData.cpp.arch.o \ | |||
$(OBJDIR)/CarlaEngineDummy.cpp.arch.o \ | |||
$(OBJDIR)/CarlaEngineInternal.cpp.arch.o \ | |||
$(OBJDIR)/CarlaEnginePorts.cpp.arch.o \ | |||
$(OBJDIR)/CarlaEngineThread.cpp.arch.o \ | |||
@@ -907,7 +907,7 @@ class HostWindow(QMainWindow): | |||
self.ui.act_canvas_show_internal.blockSignals(True) | |||
self.ui.act_canvas_show_external.blockSignals(True) | |||
if processMode == ENGINE_PROCESS_MODE_PATCHBAY and not self.host.isPlugin: | |||
if processMode == ENGINE_PROCESS_MODE_PATCHBAY: # and not self.host.isPlugin: | |||
self.ui.act_canvas_show_internal.setChecked(True) | |||
self.ui.act_canvas_show_internal.setVisible(True) | |||
self.ui.act_canvas_show_external.setChecked(False) | |||
@@ -183,6 +183,10 @@ | |||
#define CARLA_SAFE_ASSERT_INT2_CONTINUE(cond, v1, v2) if (! (cond)) { carla_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast<int>(v1), static_cast<int>(v2)); continue; } | |||
#define CARLA_SAFE_ASSERT_INT2_RETURN(cond, v1, v2, ret) if (! (cond)) { carla_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast<int>(v1), static_cast<int>(v2)); return ret; } | |||
#define CARLA_SAFE_ASSERT_UINT_BREAK(cond, value) if (! (cond)) { carla_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast<uint>(value); break; } | |||
#define CARLA_SAFE_ASSERT_UINT_CONTINUE(cond, value) if (! (cond)) { carla_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast<uint>(value)); continue; } | |||
#define CARLA_SAFE_ASSERT_UINT_RETURN(cond, value, ret) if (! (cond)) { carla_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast<uint>(value)); return ret; } | |||
#define CARLA_SAFE_ASSERT_UINT2_BREAK(cond, v1, v2) if (! (cond)) { carla_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast<uint>(v1), static_cast<uint>(v2)); break; } | |||
#define CARLA_SAFE_ASSERT_UINT2_CONTINUE(cond, v1, v2) if (! (cond)) { carla_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast<uint>(v1), static_cast<uint>(v2)); continue; } | |||
#define CARLA_SAFE_ASSERT_UINT2_RETURN(cond, v1, v2, ret) if (! (cond)) { carla_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast<uint>(v1), static_cast<uint>(v2)); return ret; } | |||
@@ -50,6 +50,8 @@ const char* EngineType2Str(const EngineType type) noexcept | |||
return "kEngineTypePlugin"; | |||
case kEngineTypeBridge: | |||
return "kEngineTypeBridge"; | |||
case kEngineTypeDummy: | |||
return "kEngineTypeDummy"; | |||
} | |||
carla_stderr("CarlaBackend::EngineType2Str(%i) - invalid type", type); | |||
@@ -35,7 +35,7 @@ protected: | |||
/* | |||
* Constructor. | |||
*/ | |||
CarlaThread(const char* const threadName = nullptr) noexcept | |||
CarlaThread(const char* const threadName) noexcept | |||
: fLock(), | |||
fSignal(), | |||
fName(threadName), | |||
@@ -301,7 +301,8 @@ private: | |||
*/ | |||
void _runEntryPoint() noexcept | |||
{ | |||
setCurrentThreadName(fName); | |||
if (fName.isNotEmpty()) | |||
setCurrentThreadName(fName); | |||
// report ready | |||
fSignal.signal(); | |||