Browse Source

Started work on proper plugin-bridge support

tags/1.9.4
falkTX 13 years ago
parent
commit
6970cb280e
11 changed files with 475 additions and 53 deletions
  1. +82
    -0
      source/backend/CarlaBridge.hpp
  2. +9
    -1
      source/backend/CarlaEngine.hpp
  3. +0
    -28
      source/backend/CarlaPlugin.hpp
  4. +7
    -0
      source/backend/engine/CarlaEngine.cpp
  5. +2
    -0
      source/backend/engine/CarlaEngine.pro
  6. +183
    -0
      source/backend/engine/CarlaEngineBridge.cpp
  7. +12
    -0
      source/backend/engine/CarlaEngineInternal.hpp
  8. +4
    -0
      source/backend/engine/CarlaEngineOsc.cpp
  9. +1
    -0
      source/backend/engine/Makefile
  10. +167
    -18
      source/backend/plugin/BridgePlugin.cpp
  11. +8
    -6
      source/utils/CarlaShmUtils.hpp

+ 82
- 0
source/backend/CarlaBridge.hpp View File

@@ -0,0 +1,82 @@
/*
* Carla Bridge API
* Copyright (C) 2013 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
*/

#ifndef __CARLA_BRIDGE_HPP__
#define __CARLA_BRIDGE_HPP__

#include <semaphore.h>

#define BRIDGE_SHM_RING_BUFFER_SIZE 2048

#ifndef BUILD_BRIDGE
/*!
* TODO.
*/
enum PluginBridgeInfoType {
kPluginBridgeAudioCount,
kPluginBridgeMidiCount,
kPluginBridgeParameterCount,
kPluginBridgeProgramCount,
kPluginBridgeMidiProgramCount,
kPluginBridgePluginInfo,
kPluginBridgeParameterInfo,
kPluginBridgeParameterData,
kPluginBridgeParameterRanges,
kPluginBridgeProgramInfo,
kPluginBridgeMidiProgramInfo,
kPluginBridgeConfigure,
kPluginBridgeSetParameterValue,
kPluginBridgeSetDefaultValue,
kPluginBridgeSetProgram,
kPluginBridgeSetMidiProgram,
kPluginBridgeSetCustomData,
kPluginBridgeSetChunkData,
kPluginBridgeUpdateNow,
kPluginBridgeError
};
#endif

/*!
* TODO.
*/
struct BridgeRingBuffer {
int head;
int tail;
int written;
bool invalidateCommit;
char buf[BRIDGE_SHM_RING_BUFFER_SIZE];
};

/*!
* TODO.
*/
struct BridgeShmControl {
// 32 and 64-bit binaries align semaphores differently.
// Let's make sure there's plenty of room for either one.
union {
sem_t runServer;
char _alignServer[32];
};
union {
sem_t runClient;
char _alignClient[32];
};

BridgeRingBuffer ringBuffer;
};

#endif // __CARLA_BRIDGE_HPP__

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

@@ -58,7 +58,12 @@ enum EngineType {
/*! /*!
* Plugin engine type, used to export the engine as a plugin. * Plugin engine type, used to export the engine as a plugin.
*/ */
kEngineTypePlugin = 3
kEngineTypePlugin = 3,

/*!
* TODO.
*/
kEngineTypeBridge = 4
}; };


/*! /*!
@@ -1008,6 +1013,9 @@ protected:
#endif #endif


private: private:
#ifdef BUILD_BRIDGE
static CarlaEngine* newBridge(const char* const audioBaseName, const char* const controlBaseName);
#endif
#ifdef WANT_JACK #ifdef WANT_JACK
static CarlaEngine* newJack(); static CarlaEngine* newJack();
#endif #endif


+ 0
- 28
source/backend/CarlaPlugin.hpp View File

@@ -34,34 +34,6 @@ CARLA_BACKEND_START_NAMESPACE
} // Fix editor indentation } // Fix editor indentation
#endif #endif


#if 1//ndef BUILD_BRIDGE
/*!
* TODO.
*/
enum PluginBridgeInfoType {
kPluginBridgeAudioCount,
kPluginBridgeMidiCount,
kPluginBridgeParameterCount,
kPluginBridgeProgramCount,
kPluginBridgeMidiProgramCount,
kPluginBridgePluginInfo,
kPluginBridgeParameterInfo,
kPluginBridgeParameterData,
kPluginBridgeParameterRanges,
kPluginBridgeProgramInfo,
kPluginBridgeMidiProgramInfo,
kPluginBridgeConfigure,
kPluginBridgeSetParameterValue,
kPluginBridgeSetDefaultValue,
kPluginBridgeSetProgram,
kPluginBridgeSetMidiProgram,
kPluginBridgeSetCustomData,
kPluginBridgeSetChunkData,
kPluginBridgeUpdateNow,
kPluginBridgeError
};
#endif

/*! /*!
* TODO. * TODO.
*/ */


+ 7
- 0
source/backend/engine/CarlaEngine.cpp View File

@@ -35,6 +35,13 @@ CARLA_BACKEND_START_NAMESPACE
return CarlaEngineProtectedData::getHostWindow(engine); return CarlaEngineProtectedData::getHostWindow(engine);
} }


#ifndef BUILD_BRIDGE
void registerEnginePlugin(CarlaEngine* const engine, const unsigned int id, CarlaPlugin* const plugin)
{
CarlaEngineProtectedData::registerEnginePlugin(engine, id, plugin);
}
#endif

// ------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------
// Carla Engine port (Abstract) // Carla Engine port (Abstract)




+ 2
- 0
source/backend/engine/CarlaEngine.pro View File

@@ -61,6 +61,7 @@ SOURCES = \
CarlaEngine.cpp \ CarlaEngine.cpp \
CarlaEngineOsc.cpp \ CarlaEngineOsc.cpp \
CarlaEngineThread.cpp \ CarlaEngineThread.cpp \
CarlaEngineBridge.cpp \
CarlaEngineJack.cpp \ CarlaEngineJack.cpp \
CarlaEnginePlugin.cpp \ CarlaEnginePlugin.cpp \
CarlaEngineRtAudio.cpp CarlaEngineRtAudio.cpp
@@ -72,6 +73,7 @@ HEADERS = \


HEADERS += \ HEADERS += \
../CarlaBackend.hpp \ ../CarlaBackend.hpp \
../CarlaBridge.hpp \
../CarlaEngine.hpp \ ../CarlaEngine.hpp \
../CarlaPlugin.hpp ../CarlaPlugin.hpp




+ 183
- 0
source/backend/engine/CarlaEngineBridge.cpp View File

@@ -0,0 +1,183 @@
/*
* Carla Plugin Engine
* Copyright (C) 2012-2013 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
*/

#ifdef BUILD_BRIDGE

#include "CarlaEngineInternal.hpp"
#include "CarlaBackendUtils.hpp"
#include "CarlaMIDI.h"

#include "CarlaBridge.hpp"
#include "CarlaShmUtils.hpp"

CARLA_BACKEND_START_NAMESPACE

#if 0
} // Fix editor indentation
#endif

// -----------------------------------------

class CarlaEngineBridge : public CarlaEngine
{
public:
CarlaEngineBridge(const char* const audioBaseName, const char* const controlBaseName)
: CarlaEngine()
{
carla_debug("CarlaEngineBridge::CarlaEngineBridge()");

fShmAudioPool.filename = "/carla-bridge_shm_" + audioBaseName;
fShmControl.filename = "/carla-bridge_shc_" + controlBaseName;
}

~CarlaEngineBridge()
{
carla_debug("CarlaEngineBridge::~CarlaEngineBridge()");
}

// -------------------------------------
// CarlaEngine virtual calls

bool init(const char* const clientName)
{
carla_debug("CarlaEngineBridge::init(\"%s\")", clientName);

char tmpFileBase[60];

// SHM Audio Pool
{
fShmAudioPool.shm = carla_shm_attach((const char*)fShmAudioPool.filename);

if (! carla_is_shm_valid(fShmAudioPool.shm))
{
_cleanup();
carla_stdout("Failed to open or create shared memory file #1");
return false;
}
}

// SHM Control
{
fShmControl.shm = carla_shm_attach((const char*)fShmControl.filename);

if (! carla_is_shm_valid(fShmControl.shm))
{
_cleanup();
carla_stdout("Failed to open or create shared memory file #2");
return false;
}

if (! carla_shm_map<ShmControl>(fShmControl.shm, fShmControl.data))
{
_cleanup();
carla_stdout("Failed to mmap shared memory file");
return false;
}
}

CarlaEngine::init(fName);
return true;
}

bool close()
{
carla_debug("CarlaEnginePlugin::close()");
CarlaEngine::close();

_cleanup();

return true;
}

bool isRunning() const
{
return true;
}

bool isOffline() const
{
return false;
}

EngineType type() const
{
return kEngineTypeBridge;
}

private:
struct BridgeAudioPool {
CarlaString filename;
float* data;
size_t size;
shm_t shm;

BridgeAudioPool()
: data(nullptr),
size(0)
{
carla_shm_init(shm);
}
} fShmAudioPool;

struct BridgeControl {
CarlaString filename;
ShmControl* data;
shm_t shm;

BridgeControl()
: data(nullptr)
{
carla_shm_init(shm);
}

} fShmControl;

void _cleanup()
{
if (fShmAudioPool.filename.isNotEmpty())
fShmAudioPool.filename.clear();

if (fShmControl.filename.isNotEmpty())
fShmControl.filename.clear();

// delete data
fShmAudioPool.data = nullptr;
fShmAudioPool.size = 0;

// and again
fShmControl.data = nullptr;

if (carla_is_shm_valid(fShmAudioPool.shm))
carla_shm_close(fShmAudioPool.shm);

if (carla_is_shm_valid(fShmControl.shm))
carla_shm_close(fShmControl.shm);
}

CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineBridge)
};

// -----------------------------------------

CarlaEngine* CarlaEngine::newBridge(const char* const audioBaseName, const char* const controlBaseName)
{
return new CarlaEngineBridge(audioBaseName, controlBaseName);
}

CARLA_BACKEND_END_NAMESPACE

#endif // BUILD_BRIDGE

+ 12
- 0
source/backend/engine/CarlaEngineInternal.hpp View File

@@ -47,6 +47,8 @@ const char* EngineType2Str(const EngineType type)
return "kEngineTypeRtAudio"; return "kEngineTypeRtAudio";
case kEngineTypePlugin: case kEngineTypePlugin:
return "kEngineTypePlugin"; return "kEngineTypePlugin";
case kEngineTypeBridge:
return "kEngineTypeBridge";
} }


carla_stderr("CarlaBackend::EngineType2Str(%i) - invalid type", type); carla_stderr("CarlaBackend::EngineType2Str(%i) - invalid type", type);
@@ -204,6 +206,16 @@ struct CarlaEngineProtectedData {
(void)engine; (void)engine;
#endif #endif
} }

#ifndef BUILD_BRIDGE
static void registerEnginePlugin(CarlaEngine* const engine, const unsigned int id, CarlaPlugin* const plugin)
{
CARLA_ASSERT(id < engine->kData->curPluginCount);

if (id < engine->kData->curPluginCount)
engine->kData->plugins[id].plugin = plugin;
}
#endif
}; };


CARLA_BACKEND_END_NAMESPACE CARLA_BACKEND_END_NAMESPACE


+ 4
- 0
source/backend/engine/CarlaEngineOsc.cpp View File

@@ -21,6 +21,10 @@
#include "CarlaPlugin.hpp" #include "CarlaPlugin.hpp"
#include "CarlaMIDI.h" #include "CarlaMIDI.h"


#ifndef BUILD_BRIDGE
# include "CarlaBridge.hpp"
#endif

CARLA_BACKEND_START_NAMESPACE CARLA_BACKEND_START_NAMESPACE


#ifndef BUILD_BRIDGE #ifndef BUILD_BRIDGE


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

@@ -42,6 +42,7 @@ endif


OBJS = \ OBJS = \
CarlaEngine.cpp.o \ CarlaEngine.cpp.o \
CarlaEngineBridge.cpp.o \
CarlaEngineJack.cpp.o \ CarlaEngineJack.cpp.o \
CarlaEnginePlugin.cpp.o \ CarlaEnginePlugin.cpp.o \
CarlaEngineRtAudio.cpp.o \ CarlaEngineRtAudio.cpp.o \


+ 167
- 18
source/backend/plugin/BridgePlugin.cpp View File

@@ -17,8 +17,13 @@


#include "CarlaPluginInternal.hpp" #include "CarlaPluginInternal.hpp"


#if 1//ndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE

#include "CarlaBridge.hpp"
#include "CarlaShmUtils.hpp"


#include <cerrno>
#include <ctime>
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtCore/QFile> #include <QtCore/QFile>
#include <QtCore/QStringList> #include <QtCore/QStringList>
@@ -49,6 +54,55 @@


CARLA_BACKEND_START_NAMESPACE CARLA_BACKEND_START_NAMESPACE


// -------------------------------------------------------------------------------------------------------------------
// Engine Helpers

extern void registerEnginePlugin(CarlaEngine* const engine, const unsigned int id, CarlaPlugin* const plugin);

// -------------------------------------------------------------------------------------------------------------------

shm_t shm_mkstemp(char* const fileBase)
{
static const char charSet[] = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";

const int size = (fileBase != nullptr) ? std::strlen(fileBase) : 0;

shm_t shm;
carla_shm_init(shm);

if (size < 6)
{
errno = EINVAL;
return shm;
}

if (std::strcmp(fileBase + size - 6, "XXXXXX") != 0)
{
errno = EINVAL;
return shm;
}

std::srand(std::time(NULL));

while (true)
{
for (int c = size - 6; c < size; c++)
{
// Note the -1 to avoid the trailing '\0' in charSet.
fileBase[c] = charSet[std::rand() % (sizeof(charSet) - 1)];
}

shm_t shm = carla_shm_create(fileBase);

if (carla_is_shm_valid(shm) || errno != EEXIST)
return shm;
}
}

// -------------------------------------------------------------------------------------------------------------------

struct BridgeParamInfo { struct BridgeParamInfo {
float value; float value;
CarlaString name; CarlaString name;
@@ -75,7 +129,6 @@ public:
kData->osc.thread.setMode(CarlaPluginThread::PLUGIN_THREAD_BRIDGE); kData->osc.thread.setMode(CarlaPluginThread::PLUGIN_THREAD_BRIDGE);
} }


#if 0
~BridgePlugin() ~BridgePlugin()
{ {
carla_debug("BridgePlugin::~BridgePlugin()"); carla_debug("BridgePlugin::~BridgePlugin()");
@@ -83,6 +136,7 @@ public:
kData->singleMutex.lock(); kData->singleMutex.lock();
kData->masterMutex.lock(); kData->masterMutex.lock();


#if 0
if (osc.data.target) if (osc.data.target)
{ {
osc_send_hide(&osc.data); osc_send_hide(&osc.data);
@@ -103,8 +157,8 @@ public:
} }


info.chunk.clear(); info.chunk.clear();
}
#endif #endif
}


// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Information (base) // Information (base)
@@ -909,11 +963,16 @@ public:


carla_debug("BridgePlugin::delete_buffers() - end"); carla_debug("BridgePlugin::delete_buffers() - end");
} }
#endif


// ------------------------------------------------------------------- // -------------------------------------------------------------------


bool init(const char* const filename, const char* const name, const char* const label, const char* const bridgeBinary) bool init(const char* const filename, const char* const name, const char* const label, const char* const bridgeBinary)
{ {
CARLA_ASSERT(kData->engine != nullptr);
CARLA_ASSERT(kData->client == nullptr);
CARLA_ASSERT(filename != nullptr);

// --------------------------------------------------------------- // ---------------------------------------------------------------
// first checks // first checks


@@ -934,46 +993,109 @@ public:
return false; return false;
} }


m_filename = strdup(filename);
// ---------------------------------------------------------------
// set info

if (name != nullptr)
fName = kData->engine->getNewUniquePluginName(name);

fFilename = filename;

// ---------------------------------------------------------------
// SHM Audio Pool
{
char tmpFileBase[60];


if (name)
m_name = x_engine->getUniquePluginName(name);
std::sprintf(tmpFileBase, "/carla-bridge_shm_XXXXXX");

fShmAudioPool.shm = shm_mkstemp(tmpFileBase);

if (! carla_is_shm_valid(fShmAudioPool.shm))
{
//_cleanup();
carla_stdout("Failed to open or create shared memory file #1");
return false;
}

fShmAudioPool.filename = tmpFileBase;
}

// ---------------------------------------------------------------
// SHM Control
{
char tmpFileBase[60];
std::sprintf(tmpFileBase, "/carla-bridge_shc_XXXXXX");

fShmControl.shm = shm_mkstemp(tmpFileBase);

if (! carla_is_shm_valid(fShmControl.shm))
{
//_cleanup();
carla_stdout("Failed to open or create shared memory file #2");
return false;
}

fShmControl.filename = tmpFileBase;

if (! carla_shm_map<BridgeShmControl>(fShmControl.shm, fShmControl.data))
{
//_cleanup();
carla_stdout("Failed to mmap shared memory file");
return false;
}

std::memset(fShmControl.data, 0, sizeof(BridgeShmControl));

if (sem_init(&fShmControl.data->runServer, 1, 0) != 0)
{
//_cleanup();
carla_stdout("Failed to initialize shared memory semaphore #1");
return false;
}

if (sem_init(&fShmControl.data->runClient, 1, 0) != 0)
{
//_cleanup();
carla_stdout("Failed to initialize shared memory semaphore #2");
return false;
}
}


// register plugin now so we can receive OSC (and wait for it) // register plugin now so we can receive OSC (and wait for it)
x_engine->__bridgePluginRegister(m_id, this);
registerEnginePlugin(kData->engine, fId, this);


osc.thread->setOscData(bridgeBinary, label, getPluginTypeString(m_type));
osc.thread->start();
kData->osc.thread.setOscData(bridgeBinary, label, getPluginTypeAsString(fPluginType));
kData->osc.thread.start();


for (int i=0; i < 200; i++) for (int i=0; i < 200; i++)
{ {
if (m_initiated || osc.thread->isFinished())
if (fInitiated || ! kData->osc.thread.isRunning())
break; break;
carla_msleep(50); carla_msleep(50);
} }


if (! m_initiated)
if (! fInitiated)
{ {
// unregister so it gets handled properly // unregister so it gets handled properly
x_engine->__bridgePluginRegister(m_id, nullptr);
registerEnginePlugin(kData->engine, fId, nullptr);


osc.thread->terminate();
x_engine->setLastError("Timeout while waiting for a response from plugin-bridge\n(or the plugin crashed on initialization?)");
kData->osc.thread.terminate();
kData->engine->setLastError("Timeout while waiting for a response from plugin-bridge\n(or the plugin crashed on initialization?)");
return false; return false;
} }
else if (m_initError)
else if (fInitError)
{ {
// unregister so it gets handled properly // unregister so it gets handled properly
x_engine->__bridgePluginRegister(m_id, nullptr);
registerEnginePlugin(kData->engine, fId, nullptr);


osc.thread->quit();
kData->osc.thread.stop();
// last error was set before // last error was set before
return false; return false;
} }


return true; return true;
} }
#endif


private: private:
const BinaryType fBinaryType; const BinaryType fBinaryType;
@@ -983,6 +1105,33 @@ private:
bool fInitError; bool fInitError;
bool fSaved; bool fSaved;


struct BridgeAudioPool {
CarlaString filename;
float* data;
size_t size;
shm_t shm;

BridgeAudioPool()
: data(nullptr),
size(0)
{
carla_shm_init(shm);
}
} fShmAudioPool;

struct BridgeControl {
CarlaString filename;
BridgeShmControl* data;
shm_t shm;

BridgeControl()
: data(nullptr)
{
carla_shm_init(shm);
}

} fShmControl;

struct Info { struct Info {
uint32_t aIns, aOuts; uint32_t aIns, aOuts;
uint32_t mIns, mOuts; uint32_t mIns, mOuts;


+ 8
- 6
source/utils/CarlaShmUtils.hpp View File

@@ -35,7 +35,7 @@ typedef int shm_t;
// shared memory calls // shared memory calls


static inline static inline
bool carla_is_shm_valid(shm_t shm)
bool carla_is_shm_valid(const shm_t& shm)
{ {
#ifdef CARLA_OS_WIN #ifdef CARLA_OS_WIN
return (shm.shm != nullptr && shm.shm != INVALID_HANDLE_VALUE); return (shm.shm != nullptr && shm.shm != INVALID_HANDLE_VALUE);
@@ -105,7 +105,7 @@ void carla_shm_close(shm_t& shm)
} }


static inline static inline
void* carla_shm_map(shm_t shm, const size_t size)
void* carla_shm_map(shm_t& shm, const size_t size)
{ {
CARLA_ASSERT(carla_is_shm_valid(shm)); CARLA_ASSERT(carla_is_shm_valid(shm));
CARLA_ASSERT(size > 0); CARLA_ASSERT(size > 0);
@@ -129,12 +129,13 @@ void* carla_shm_map(shm_t shm, const size_t size)


return ptr; return ptr;
#else #else
ftruncate(shm, size);
return mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0); return mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0);
#endif #endif
} }


static inline static inline
void carla_shm_unmap(const shm_t shm, void* const ptr, const size_t size)
void carla_shm_unmap(const shm_t& shm, void* const ptr, const size_t size)
{ {
CARLA_ASSERT(carla_is_shm_valid(shm)); CARLA_ASSERT(carla_is_shm_valid(shm));
CARLA_ASSERT(ptr != nullptr); CARLA_ASSERT(ptr != nullptr);
@@ -177,14 +178,15 @@ void carla_shm_unmap(const shm_t shm, void* const ptr, const size_t size)


template<typename T> template<typename T>
static inline static inline
void carla_shm_map(shm_t shm, T* value)
bool carla_shm_map(shm_t& shm, T* value)
{ {
value = carla_shm_map(shm, sizeof(value));
value = (T*)carla_shm_map(shm, sizeof(value));
return (value != nullptr);
} }


template<typename T> template<typename T>
static inline static inline
void carla_shm_unmap(const shm_t shm, T* value)
void carla_shm_unmap(const shm_t& shm, T* value)
{ {
carla_shm_unmap(shm, value, sizeof(value)); carla_shm_unmap(shm, value, sizeof(value));
value = nullptr; value = nullptr;


Loading…
Cancel
Save