Browse Source

Add a real Dummy engine driver, usable with carla-single too

via special env var CARLA_BRIDGE_DUMMY

Signed-off-by: falkTX <falktx@falktx.com>
tags/v2.1-rc1
falkTX 5 years ago
parent
commit
d427e92710
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
12 changed files with 348 additions and 11 deletions
  1. +9
    -1
      source/backend/CarlaEngine.hpp
  2. +14
    -2
      source/backend/CarlaStandalone.cpp
  3. +3
    -2
      source/backend/engine/CarlaEngine.cpp
  4. +287
    -0
      source/backend/engine/CarlaEngineDummy.cpp
  5. +1
    -1
      source/backend/engine/CarlaEngineRtAudio.cpp
  6. +1
    -0
      source/backend/engine/Makefile
  7. +21
    -2
      source/bridges-plugin/CarlaBridgePlugin.cpp
  8. +2
    -0
      source/bridges-plugin/Makefile
  9. +1
    -1
      source/frontend/carla_host.py
  10. +4
    -0
      source/includes/CarlaDefines.h
  11. +2
    -0
      source/utils/CarlaEngineUtils.hpp
  12. +3
    -2
      source/utils/CarlaThread.hpp

+ 9
- 1
source/backend/CarlaEngine.hpp View File

@@ -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();



+ 14
- 2
source/backend/CarlaStandalone.cpp View File

@@ -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);


+ 3
- 2
source/backend/engine/CarlaEngine.cpp View File

@@ -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)


+ 287
- 0
source/backend/engine/CarlaEngineDummy.cpp View File

@@ -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

+ 1
- 1
source/backend/engine/CarlaEngineRtAudio.cpp View File

@@ -1138,7 +1138,7 @@ CarlaEngine* CarlaEngine::newRtAudio(const AudioApi api)
{
initRtAudioAPIsIfNeeded();

RtAudio::Api rtApi(RtAudio::UNSPECIFIED);
RtAudio::Api rtApi = RtAudio::UNSPECIFIED;

switch (api)
{


+ 1
- 0
source/backend/engine/Makefile View File

@@ -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 \


+ 21
- 2
source/bridges-plugin/CarlaBridgePlugin.cpp View File

@@ -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);
}


+ 2
- 0
source/bridges-plugin/Makefile View File

@@ -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 \


+ 1
- 1
source/frontend/carla_host.py View File

@@ -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)


+ 4
- 0
source/includes/CarlaDefines.h View File

@@ -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; }


+ 2
- 0
source/utils/CarlaEngineUtils.hpp View File

@@ -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);


+ 3
- 2
source/utils/CarlaThread.hpp View File

@@ -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();


Loading…
Cancel
Save