From 467ec8a84d6f46f4ac2de8581c841717a0ec8e89 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 26 Mar 2013 06:28:17 +0000 Subject: [PATCH] More work for plugin-bridge support --- source/backend/CarlaBackend.hpp | 5 +- source/backend/CarlaBridge.hpp | 154 +++++- source/backend/CarlaEngine.hpp | 14 +- source/backend/CarlaPlugin.hpp | 2 +- source/backend/CarlaStandalone.hpp | 5 + source/backend/engine/CarlaEngine.cpp | 76 +-- source/backend/engine/CarlaEngineBridge.cpp | 170 +++++- source/backend/engine/CarlaEngineInternal.hpp | 4 +- source/backend/engine/CarlaEnginePlugin.cpp | 5 +- source/backend/engine/CarlaEngineThread.cpp | 6 +- source/backend/plugin/BridgePlugin.cpp | 485 +++++++++++++----- source/backend/plugin/CarlaPluginThread.cpp | 23 +- source/backend/plugin/CarlaPluginThread.hpp | 5 +- source/backend/standalone/CarlaStandalone.cpp | 83 ++- source/bridges/CarlaBridgeClient.hpp | 2 + source/bridges/CarlaBridgePlugin.cpp | 43 +- source/bridges/Makefile | 17 +- .../bridges/qtcreator/carla-bridge-plugin.pro | 4 +- source/carla_shared.py | 1 + source/libs/jackbridge/Makefile | 2 +- source/libs/jackbridge/jackbridge.c | 32 ++ source/libs/jackbridge/jackbridge.h | 9 + source/utils/CarlaShmUtils.hpp | 51 +- source/utils/CarlaThread.hpp | 2 +- 24 files changed, 892 insertions(+), 308 deletions(-) diff --git a/source/backend/CarlaBackend.hpp b/source/backend/CarlaBackend.hpp index d2037cf21..880d7f459 100644 --- a/source/backend/CarlaBackend.hpp +++ b/source/backend/CarlaBackend.hpp @@ -619,7 +619,7 @@ enum ProcessMode { PROCESS_MODE_MULTIPLE_CLIENTS = 1, //!< Multiple client mode (1 master client + 1 client per plugin) PROCESS_MODE_CONTINUOUS_RACK = 2, //!< Single client, 'rack' mode. Processes plugins in order of id, with forced stereo. PROCESS_MODE_PATCHBAY = 3, //!< Single client, 'patchbay' mode. - PROCESS_MODE_BRIDGE = 4 //!< Special mode, used in plugin-bridges only. RT buffers come from shared memory in a separate host app. + PROCESS_MODE_BRIDGE = 4 //!< Special mode, used in plugin-bridges only. }; /*! @@ -627,7 +627,8 @@ enum ProcessMode { */ enum TransportMode { TRANSPORT_MODE_INTERNAL = 0, //!< Internal transport mode. - TRANSPORT_MODE_JACK = 1 //!< JACK transport, only available if driver name is "JACK" + TRANSPORT_MODE_JACK = 1, //!< JACK transport, only available if driver name is "JACK" + TRANSPORT_MODE_BRIDGE = 2 //!< Special mode, used in plugin-bridges only. }; /*! diff --git a/source/backend/CarlaBridge.hpp b/source/backend/CarlaBridge.hpp index 297674164..449c44891 100644 --- a/source/backend/CarlaBridge.hpp +++ b/source/backend/CarlaBridge.hpp @@ -18,6 +18,8 @@ #ifndef __CARLA_BRIDGE_HPP__ #define __CARLA_BRIDGE_HPP__ +#include "CarlaUtils.hpp" + #include #define BRIDGE_SHM_RING_BUFFER_SIZE 2048 @@ -50,6 +52,14 @@ enum PluginBridgeInfoType { }; #endif +enum PluginBridgeOpcode { + kPluginBridgeOpcodeNull = 0, + kPluginBridgeOpcodeReadyWait = 1, + kPluginBridgeOpcodeBufferSize = 2, + kPluginBridgeOpcodeSampleRate = 3, + kPluginBridgeOpcodeProcess = 4 +}; + /*! * TODO. */ @@ -69,14 +79,154 @@ struct BridgeShmControl { // Let's make sure there's plenty of room for either one. union { sem_t runServer; - char _alignServer[32]; + char _alignServer[64]; }; union { sem_t runClient; - char _alignClient[32]; + char _alignClient[64]; }; BridgeRingBuffer ringBuffer; }; +// --------------------------------------------------------------------------------------------- + +static inline +void rdwr_tryRead(BridgeRingBuffer* const ringbuf, void* const buf, const size_t size) +{ + char* const charbuf = static_cast(buf); + + size_t tail = ringbuf->tail; + size_t head = ringbuf->head; + size_t wrap = 0; + + if (head <= tail) { + wrap = BRIDGE_SHM_RING_BUFFER_SIZE; + } + if (head - tail + wrap < size) { + return; + } + + size_t readto = tail + size; + + if (readto >= BRIDGE_SHM_RING_BUFFER_SIZE) + { + readto -= BRIDGE_SHM_RING_BUFFER_SIZE; + size_t firstpart = BRIDGE_SHM_RING_BUFFER_SIZE - tail; + std::memcpy(charbuf, ringbuf->buf + tail, firstpart); + std::memcpy(charbuf + firstpart, ringbuf->buf, readto); + } + else + { + std::memcpy(charbuf, ringbuf->buf + tail, size); + } + + ringbuf->tail = readto; +} + +static inline +void rdwr_tryWrite(BridgeRingBuffer* const ringbuf, const void* const buf, const size_t size) +{ + const char* const charbuf = static_cast(buf); + + size_t written = ringbuf->written; + size_t tail = ringbuf->tail; + size_t wrap = 0; + + if (tail <= written) + { + wrap = BRIDGE_SHM_RING_BUFFER_SIZE; + } + if (tail - written + wrap < size) + { + carla_stderr2("Operation ring buffer full! Dropping events."); + ringbuf->invalidateCommit = true; + return; + } + + size_t writeto = written + size; + + if (writeto >= BRIDGE_SHM_RING_BUFFER_SIZE) + { + writeto -= BRIDGE_SHM_RING_BUFFER_SIZE; + size_t firstpart = BRIDGE_SHM_RING_BUFFER_SIZE - written; + std::memcpy(ringbuf->buf + written, charbuf, firstpart); + std::memcpy(ringbuf->buf, charbuf + firstpart, writeto); + } + else + { + std::memcpy(ringbuf->buf + written, charbuf, size); + } + + ringbuf->written = writeto; +} + +static inline +void rdwr_commitWrite(BridgeRingBuffer* const ringbuf) +{ + if (ringbuf->invalidateCommit) + { + ringbuf->written = ringbuf->head; + ringbuf->invalidateCommit = false; + } + else + { + ringbuf->head = ringbuf->written; + } +} + +// --------------------------------------------------------------------------------------------- + +static inline +bool rdwr_dataAvailable(BridgeRingBuffer* const ringbuf) +{ + return (ringbuf->tail != ringbuf->head); +} + +static inline +PluginBridgeOpcode rdwr_readOpcode(BridgeRingBuffer* const ringbuf) +{ + PluginBridgeOpcode code = kPluginBridgeOpcodeNull; + rdwr_tryRead(ringbuf, &code, sizeof(PluginBridgeOpcode)); + return code; +} + +static inline +int rdwr_readInt(BridgeRingBuffer* const ringbuf) +{ + int i = 0; + rdwr_tryRead(ringbuf, &i, sizeof(int)); + return i; +} + +static inline +float rdwr_readFloat(BridgeRingBuffer* const ringbuf) +{ + float f = 0.0f; + rdwr_tryRead(ringbuf, &f, sizeof(float)); + return f; +} + +// --------------------------------------------------------------------------------------------- + +static inline +void rdwr_writeOpcode(BridgeRingBuffer* const ringbuf, const PluginBridgeOpcode opcode) +{ + rdwr_tryWrite(ringbuf, &opcode, sizeof(PluginBridgeOpcode)); +} + +static inline +void rdwr_writeInt(BridgeRingBuffer* const ringbuf, const int value) +{ + rdwr_tryWrite(ringbuf, &value, sizeof(int)); +} + +static inline +void rdwr_writeFloat(BridgeRingBuffer* const ringbuf, const float value) +{ + rdwr_tryWrite(ringbuf, &value, sizeof(float)); +} + +// --------------------------------------------------------------------------------------------- + #endif // __CARLA_BRIDGE_HPP__ diff --git a/source/backend/CarlaEngine.hpp b/source/backend/CarlaEngine.hpp index 17fd19dd4..a9d5839fc 100644 --- a/source/backend/CarlaEngine.hpp +++ b/source/backend/CarlaEngine.hpp @@ -23,8 +23,6 @@ #ifdef BUILD_BRIDGE struct CarlaOscData; -#else -class QProcessEnvironment; #endif CARLA_BACKEND_START_NAMESPACE @@ -913,20 +911,13 @@ public: */ void setAboutToClose(); -#ifndef BUILD_BRIDGE // ------------------------------------------------------------------- // Options - /*! - * Get the engine options as process environment. - */ - const QProcessEnvironment& getOptionsAsProcessEnvironment() const; - /*! * Set the engine option \a option. */ void setOption(const OptionsType option, const int value, const char* const valueStr); -#endif // ------------------------------------------------------------------- // OSC Stuff @@ -1013,9 +1004,6 @@ protected: #endif private: -#ifdef BUILD_BRIDGE - static CarlaEngine* newBridge(const char* const audioBaseName, const char* const controlBaseName); -#endif #ifdef WANT_JACK static CarlaEngine* newJack(); #endif @@ -1038,6 +1026,8 @@ private: public: #ifdef BUILD_BRIDGE + static CarlaEngine* newBridge(const char* const audioBaseName, const char* const controlBaseName); + void osc_send_bridge_audio_count(const int32_t ins, const int32_t outs, const int32_t total); void osc_send_bridge_midi_count(const int32_t ins, const int32_t outs, const int32_t total); void osc_send_bridge_parameter_count(const int32_t ins, const int32_t outs, const int32_t total); diff --git a/source/backend/CarlaPlugin.hpp b/source/backend/CarlaPlugin.hpp index d908ad2f2..012854e78 100644 --- a/source/backend/CarlaPlugin.hpp +++ b/source/backend/CarlaPlugin.hpp @@ -804,7 +804,7 @@ public: static const PluginDescriptor* getNativePluginDescriptor(const size_t index); static CarlaPlugin* newNative(const Initializer& init); - static CarlaPlugin* newBridge(const Initializer& init, const BinaryType btype, const PluginType ptype, const char* const bridgeFilename); + static CarlaPlugin* newBridge(const Initializer& init, const BinaryType btype, const PluginType ptype, const char* const bridgeBinary); static CarlaPlugin* newLADSPA(const Initializer& init, const LADSPA_RDF_Descriptor* const rdfDescriptor); static CarlaPlugin* newDSSI(const Initializer& init, const char* const guiFilename); diff --git a/source/backend/CarlaStandalone.hpp b/source/backend/CarlaStandalone.hpp index 687b6ef65..75ed3ed38 100644 --- a/source/backend/CarlaStandalone.hpp +++ b/source/backend/CarlaStandalone.hpp @@ -250,6 +250,11 @@ CARLA_EXPORT void carla_nsm_reply_open(); CARLA_EXPORT void carla_nsm_reply_save(); #endif +#ifdef BUILD_BRIDGE +CARLA_EXPORT bool carla_engine_init_bridge(const char* audioBaseName, const char* controlBaseName, const char* clientName); +CARLA_EXPORT CarlaBackend::CarlaEngine* carla_get_standalone_engine(); +#endif + /**@}*/ #endif // __CARLA_STANDALONE_HPP__ diff --git a/source/backend/engine/CarlaEngine.cpp b/source/backend/engine/CarlaEngine.cpp index f10f43bd0..9ec65b5da 100644 --- a/source/backend/engine/CarlaEngine.cpp +++ b/source/backend/engine/CarlaEngine.cpp @@ -101,7 +101,7 @@ CarlaEngineEventPort::CarlaEngineEventPort(const bool isInput, const ProcessMode { carla_debug("CarlaEngineEventPort::CarlaEngineEventPort(%s, %s)", bool2str(isInput), ProcessMode2Str(processMode)); - if (kProcessMode == PROCESS_MODE_PATCHBAY) + if (kProcessMode == PROCESS_MODE_PATCHBAY || kProcessMode == PROCESS_MODE_BRIDGE) fBuffer = new EngineEvent[PATCHBAY_EVENT_COUNT]; } @@ -109,7 +109,7 @@ CarlaEngineEventPort::~CarlaEngineEventPort() { carla_debug("CarlaEngineEventPort::~CarlaEngineEventPort()"); - if (kProcessMode == PROCESS_MODE_PATCHBAY) + if (kProcessMode == PROCESS_MODE_PATCHBAY || kProcessMode == PROCESS_MODE_BRIDGE) { CARLA_ASSERT(fBuffer != nullptr); @@ -128,22 +128,23 @@ void CarlaEngineEventPort::initBuffer(CarlaEngine* const engine) #ifndef BUILD_BRIDGE if (kProcessMode == PROCESS_MODE_CONTINUOUS_RACK) fBuffer = engine->getRackEventBuffer(kIsInput); - else if (kProcessMode == PROCESS_MODE_PATCHBAY && ! kIsInput) - carla_zeroMem(fBuffer, sizeof(EngineEvent)*PATCHBAY_EVENT_COUNT); + else #endif + if ((kProcessMode == PROCESS_MODE_PATCHBAY || kProcessMode == PROCESS_MODE_BRIDGE) && ! kIsInput) + carla_zeroMem(fBuffer, sizeof(EngineEvent)*PATCHBAY_EVENT_COUNT); } uint32_t CarlaEngineEventPort::getEventCount() { CARLA_ASSERT(kIsInput); CARLA_ASSERT(fBuffer != nullptr); - CARLA_ASSERT(kProcessMode == PROCESS_MODE_CONTINUOUS_RACK || kProcessMode == PROCESS_MODE_PATCHBAY); + CARLA_ASSERT(kProcessMode == PROCESS_MODE_CONTINUOUS_RACK || kProcessMode == PROCESS_MODE_PATCHBAY || kProcessMode == PROCESS_MODE_BRIDGE); if (! kIsInput) return 0; if (fBuffer == nullptr) return 0; - if (kProcessMode != PROCESS_MODE_CONTINUOUS_RACK && kProcessMode != PROCESS_MODE_PATCHBAY) + if (kProcessMode != PROCESS_MODE_CONTINUOUS_RACK && kProcessMode != PROCESS_MODE_PATCHBAY && kProcessMode != PROCESS_MODE_BRIDGE) return 0; uint32_t count = 0; @@ -162,14 +163,14 @@ const EngineEvent& CarlaEngineEventPort::getEvent(const uint32_t index) { CARLA_ASSERT(kIsInput); CARLA_ASSERT(fBuffer != nullptr); - CARLA_ASSERT(kProcessMode == PROCESS_MODE_CONTINUOUS_RACK || kProcessMode == PROCESS_MODE_PATCHBAY); + CARLA_ASSERT(kProcessMode == PROCESS_MODE_CONTINUOUS_RACK || kProcessMode == PROCESS_MODE_PATCHBAY || kProcessMode == PROCESS_MODE_BRIDGE); CARLA_ASSERT(index < kMaxEventCount); if (! kIsInput) return kFallbackEngineEvent; if (fBuffer == nullptr) return kFallbackEngineEvent; - if (kProcessMode != PROCESS_MODE_CONTINUOUS_RACK && kProcessMode != PROCESS_MODE_PATCHBAY) + if (kProcessMode != PROCESS_MODE_CONTINUOUS_RACK && kProcessMode != PROCESS_MODE_PATCHBAY && kProcessMode != PROCESS_MODE_BRIDGE) return kFallbackEngineEvent; if (index >= kMaxEventCount) return kFallbackEngineEvent; @@ -181,7 +182,7 @@ void CarlaEngineEventPort::writeControlEvent(const uint32_t time, const uint8_t { CARLA_ASSERT(! kIsInput); CARLA_ASSERT(fBuffer != nullptr); - CARLA_ASSERT(kProcessMode == PROCESS_MODE_CONTINUOUS_RACK || kProcessMode == PROCESS_MODE_PATCHBAY); + CARLA_ASSERT(kProcessMode == PROCESS_MODE_CONTINUOUS_RACK || kProcessMode == PROCESS_MODE_PATCHBAY || kProcessMode == PROCESS_MODE_BRIDGE); CARLA_ASSERT(type != kEngineControlEventTypeNull); CARLA_ASSERT(channel < MAX_MIDI_CHANNELS); CARLA_SAFE_ASSERT(value >= 0.0 && value <= 1.0); @@ -190,7 +191,7 @@ void CarlaEngineEventPort::writeControlEvent(const uint32_t time, const uint8_t return; if (fBuffer == nullptr) return; - if (kProcessMode != PROCESS_MODE_CONTINUOUS_RACK && kProcessMode != PROCESS_MODE_PATCHBAY) + if (kProcessMode != PROCESS_MODE_CONTINUOUS_RACK && kProcessMode != PROCESS_MODE_PATCHBAY && kProcessMode != PROCESS_MODE_BRIDGE) return; if (type == kEngineControlEventTypeNull) return; @@ -224,7 +225,7 @@ void CarlaEngineEventPort::writeMidiEvent(const uint32_t time, const uint8_t cha { CARLA_ASSERT(! kIsInput); CARLA_ASSERT(fBuffer != nullptr); - CARLA_ASSERT(kProcessMode == PROCESS_MODE_CONTINUOUS_RACK || kProcessMode == PROCESS_MODE_PATCHBAY); + CARLA_ASSERT(kProcessMode == PROCESS_MODE_CONTINUOUS_RACK || kProcessMode == PROCESS_MODE_PATCHBAY || kProcessMode == PROCESS_MODE_BRIDGE); CARLA_ASSERT(channel < MAX_MIDI_CHANNELS); CARLA_ASSERT(data != nullptr); CARLA_ASSERT(size > 0); @@ -233,7 +234,7 @@ void CarlaEngineEventPort::writeMidiEvent(const uint32_t time, const uint8_t cha return; if (fBuffer == nullptr) return; - if (kProcessMode != PROCESS_MODE_CONTINUOUS_RACK && kProcessMode != PROCESS_MODE_PATCHBAY) + if (kProcessMode != PROCESS_MODE_CONTINUOUS_RACK && kProcessMode != PROCESS_MODE_PATCHBAY && kProcessMode != PROCESS_MODE_BRIDGE) return; if (channel >= MAX_MIDI_CHANNELS) return; @@ -641,7 +642,7 @@ bool CarlaEngine::close() void CarlaEngine::idle() { CARLA_ASSERT(kData->plugins != nullptr); - CARLA_ASSERT(isRunning()); + //CARLA_ASSERT(isRunning()); for (unsigned int i=0; i < kData->curPluginCount; i++) { @@ -714,26 +715,10 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype, cons bridgeBinary = (const char*)fOptions.bridge_native; #endif - if (fOptions.preferPluginBridges && bridgeBinary != nullptr) + if (true) + //if (fOptions.preferPluginBridges && bridgeBinary != nullptr) { - // TODO - if (fOptions.processMode != PROCESS_MODE_MULTIPLE_CLIENTS) - { - setLastError("Can only use bridged plugins in JACK Multi-Client mode"); - return -1; - } - - // TODO - if (type() != kEngineTypeJack) - { - setLastError("Can only use bridged plugins with JACK backend"); - return -1; - } - -#if 0 plugin = CarlaPlugin::newBridge(init, btype, ptype, bridgeBinary); -#endif - setLastError("Bridged plugins are not implemented yet"); } else #endif // BUILD_BRIDGE @@ -1207,13 +1192,6 @@ void CarlaEngine::setAboutToClose() // ----------------------------------------------------------------------- // Global options -#ifndef BUILD_BRIDGE - -const QProcessEnvironment& CarlaEngine::getOptionsAsProcessEnvironment() const -{ - return kData->procEnv; -} - #define CARLA_ENGINE_SET_OPTION_RUNNING_CHECK \ if (isRunning()) \ return carla_stderr("CarlaEngine::setOption(%s, %i, \"%s\") - Cannot set this option while engine is running!", OptionsType2Str(option), value, valueStr); @@ -1231,7 +1209,7 @@ void CarlaEngine::setOption(const OptionsType option, const int value, const cha case OPTION_PROCESS_MODE: CARLA_ENGINE_SET_OPTION_RUNNING_CHECK - if (value < PROCESS_MODE_SINGLE_CLIENT || value > PROCESS_MODE_PATCHBAY) + if (value < PROCESS_MODE_SINGLE_CLIENT || value > PROCESS_MODE_BRIDGE) return carla_stderr("CarlaEngine::setOption(%s, %i, \"%s\") - invalid value", OptionsType2Str(option), value, valueStr); fOptions.processMode = static_cast(value); @@ -1240,7 +1218,7 @@ void CarlaEngine::setOption(const OptionsType option, const int value, const cha case OPTION_TRANSPORT_MODE: // FIXME: Always enable JACK transport for now #if 0 - if (value < CarlaBackend::TRANSPORT_MODE_INTERNAL || value > CarlaBackend::TRANSPORT_MODE_JACK) + if (value < CarlaBackend::TRANSPORT_MODE_INTERNAL || value > CarlaBackend::TRANSPORT_MODE_BRIDGE) return carla_stderr2("carla_set_engine_option(OPTION_TRANSPORT_MODE, %i, \"%s\") - invalid value", value, valueStr); fOptions.transportMode = static_cast(value); @@ -1293,6 +1271,7 @@ void CarlaEngine::setOption(const OptionsType option, const int value, const cha fOptions.oscUiTimeout = static_cast(value); break; +#ifndef BUILD_BRIDGE case OPTION_PATH_BRIDGE_NATIVE: fOptions.bridge_native = valueStr; break; @@ -1308,6 +1287,7 @@ void CarlaEngine::setOption(const OptionsType option, const int value, const cha case OPTION_PATH_BRIDGE_WIN64: fOptions.bridge_win64 = valueStr; break; +#endif #ifdef WANT_LV2 case OPTION_PATH_BRIDGE_LV2_GTK2: @@ -1346,7 +1326,6 @@ void CarlaEngine::setOption(const OptionsType option, const int value, const cha #endif } } -#endif // ----------------------------------------------------------------------- // OSC Stuff @@ -2167,21 +2146,6 @@ void CarlaEngine::osc_send_bridge_set_chunk_data(const char* const chunkFile) lo_send(kData->oscData->target, targetPath, "s", chunkFile); } } - -void CarlaEngine::osc_send_bridge_set_peaks() -{ - CARLA_ASSERT(kData->oscData != nullptr); - - const EnginePluginData& pData = kData->plugins[0]; - - if (kData->oscData != nullptr && kData->oscData->target != nullptr) - { - char targetPath[std::strlen(kData->oscData->path)+22]; - std::strcpy(targetPath, kData->oscData->path); - std::strcat(targetPath, "/bridge_set_peaks"); - lo_send(kData->oscData->target, targetPath, "ffff", pData.insPeak[0], pData.insPeak[1], pData.outsPeak[0], pData.outsPeak[1]); - } -} #endif CARLA_BACKEND_END_NAMESPACE diff --git a/source/backend/engine/CarlaEngineBridge.cpp b/source/backend/engine/CarlaEngineBridge.cpp index b9a8d255d..860e56738 100644 --- a/source/backend/engine/CarlaEngineBridge.cpp +++ b/source/backend/engine/CarlaEngineBridge.cpp @@ -21,9 +21,17 @@ #include "CarlaBackendUtils.hpp" #include "CarlaMIDI.h" -#include "CarlaBridge.hpp" +#include "../CarlaBridge.hpp" #include "CarlaShmUtils.hpp" +#include "jackbridge/jackbridge.h" + +#include + +//#ifdef CARLA_OS_WIN +//# include +//#endif + CARLA_BACKEND_START_NAMESPACE #if 0 @@ -32,16 +40,22 @@ CARLA_BACKEND_START_NAMESPACE // ----------------------------------------- -class CarlaEngineBridge : public CarlaEngine +class CarlaEngineBridge : public CarlaEngine, + public CarlaThread { public: CarlaEngineBridge(const char* const audioBaseName, const char* const controlBaseName) - : CarlaEngine() + : CarlaEngine(), + fIsRunning(false), + fQuitNow(false) { carla_debug("CarlaEngineBridge::CarlaEngineBridge()"); - fShmAudioPool.filename = "/carla-bridge_shm_" + audioBaseName; - fShmControl.filename = "/carla-bridge_shc_" + controlBaseName; + fShmAudioPool.filename = "/carla-bridge_shm_"; + fShmAudioPool.filename += audioBaseName; + + fShmControl.filename = "/carla-bridge_shc_"; + fShmControl.filename += controlBaseName; } ~CarlaEngineBridge() @@ -56,11 +70,14 @@ public: { carla_debug("CarlaEngineBridge::init(\"%s\")", clientName); - char tmpFileBase[60]; - // SHM Audio Pool { +#ifdef CARLA_OS_WIN + // TESTING! + fShmAudioPool.shm = carla_shm_attach_linux((const char*)fShmAudioPool.filename); +#else fShmAudioPool.shm = carla_shm_attach((const char*)fShmAudioPool.filename); +#endif if (! carla_is_shm_valid(fShmAudioPool.shm)) { @@ -72,7 +89,12 @@ public: // SHM Control { +#ifdef CARLA_OS_WIN + // TESTING! + fShmControl.shm = carla_shm_attach_linux((const char*)fShmControl.filename); +#else fShmControl.shm = carla_shm_attach((const char*)fShmControl.filename); +#endif if (! carla_is_shm_valid(fShmControl.shm)) { @@ -81,7 +103,7 @@ public: return false; } - if (! carla_shm_map(fShmControl.shm, fShmControl.data)) + if (! carla_shm_map(fShmControl.shm, fShmControl.data)) { _cleanup(); carla_stdout("Failed to mmap shared memory file"); @@ -89,7 +111,20 @@ public: } } - CarlaEngine::init(fName); + // Read values from memory + CARLA_ASSERT(rdwr_readOpcode(&fShmControl.data->ringBuffer) == kPluginBridgeOpcodeBufferSize); + fBufferSize = rdwr_readInt(&fShmControl.data->ringBuffer); + carla_stderr("BufferSize: %i", fBufferSize); + + CARLA_ASSERT(rdwr_readOpcode(&fShmControl.data->ringBuffer) == kPluginBridgeOpcodeSampleRate); + fSampleRate = rdwr_readFloat(&fShmControl.data->ringBuffer); + carla_stderr("SampleRate: %f", fSampleRate); + + fQuitNow = false; + fIsRunning = true; + + CarlaThread::start(); + CarlaEngine::init(clientName); return true; } @@ -98,6 +133,9 @@ public: carla_debug("CarlaEnginePlugin::close()"); CarlaEngine::close(); + fQuitNow = true; + CarlaThread::stop(); + _cleanup(); return true; @@ -105,7 +143,7 @@ public: bool isRunning() const { - return true; + return fIsRunning; } bool isOffline() const @@ -118,16 +156,113 @@ public: return kEngineTypeBridge; } + // ------------------------------------- + // CarlaThread virtual calls + + void run() + { + // TODO - set RT permissions + + const int timeout = 50; + + while (! fQuitNow) + { + carla_debug("RUN 001"); + timespec ts_timeout; +#if 0//def CARLA_OS_WIN + timeval now; + gettimeofday(&now, nullptr); + ts_timeout.tv_sec = now.tv_sec; + ts_timeout.tv_nsec = now.tv_usec * 1000; +#else + linux_clock_gettime_rt(&ts_timeout); +#endif + + time_t seconds = timeout / 1000; + ts_timeout.tv_sec += seconds; + ts_timeout.tv_nsec += (timeout - seconds * 1000) * 1000000; + if (ts_timeout.tv_nsec >= 1000000000) { + ts_timeout.tv_nsec -= 1000000000; + ts_timeout.tv_sec++; + } + + if (linux_sem_timedwait(&fShmControl.data->runServer, &ts_timeout)) + { + if (errno == ETIMEDOUT) + { + continue; + } + else + { + fQuitNow = true; + break; + } + } + + carla_debug("RUN 004"); + + while (rdwr_dataAvailable(&fShmControl.data->ringBuffer)) + { + carla_debug("RUN 005"); + const PluginBridgeOpcode opcode = rdwr_readOpcode(&fShmControl.data->ringBuffer); + + switch (opcode) + { + case kPluginBridgeOpcodeNull: + break; + case kPluginBridgeOpcodeReadyWait: + fShmAudioPool.data = (float*)carla_shm_map(fShmAudioPool.shm, rdwr_readInt(&fShmControl.data->ringBuffer)); + break; + case kPluginBridgeOpcodeBufferSize: + bufferSizeChanged(rdwr_readInt(&fShmControl.data->ringBuffer)); + break; + case kPluginBridgeOpcodeSampleRate: + sampleRateChanged(rdwr_readFloat(&fShmControl.data->ringBuffer)); + break; + case kPluginBridgeOpcodeProcess: + { + CARLA_ASSERT(fShmAudioPool.data != nullptr); + CarlaPlugin* const plugin(getPluginUnchecked(0)); + carla_debug("RUN 006"); + + if (plugin != nullptr && plugin->enabled() && plugin->tryLock()) + { + const uint32_t inCount = plugin->audioInCount(); + const uint32_t outCount = plugin->audioOutCount(); + + float* inBuffer[inCount]; + float* outBuffer[outCount]; + + for (uint32_t i=0; i < inCount; i++) + inBuffer[i] = fShmAudioPool.data + i*fBufferSize; + for (uint32_t i=0; i < outCount; i++) + outBuffer[i] = fShmAudioPool.data + (i+inCount)*fBufferSize; + + plugin->initBuffers(); + plugin->setActive(true, false, false); + plugin->process(inBuffer, outBuffer, fBufferSize); + plugin->unlock(); + } + break; + } + } + } + + if (linux_sem_post(&fShmControl.data->runClient) != 0) + carla_stderr2("Could not post to semaphore"); + } + + fIsRunning = false; + } + private: struct BridgeAudioPool { CarlaString filename; float* data; - size_t size; shm_t shm; BridgeAudioPool() - : data(nullptr), - size(0) + : data(nullptr) { carla_shm_init(shm); } @@ -135,7 +270,7 @@ private: struct BridgeControl { CarlaString filename; - ShmControl* data; + BridgeShmControl* data; shm_t shm; BridgeControl() @@ -146,6 +281,9 @@ private: } fShmControl; + bool fIsRunning; + bool fQuitNow; + void _cleanup() { if (fShmAudioPool.filename.isNotEmpty()) @@ -154,11 +292,7 @@ private: 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)) diff --git a/source/backend/engine/CarlaEngineInternal.hpp b/source/backend/engine/CarlaEngineInternal.hpp index d3068f0a1..3e20e7715 100644 --- a/source/backend/engine/CarlaEngineInternal.hpp +++ b/source/backend/engine/CarlaEngineInternal.hpp @@ -210,9 +210,9 @@ struct CarlaEngineProtectedData { #ifndef BUILD_BRIDGE static void registerEnginePlugin(CarlaEngine* const engine, const unsigned int id, CarlaPlugin* const plugin) { - CARLA_ASSERT(id < engine->kData->curPluginCount); + CARLA_ASSERT(id == engine->kData->curPluginCount); - if (id < engine->kData->curPluginCount) + if (id == engine->kData->curPluginCount) engine->kData->plugins[id].plugin = plugin; } #endif diff --git a/source/backend/engine/CarlaEnginePlugin.cpp b/source/backend/engine/CarlaEnginePlugin.cpp index c08bd7003..38ab45136 100644 --- a/source/backend/engine/CarlaEnginePlugin.cpp +++ b/source/backend/engine/CarlaEnginePlugin.cpp @@ -99,10 +99,7 @@ public: fBufferSize = d_bufferSize(); fSampleRate = d_sampleRate(); - fName = clientName; - fName.toBasic(); - - CarlaEngine::init(fName); + CarlaEngine::init(clientName); return true; } diff --git a/source/backend/engine/CarlaEngineThread.cpp b/source/backend/engine/CarlaEngineThread.cpp index 5b679ec23..fd2f5a00f 100644 --- a/source/backend/engine/CarlaEngineThread.cpp +++ b/source/backend/engine/CarlaEngineThread.cpp @@ -129,17 +129,13 @@ void CarlaEngineThread::run() } } +#ifndef BUILD_BRIDGE // --------------------------------------------------- // Update OSC control client peaks if (oscRegisted) - { -#ifdef BUILD_BRIDGE - kEngine->osc_send_bridge_set_peaks(); -#else kEngine->osc_send_control_set_peaks(i); #endif - } } } diff --git a/source/backend/plugin/BridgePlugin.cpp b/source/backend/plugin/BridgePlugin.cpp index 451960068..0b3a489d0 100644 --- a/source/backend/plugin/BridgePlugin.cpp +++ b/source/backend/plugin/BridgePlugin.cpp @@ -84,8 +84,6 @@ shm_t shm_mkstemp(char* const fileBase) return shm; } - std::srand(std::time(NULL)); - while (true) { for (int c = size - 6; c < size; c++) @@ -136,28 +134,24 @@ public: kData->singleMutex.lock(); kData->masterMutex.lock(); -#if 0 - if (osc.data.target) + _cleanup(); + + if (kData->osc.data.target != nullptr) { - osc_send_hide(&osc.data); - osc_send_quit(&osc.data); - osc.data.free(); + osc_send_hide(&kData->osc.data); + osc_send_quit(&kData->osc.data); } - if (osc.thread) - { - // Wait a bit first, try safe quit, then force kill - if (osc.thread->isRunning() && ! osc.thread->wait(3000)) - { - carla_stderr("Failed to properly stop Plugin Bridge thread"); - osc.thread->terminate(); - } + kData->osc.data.free(); - delete osc.thread; + // Wait a bit first, then force kill + if (kData->osc.thread.isRunning() && ! kData->osc.thread.stop(1000)) // kData->engine->getOptions().oscUiTimeout + { + carla_stderr("Failed to properly stop Plugin Bridge thread"); + kData->osc.thread.terminate(); } - info.chunk.clear(); -#endif + //info.chunk.clear(); } // ------------------------------------------------------------------- @@ -227,39 +221,29 @@ public: return params[parameterId].value; } +#endif void getLabel(char* const strBuf) { - if (info.label) - strncpy(strBuf, info.label, STR_MAX); - else - CarlaPlugin::getLabel(strBuf); + std::strncpy(strBuf, (const char*)fInfo.label, STR_MAX); } void getMaker(char* const strBuf) { - if (info.maker) - strncpy(strBuf, info.maker, STR_MAX); - else - CarlaPlugin::getMaker(strBuf); + std::strncpy(strBuf, (const char*)fInfo.maker, STR_MAX); } void getCopyright(char* const strBuf) { - if (info.copyright) - strncpy(strBuf, info.copyright, STR_MAX); - else - CarlaPlugin::getCopyright(strBuf); + std::strncpy(strBuf, (const char*)fInfo.copyright, STR_MAX); } void getRealName(char* const strBuf) { - if (info.name) - strncpy(strBuf, info.name, STR_MAX); - else - CarlaPlugin::getRealName(strBuf); + std::strncpy(strBuf, (const char*)fInfo.name, STR_MAX); } +#if 0 void getParameterName(const uint32_t parameterId, char* const strBuf) { CARLA_ASSERT(parameterId < param.count); @@ -273,15 +257,6 @@ public: strncpy(strBuf, params[parameterId].unit.toUtf8().constData(), STR_MAX); } - - void getGuiInfo(GuiType* const type, bool* const resizable) - { - if (m_hints & PLUGIN_HAS_GUI) - *type = GUI_EXTERNAL_OSC; - else - *type = GUI_NONE; - *resizable = false; - } #endif // ------------------------------------------------------------------- @@ -289,12 +264,11 @@ public: int setOscPluginBridgeInfo(const PluginBridgeInfoType type, const int argc, const lo_arg* const* const argv, const char* const types) { - carla_debug("setOscPluginBridgeInfo(%i, %i, %p, \"%s\")", type, argc, argv, types); + carla_stdout("setOscPluginBridgeInfo(%i, %i, %p, \"%s\")", type, argc, argv, types); -#if 0 switch (type) { - case PluginBridgeAudioCount: + case kPluginBridgeAudioCount: { CARLA_BRIDGE_CHECK_OSC_TYPES(3, "iii"); @@ -306,14 +280,16 @@ public: CARLA_ASSERT(aOuts >= 0); CARLA_ASSERT(aIns + aOuts == aTotal); - info.aIns = aIns; - info.aOuts = aOuts; + fInfo.aIns = aIns; + fInfo.aOuts = aOuts; break; - Q_UNUSED(aTotal); + + // unused + (void)aTotal; } - case PluginBridgeMidiCount: + case kPluginBridgeMidiCount: { CARLA_BRIDGE_CHECK_OSC_TYPES(3, "iii"); @@ -325,13 +301,16 @@ public: CARLA_ASSERT(mOuts >= 0); CARLA_ASSERT(mIns + mOuts == mTotal); - info.mIns = mIns; - info.mOuts = mOuts; + fInfo.mIns = mIns; + fInfo.mOuts = mOuts; break; - Q_UNUSED(mTotal); + + // unused + (void)mTotal; } +#if 0 case PluginBridgeParameterCount: { CARLA_BRIDGE_CHECK_OSC_TYPES(3, "iii"); @@ -438,8 +417,9 @@ public: break; } +#endif - case PluginBridgePluginInfo: + case kPluginBridgePluginInfo: { CARLA_BRIDGE_CHECK_OSC_TYPES(7, "iissssh"); @@ -458,21 +438,23 @@ public: CARLA_ASSERT(maker); CARLA_ASSERT(copyright); - m_hints = hints | PLUGIN_IS_BRIDGE; - info.category = (PluginCategory)category; - info.uniqueId = uniqueId; + fHints = hints | PLUGIN_IS_BRIDGE; - info.name = strdup(name); - info.label = strdup(label); - info.maker = strdup(maker); - info.copyright = strdup(copyright); + fInfo.category = static_cast(category); + fInfo.uniqueId = uniqueId; - if (! m_name) - m_name = x_engine->getUniquePluginName(name); + fInfo.name = name; + fInfo.label = label; + fInfo.maker = maker; + fInfo.copyright = copyright; + + if (fName.isEmpty()) + fName = kData->engine->getNewUniquePluginName(name); break; } +#if 0 case PluginBridgeParameterInfo: { CARLA_BRIDGE_CHECK_OSC_TYPES(3, "iss"); @@ -753,30 +735,29 @@ public: break; } +#endif - case PluginBridgeUpdateNow: + case kPluginBridgeUpdateNow: { - m_initiated = true; + fInitiated = true; break; } - case PluginBridgeError: + case kPluginBridgeError: { CARLA_BRIDGE_CHECK_OSC_TYPES(1, "s"); const char* const error = (const char*)&argv[0]->s; - CARLA_ASSERT(error); + CARLA_ASSERT(error != nullptr); - m_initiated = true; - m_initError = true; - - x_engine->setLastError(error); + kData->engine->setLastError(error); + fInitError = true; + fInitiated = true; break; } } -#endif return 0; } @@ -837,6 +818,7 @@ public: osc_send_configure(&osc.data, CARLA_BRIDGE_MSG_SET_CHUNK, filePath.toUtf8().constData()); } } +#endif // ------------------------------------------------------------------- // Set gui stuff @@ -844,15 +826,15 @@ public: void showGui(const bool yesNo) { if (yesNo) - osc_send_show(&osc.data); + osc_send_show(&kData->osc.data); else - osc_send_hide(&osc.data); + osc_send_hide(&kData->osc.data); } void idleGui() { - if (! osc.thread->isRunning()) - carla_stderr("TESTING: Bridge has closed!"); + if (! kData->osc.thread.isRunning()) + carla_stderr2("TESTING: Bridge has closed!"); CarlaPlugin::idleGui(); } @@ -860,6 +842,131 @@ public: // ------------------------------------------------------------------- // Plugin state + void reload() + { + carla_debug("BridgePlugin::reload() - start"); + CARLA_ASSERT(kData->engine != nullptr); + + if (kData->engine == nullptr) + return; + + const ProcessMode processMode(kData->engine->getProccessMode()); + + // Safely disable plugin for reload + const ScopedDisabler sd(this); + + deleteBuffers(); + + bool needsCtrlIn, needsCtrlOut; + needsCtrlIn = needsCtrlOut = false; + + if (fInfo.aIns > 0) + { + kData->audioIn.createNew(fInfo.aIns); + } + + if (fInfo.aOuts > 0) + { + kData->audioOut.createNew(fInfo.aOuts); + needsCtrlIn = true; + } + + if (fInfo.mIns > 0) + needsCtrlIn = true; + + if (fInfo.mOuts > 0) + needsCtrlOut = true; + + const uint portNameSize = kData->engine->maxPortNameSize(); + CarlaString portName; + + // Audio Ins + for (uint32_t j=0; j < fInfo.aIns; j++) + { + portName.clear(); + + if (processMode == PROCESS_MODE_SINGLE_CLIENT) + { + portName = fName; + portName += ":"; + } + + if (fInfo.aIns > 1) + { + portName += "input_"; + portName += CarlaString(j+1); + } + else + portName += "input"; + portName.truncate(portNameSize); + + kData->audioIn.ports[j].port = (CarlaEngineAudioPort*)kData->client->addPort(kEnginePortTypeAudio, portName, true); + kData->audioIn.ports[j].rindex = j; + } + + // Audio Outs + for (uint32_t j=0; j < fInfo.aOuts; j++) + { + portName.clear(); + + if (processMode == PROCESS_MODE_SINGLE_CLIENT) + { + portName = fName; + portName += ":"; + } + + if (fInfo.aOuts > 1) + { + portName += "output_"; + portName += CarlaString(j+1); + } + else + portName += "output"; + portName.truncate(portNameSize); + + kData->audioOut.ports[j].port = (CarlaEngineAudioPort*)kData->client->addPort(kEnginePortTypeAudio, portName, false); + kData->audioOut.ports[j].rindex = j; + } + + if (needsCtrlIn) + { + portName.clear(); + + if (processMode == PROCESS_MODE_SINGLE_CLIENT) + { + portName = fName; + portName += ":"; + } + + portName += "event-in"; + portName.truncate(portNameSize); + + kData->event.portIn = (CarlaEngineEventPort*)kData->client->addPort(kEnginePortTypeEvent, portName, true); + } + + if (needsCtrlOut) + { + portName.clear(); + + if (processMode == PROCESS_MODE_SINGLE_CLIENT) + { + portName = fName; + portName += ":"; + } + + portName += "event-out"; + portName.truncate(portNameSize); + + kData->event.portOut = (CarlaEngineEventPort*)kData->client->addPort(kEnginePortTypeEvent, portName, false); + } + + //bufferSizeChanged(kData->engine->getBufferSize()); + //reloadPrograms(true); + + carla_debug("BridgePlugin::reload() - end"); + } + +#if 0 void prepareForSave() { m_saved = false; @@ -877,93 +984,125 @@ public: else carla_debug("BridgePlugin::prepareForSave() - success!"); } +#endif + + // ------------------------------------------------------------------- + // Plugin processing + + void process(float** const inBuffer, float** const outBuffer, const uint32_t frames) + { + uint32_t i, k; + + // -------------------------------------------------------------------------------------------------------- + // Check if active + + if (! kData->active) + { + // disable any output sound + for (i=0; i < kData->audioOut.count; i++) + carla_zeroFloat(outBuffer[i], frames); + + kData->activeBefore = kData->active; + return; + } + + for (i=0; i < fInfo.aIns; ++i) + carla_copyFloat(fShmAudioPool.data + i * frames, inBuffer[i], frames); + + rdwr_writeOpcode(&fShmControl.data->ringBuffer, kPluginBridgeOpcodeProcess); + rdwr_commitWrite(&fShmControl.data->ringBuffer); + + waitForServer(); + + for (i=0; i < fInfo.aOuts; ++i) + carla_copyFloat(outBuffer[i], fShmAudioPool.data + (i+fInfo.aIns) * frames, frames); + } // ------------------------------------------------------------------- // Post-poned events - void uiParameterChange(const uint32_t index, const double value) + void uiParameterChange(const uint32_t index, const float value) { - CARLA_ASSERT(index < param.count); + CARLA_ASSERT(index < kData->param.count); - if (index >= param.count) + if (index >= kData->param.count) return; - if (! osc.data.target) + if (kData->osc.data.target == nullptr) return; - osc_send_control(&osc.data, param.data[index].rindex, value); + osc_send_control(&kData->osc.data, kData->param.data[index].rindex, value); } void uiProgramChange(const uint32_t index) { - CARLA_ASSERT(index < prog.count); + CARLA_ASSERT(index < kData->prog.count); - if (index >= prog.count) + if (index >= kData->prog.count) return; - if (! osc.data.target) + if (kData->osc.data.target == nullptr) return; - osc_send_program(&osc.data, index); + osc_send_program(&kData->osc.data, index); } void uiMidiProgramChange(const uint32_t index) { - CARLA_ASSERT(index < midiprog.count); + CARLA_ASSERT(index < kData->midiprog.count); - if (index >= midiprog.count) + if (index >= kData->midiprog.count) return; - if (! osc.data.target) + if (kData->osc.data.target == nullptr) return; - osc_send_midi_program(&osc.data, index); + osc_send_midi_program(&kData->osc.data, index); } void uiNoteOn(const uint8_t channel, const uint8_t note, const uint8_t velo) { - CARLA_ASSERT(channel < 16); - CARLA_ASSERT(note < 128); - CARLA_ASSERT(velo > 0 && velo < 128); + CARLA_ASSERT(channel < MAX_MIDI_CHANNELS); + CARLA_ASSERT(note < MAX_MIDI_NOTE); + CARLA_ASSERT(velo > 0 && velo < MAX_MIDI_VALUE); - if (! osc.data.target) + if (channel >= MAX_MIDI_CHANNELS) + return; + if (note >= MAX_MIDI_NOTE) + return; + if (velo >= MAX_MIDI_VALUE) + return; + if (kData->osc.data.target == nullptr) return; uint8_t midiData[4] = { 0 }; midiData[1] = MIDI_STATUS_NOTE_ON + channel; midiData[2] = note; midiData[3] = velo; - osc_send_midi(&osc.data, midiData); + + osc_send_midi(&kData->osc.data, midiData); } void uiNoteOff(const uint8_t channel, const uint8_t note) { - CARLA_ASSERT(channel < 16); - CARLA_ASSERT(note < 128); + CARLA_ASSERT(channel < MAX_MIDI_CHANNELS); + CARLA_ASSERT(note < MAX_MIDI_NOTE); - if (! osc.data.target) + if (channel >= MAX_MIDI_CHANNELS) + return; + if (note >= MAX_MIDI_NOTE) + return; + if (kData->osc.data.target == nullptr) return; uint8_t midiData[4] = { 0 }; midiData[1] = MIDI_STATUS_NOTE_OFF + channel; midiData[2] = note; - osc_send_midi(&osc.data, midiData); - } - // ------------------------------------------------------------------- - // Cleanup + osc_send_midi(&kData->osc.data, midiData); + } - void deleteBuffers() + void bufferSizeChanged(const uint32_t newBufferSize) { - carla_debug("BridgePlugin::delete_buffers() - start"); - - if (param.count > 0) - delete[] params; - - params = nullptr; - - CarlaPlugin::deleteBuffers(); - - carla_debug("BridgePlugin::delete_buffers() - end"); + resizeAudioPool(newBufferSize); } -#endif // ------------------------------------------------------------------- @@ -1001,11 +1140,23 @@ public: fFilename = filename; + // --------------------------------------------------------------- + // register client + + kData->client = kData->engine->addClient(this); + + if (kData->client == nullptr || ! kData->client->isOk()) + { + kData->engine->setLastError("Failed to register plugin client"); + return false; + } + // --------------------------------------------------------------- // SHM Audio Pool { char tmpFileBase[60]; + std::srand(std::time(NULL)); std::sprintf(tmpFileBase, "/carla-bridge_shm_XXXXXX"); fShmAudioPool.shm = shm_mkstemp(tmpFileBase); @@ -1024,6 +1175,7 @@ public: // SHM Control { char tmpFileBase[60]; + std::sprintf(tmpFileBase, "/carla-bridge_shc_XXXXXX"); fShmControl.shm = shm_mkstemp(tmpFileBase); @@ -1044,7 +1196,10 @@ public: return false; } + CARLA_ASSERT(fShmControl.data != nullptr); + std::memset(fShmControl.data, 0, sizeof(BridgeShmControl)); + std::strcpy(fShmControl.data->ringBuffer.buf, "This thing is actually working!!!!"); if (sem_init(&fShmControl.data->runServer, 1, 0) != 0) { @@ -1061,11 +1216,29 @@ public: } } + // initial values + rdwr_writeOpcode(&fShmControl.data->ringBuffer, kPluginBridgeOpcodeBufferSize); + rdwr_writeInt(&fShmControl.data->ringBuffer, kData->engine->getBufferSize()); + + rdwr_writeOpcode(&fShmControl.data->ringBuffer, kPluginBridgeOpcodeSampleRate); + rdwr_writeFloat(&fShmControl.data->ringBuffer, kData->engine->getSampleRate()); + + rdwr_commitWrite(&fShmControl.data->ringBuffer); + // register plugin now so we can receive OSC (and wait for it) + fHints |= PLUGIN_IS_BRIDGE; registerEnginePlugin(kData->engine, fId, this); - kData->osc.thread.setOscData(bridgeBinary, label, getPluginTypeAsString(fPluginType)); - kData->osc.thread.start(); + // init OSC + { + char shmIdStr[12+1] = { 0 }; + std::strncpy(shmIdStr, &fShmAudioPool.filename[fShmAudioPool.filename.length()-6], 6); + std::strncat(shmIdStr, &fShmControl.filename[fShmControl.filename.length()-6], 6); + + kData->osc.thread.setOscData(bridgeBinary, label, getPluginTypeAsString(fPluginType), shmIdStr); + kData->osc.thread.start(); + kData->osc.thread.waitForStarted(); + } for (int i=0; i < 200; i++) { @@ -1079,7 +1252,9 @@ public: // unregister so it gets handled properly registerEnginePlugin(kData->engine, fId, nullptr); - kData->osc.thread.terminate(); + if (kData->osc.thread.isRunning()) + 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; } @@ -1093,10 +1268,16 @@ public: return false; } + resizeAudioPool(kData->engine->getBufferSize()); + + rdwr_writeOpcode(&fShmControl.data->ringBuffer, kPluginBridgeOpcodeReadyWait); + rdwr_writeInt(&fShmControl.data->ringBuffer, fShmAudioPool.size); + rdwr_commitWrite(&fShmControl.data->ringBuffer); + waitForServer(); + return true; } - private: const BinaryType fBinaryType; const PluginType fPluginType; @@ -1154,6 +1335,61 @@ private: BridgeParamInfo* fParams; + void _cleanup() + { + if (fShmAudioPool.filename.isNotEmpty()) + fShmAudioPool.filename.clear(); + + if (fShmControl.filename.isNotEmpty()) + fShmControl.filename.clear(); + + if (fShmAudioPool.data != nullptr) + { + carla_shm_unmap(fShmAudioPool.shm, fShmAudioPool.data, fShmAudioPool.size); + fShmAudioPool.data = nullptr; + } + + fShmAudioPool.size = 0; + + // and again + if (fShmControl.data != nullptr) + { + carla_shm_unmap(fShmControl.shm, fShmControl.data, sizeof(BridgeShmControl)); + 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); + } + + void resizeAudioPool(const uint32_t bufferSize) + { + if (fShmAudioPool.data != nullptr) + carla_shm_unmap(fShmAudioPool.shm, fShmAudioPool.data, fShmAudioPool.size); + + fShmAudioPool.size = (fInfo.aIns+fInfo.aOuts)*bufferSize*sizeof(float); + + if (fShmAudioPool.size > 0) + fShmAudioPool.data = (float*)carla_shm_map(fShmAudioPool.shm, fShmAudioPool.size); + else + fShmAudioPool.data = nullptr; + } + + void waitForServer() + { + sem_post(&fShmControl.data->runServer); + + timespec ts_timeout; + clock_gettime(CLOCK_REALTIME, &ts_timeout); + ts_timeout.tv_sec += 5; + + if (sem_timedwait(&fShmControl.data->runClient, &ts_timeout) != 0) + kData->active = false; // TODO + } + CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(BridgePlugin) }; @@ -1163,12 +1399,12 @@ CARLA_BACKEND_END_NAMESPACE CARLA_BACKEND_START_NAMESPACE -CarlaPlugin* CarlaPlugin::newBridge(const Initializer& init, BinaryType btype, PluginType ptype, const char* const extra) +CarlaPlugin* CarlaPlugin::newBridge(const Initializer& init, BinaryType btype, PluginType ptype, const char* const bridgeBinary) { - carla_debug("CarlaPlugin::newBridge({%p, \"%s\", \"%s\", \"%s\"}, %s, %s)", init.engine, init.filename, init.name, init.label, BinaryType2Str(btype), PluginType2Str(ptype)); + carla_debug("CarlaPlugin::newBridge({%p, \"%s\", \"%s\", \"%s\"}, %s, %s, \"%s\")", init.engine, init.filename, init.name, init.label, BinaryType2Str(btype), PluginType2Str(ptype), bridgeBinary); -#if 1//ndef BUILD_BRIDGE - if (extra == nullptr) +#ifndef BUILD_BRIDGE + if (bridgeBinary == nullptr) { init.engine->setLastError("Bridge not possible, bridge-binary not found"); return nullptr; @@ -1176,7 +1412,7 @@ CarlaPlugin* CarlaPlugin::newBridge(const Initializer& init, BinaryType btype, P BridgePlugin* const plugin = new BridgePlugin(init.engine, init.id, btype, ptype); - //if (! plugin->init(init.filename, init.name, init.label, (const char*)extra)) + if (! plugin->init(init.filename, init.name, init.label, bridgeBinary)) { delete plugin; return nullptr; @@ -1188,10 +1424,13 @@ CarlaPlugin* CarlaPlugin::newBridge(const Initializer& init, BinaryType btype, P #else init.engine->setLastError("Plugin bridge support not available"); return nullptr; + + // unused + (void)bridgeBinary; #endif } -#if 1//ndef BUILD_BRIDGE +#ifndef BUILD_BRIDGE // ------------------------------------------------------------------- // Bridge Helper diff --git a/source/backend/plugin/CarlaPluginThread.cpp b/source/backend/plugin/CarlaPluginThread.cpp index 44cefc4e0..3922c8203 100644 --- a/source/backend/plugin/CarlaPluginThread.cpp +++ b/source/backend/plugin/CarlaPluginThread.cpp @@ -64,11 +64,12 @@ void CarlaPluginThread::setMode(const CarlaPluginThread::Mode mode) fMode = mode; } -void CarlaPluginThread::setOscData(const char* const binary, const char* const label, const char* const extra) +void CarlaPluginThread::setOscData(const char* const binary, const char* const label, const char* const extra1, const char* const extra2) { fBinary = binary; fLabel = label; - fExtra = extra; + fExtra1 = extra1; + fExtra2 = extra2; } void CarlaPluginThread::run() @@ -84,40 +85,44 @@ void CarlaPluginThread::run() #endif } - QString name(kPlugin->name() ? kPlugin->name() : "(none)"); + QString name(kPlugin->name()); QStringList arguments; + if (name.isEmpty()) + name = "(none)"; + switch (fMode) { case PLUGIN_THREAD_NULL: break; case PLUGIN_THREAD_DSSI_GUI: - /* osc_url */ arguments << QString("%1/%2").arg(kEngine->getOscServerPathUDP()).arg(kPlugin->id()); + /* osc-url */ arguments << QString("%1/%2").arg(kEngine->getOscServerPathUDP()).arg(kPlugin->id()); /* filename */ arguments << kPlugin->filename(); /* label */ arguments << (const char*)fLabel; /* ui-title */ arguments << QString("%1 (GUI)").arg(kPlugin->name()); break; case PLUGIN_THREAD_LV2_GUI: - /* osc_url */ arguments << QString("%1/%2").arg(kEngine->getOscServerPathTCP()).arg(kPlugin->id()); + /* osc-url */ arguments << QString("%1/%2").arg(kEngine->getOscServerPathTCP()).arg(kPlugin->id()); /* URI */ arguments << (const char*)fLabel; - /* ui-URI */ arguments << (const char*)fExtra; + /* ui-URI */ arguments << (const char*)fExtra1; /* ui-title */ arguments << QString("%1 (GUI)").arg(kPlugin->name()); break; case PLUGIN_THREAD_VST_GUI: - /* osc_url */ arguments << QString("%1/%2").arg(kEngine->getOscServerPathTCP()).arg(kPlugin->id()); + /* osc-url */ arguments << QString("%1/%2").arg(kEngine->getOscServerPathTCP()).arg(kPlugin->id()); /* filename */ arguments << kPlugin->filename(); /* ui-title */ arguments << QString("%1 (GUI)").arg(kPlugin->name()); break; case PLUGIN_THREAD_BRIDGE: - /* osc_url */ arguments << QString("%1/%2").arg(kEngine->getOscServerPathTCP()).arg(kPlugin->id()); - /* stype */ arguments << (const char*)fExtra; + /* osc-url */ arguments << QString("%1/%2").arg(kEngine->getOscServerPathTCP()).arg(kPlugin->id()); + /* stype */ arguments << (const char*)fExtra1; /* filename */ arguments << kPlugin->filename(); /* name */ arguments << name; /* label */ arguments << (const char*)fLabel; + /* SHM ids */ arguments << (const char*)fExtra2; break; } diff --git a/source/backend/plugin/CarlaPluginThread.hpp b/source/backend/plugin/CarlaPluginThread.hpp index 29c0a3a28..f966a9920 100644 --- a/source/backend/plugin/CarlaPluginThread.hpp +++ b/source/backend/plugin/CarlaPluginThread.hpp @@ -41,7 +41,7 @@ public: ~CarlaPluginThread(); void setMode(const CarlaPluginThread::Mode mode); - void setOscData(const char* const binary, const char* const label, const char* const extra=""); + void setOscData(const char* const binary, const char* const label, const char* const extra1="", const char* const extra2=""); protected: void run(); @@ -53,7 +53,8 @@ private: Mode fMode; CarlaString fBinary; CarlaString fLabel; - CarlaString fExtra; + CarlaString fExtra1; + CarlaString fExtra2; QProcess* fProcess; }; diff --git a/source/backend/standalone/CarlaStandalone.cpp b/source/backend/standalone/CarlaStandalone.cpp index a47c1df99..021af6e48 100644 --- a/source/backend/standalone/CarlaStandalone.cpp +++ b/source/backend/standalone/CarlaStandalone.cpp @@ -46,7 +46,9 @@ struct CarlaBackendStandalone { CarlaEngine* engine; CarlaString lastError; CarlaString procName; +#ifndef BUILD_BRIDGE EngineOptions options; +#endif QApplication* app; bool needsInit; @@ -85,14 +87,6 @@ struct CarlaBackendStandalone { } standalone; -// ------------------------------------------------------------------------------------------------------------------- -// Helpers - -CarlaEngine* carla_get_standalone_engine() -{ - return standalone.engine; -} - // ------------------------------------------------------------------------------------------------------------------- // API @@ -232,16 +226,6 @@ bool carla_engine_init(const char* driverName, const char* clientName) CARLA_ASSERT(driverName != nullptr); CARLA_ASSERT(clientName != nullptr); -#if 0 - static bool sigInfoInitiated = false; - - if (! sigInfoInitiated) - { - setup_siginfo(); - sigInfoInitiated = true; - } -#endif - standalone.engine = CarlaEngine::newDriverByName(driverName); if (standalone.engine == nullptr) @@ -377,14 +361,13 @@ void carla_set_engine_callback(CarlaBackend::CallbackFunc func, void* ptr) standalone.engine->setCallback(func, ptr); } +#ifndef BUILD_BRIDGE void carla_set_engine_option(CarlaBackend::OptionsType option, int value, const char* valueStr) { carla_debug("carla_set_engine_option(%s, %i, \"%s\")", CarlaBackend::OptionsType2Str(option), value, valueStr); -#ifndef BUILD_BRIDGE if (standalone.engine != nullptr) standalone.engine->setOption(option, value, valueStr); -#endif switch (option) { @@ -493,6 +476,7 @@ void carla_set_engine_option(CarlaBackend::OptionsType option, int value, const #endif } } +#endif // ------------------------------------------------------------------------------------------------------------------- @@ -1853,6 +1837,65 @@ void carla_nsm_reply_save() // ------------------------------------------------------------------------------------------------------------------- +#ifdef BUILD_BRIDGE +bool carla_engine_init_bridge(const char* audioBaseName, const char* controlBaseName, const char* clientName) +{ + carla_debug("carla_engine_init_bridge(\"%s\", \"%s\", \"%s\")", audioBaseName, controlBaseName, clientName); + CARLA_ASSERT(standalone.engine == nullptr); + CARLA_ASSERT(audioBaseName != nullptr); + CARLA_ASSERT(controlBaseName != nullptr); + CARLA_ASSERT(clientName != nullptr); + + standalone.engine = CarlaEngine::newBridge(audioBaseName, controlBaseName); + + if (standalone.engine == nullptr) + { + standalone.lastError = "The seleted audio driver is not available!"; + return false; + } + + if (standalone.callback != nullptr) + standalone.engine->setCallback(standalone.callback, nullptr); + + standalone.engine->setOption(CarlaBackend::OPTION_PROCESS_MODE, CarlaBackend::PROCESS_MODE_BRIDGE, nullptr); + standalone.engine->setOption(CarlaBackend::OPTION_TRANSPORT_MODE, CarlaBackend::TRANSPORT_MODE_BRIDGE, nullptr); + standalone.engine->setOption(CarlaBackend::OPTION_PREFER_PLUGIN_BRIDGES, false, nullptr); + standalone.engine->setOption(CarlaBackend::OPTION_PREFER_UI_BRIDGES, false, nullptr); + + // TODO - read from environment +#ifndef BUILD_BRIDGE + standalone.engine->setOption(CarlaBackend::OPTION_FORCE_STEREO, standalone.options.forceStereo ? 1 : 0, nullptr); +# ifdef WANT_DSSI + standalone.engine->setOption(CarlaBackend::OPTION_USE_DSSI_VST_CHUNKS, standalone.options.useDssiVstChunks ? 1 : 0, nullptr); +# endif + standalone.engine->setOption(CarlaBackend::OPTION_MAX_PARAMETERS, static_cast(standalone.options.maxParameters), nullptr); +#endif + + const bool initiated = standalone.engine->init(clientName); + + if (initiated) + { + standalone.lastError = "no error"; + standalone.init(); + } + else + { + standalone.lastError = standalone.engine->getLastError(); + delete standalone.engine; + standalone.engine = nullptr; + } + + return initiated; +} + +CarlaEngine* carla_get_standalone_engine() +{ + return standalone.engine; +} +#endif + +// ------------------------------------------------------------------------------------------------------------------- + #if 0 //def QTCREATOR_TEST diff --git a/source/bridges/CarlaBridgeClient.hpp b/source/bridges/CarlaBridgeClient.hpp index ab57b31d4..46720825d 100644 --- a/source/bridges/CarlaBridgeClient.hpp +++ b/source/bridges/CarlaBridgeClient.hpp @@ -140,6 +140,8 @@ private: const char* fUiFilename; void* fUiLib; bool fUiQuit; +#else + friend class CarlaPluginClient; #endif const CarlaOscData* fOscData; diff --git a/source/bridges/CarlaBridgePlugin.cpp b/source/bridges/CarlaBridgePlugin.cpp index 6c0f92909..8e9cd36fe 100644 --- a/source/bridges/CarlaBridgePlugin.cpp +++ b/source/bridges/CarlaBridgePlugin.cpp @@ -115,17 +115,21 @@ class CarlaPluginClient : public CarlaBridgeClient, public QObject { public: - CarlaPluginClient(const char* const name) + CarlaPluginClient(const bool useBridge, const char* const driverName, const char* audioBaseName, const char* controlBaseName) : CarlaBridgeClient(nullptr), QObject(nullptr), fEngine(nullptr), fPlugin(nullptr), fTimerId(0) { - CARLA_ASSERT(name != nullptr); - carla_debug("CarlaPluginClient::CarlaPluginClient()"); + CARLA_ASSERT(driverName != nullptr); + carla_debug("CarlaPluginClient::CarlaPluginClient(%s, \"%s\", %s, %s)", bool2str(useBridge), driverName, audioBaseName, controlBaseName); + + if (useBridge) + carla_engine_init_bridge(audioBaseName, controlBaseName, driverName); + else + carla_engine_init("JACK", driverName); - carla_engine_init("JACK", name); carla_set_engine_callback(callback, this); } @@ -138,6 +142,14 @@ public: carla_engine_close(); } + void oscInit(const char* const url) + { + CarlaBridgeClient::oscInit(url); + + fEngine = carla_get_standalone_engine(); + fEngine->setOscBridgeData(fOscData); + } + void ready() { CARLA_ASSERT(fTimerId == 0); @@ -405,9 +417,6 @@ int CarlaBridgeOsc::handleMsgQuit() carla_debug("CarlaBridgeOsc::handleMsgQuit()"); CARLA_ASSERT(kClient != nullptr); - if (kClient == nullptr) - return 1; - gCloseNow = true; return 0; @@ -473,7 +482,7 @@ int main(int argc, char* argv[]) { CARLA_BRIDGE_USE_NAMESPACE - if (argc != 6) + if (argc != 6 && argc != 7) { carla_stdout("usage: %s