| @@ -1116,7 +1116,7 @@ private: | |||
| #ifdef BUILD_BRIDGE | |||
| public: | |||
| // Bridge | |||
| static CarlaEngine* newBridge(const char* const audioBaseName, const char* const controlBaseName); | |||
| static CarlaEngine* newBridge(const char* const audioBaseName, const char* const controlBaseName, const char* const timeBaseName); | |||
| // ------------------------------------------------------------------- | |||
| // Bridge/Controller OSC stuff | |||
| @@ -497,9 +497,10 @@ CARLA_EXPORT bool carla_engine_init(const char* driverName, const char* clientNa | |||
| * Initialize the engine in bridged mode. | |||
| * @param audioBaseName Shared memory key for audio pool | |||
| * @param controlBaseName Shared memory key for control messages | |||
| * @param timeBaseName Shared memory key for time info | |||
| * @param clientName Engine master client name | |||
| */ | |||
| CARLA_EXPORT bool carla_engine_init_bridge(const char audioBaseName[6+1], const char controlBaseName[6+1], const char* clientName); | |||
| CARLA_EXPORT bool carla_engine_init_bridge(const char audioBaseName[6+1], const char controlBaseName[6+1], const char timeBaseName[6+1], const char* clientName); | |||
| #endif | |||
| /*! | |||
| @@ -82,6 +82,8 @@ struct BridgeAudioPool { | |||
| if (jackbridge_shm_is_valid(shm)) | |||
| jackbridge_shm_close(shm); | |||
| } | |||
| CARLA_DECLARE_NON_COPY_STRUCT(BridgeAudioPool) | |||
| }; | |||
| struct BridgeControl : public RingBufferControl<StackRingBuffer> { | |||
| @@ -139,6 +141,55 @@ struct BridgeControl : public RingBufferControl<StackRingBuffer> { | |||
| { | |||
| return static_cast<PluginBridgeOpcode>(readInt()); | |||
| } | |||
| CARLA_DECLARE_NON_COPY_STRUCT(BridgeControl) | |||
| }; | |||
| struct BridgeTime { | |||
| CarlaString filename; | |||
| BridgeTimeInfo* info; | |||
| char shm[32]; | |||
| BridgeTime() | |||
| : info(nullptr) | |||
| { | |||
| carla_zeroChar(shm, 32); | |||
| jackbridge_shm_init(shm); | |||
| } | |||
| ~BridgeTime() | |||
| { | |||
| // should be cleared by now | |||
| CARLA_ASSERT(info == nullptr); | |||
| clear(); | |||
| } | |||
| bool attach() | |||
| { | |||
| jackbridge_shm_attach(shm, filename); | |||
| return jackbridge_shm_is_valid(shm); | |||
| } | |||
| void clear() | |||
| { | |||
| filename.clear(); | |||
| info = nullptr; | |||
| if (jackbridge_shm_is_valid(shm)) | |||
| jackbridge_shm_close(shm); | |||
| } | |||
| bool mapData() | |||
| { | |||
| CARLA_ASSERT(info == nullptr); | |||
| return jackbridge_shm_map2<BridgeTimeInfo>(shm, info); | |||
| } | |||
| CARLA_DECLARE_NON_COPY_STRUCT(BridgeTime) | |||
| }; | |||
| // ------------------------------------------------------------------- | |||
| @@ -147,7 +198,7 @@ class CarlaEngineBridge : public CarlaEngine, | |||
| public CarlaThread | |||
| { | |||
| public: | |||
| CarlaEngineBridge(const char* const audioBaseName, const char* const controlBaseName) | |||
| CarlaEngineBridge(const char* const audioBaseName, const char* const controlBaseName, const char* const timeBaseName) | |||
| : CarlaEngine(), | |||
| CarlaThread("CarlaEngineBridge"), | |||
| fIsRunning(false) | |||
| @@ -157,8 +208,11 @@ public: | |||
| fShmAudioPool.filename = "/carla-bridge_shm_"; | |||
| fShmAudioPool.filename += audioBaseName; | |||
| fShmControl.filename = "/carla-bridge_shc_"; | |||
| fShmControl.filename += controlBaseName; | |||
| fShmControl.filename = "/carla-bridge_shc_"; | |||
| fShmControl.filename += controlBaseName; | |||
| fShmTime.filename = "/carla-bridge_sht_"; | |||
| fShmTime.filename += timeBaseName; | |||
| } | |||
| ~CarlaEngineBridge() override | |||
| @@ -194,7 +248,7 @@ public: | |||
| if (! fShmControl.mapData()) | |||
| { | |||
| carla_stdout("Failed to mmap shared memory file"); | |||
| carla_stdout("Failed to map shared memory file #2"); | |||
| // clear | |||
| fShmControl.clear(); | |||
| fShmAudioPool.clear(); | |||
| @@ -202,6 +256,28 @@ public: | |||
| } | |||
| } | |||
| // SHM Transport | |||
| { | |||
| if (! fShmTime.attach()) | |||
| { | |||
| carla_stdout("Failed to open or create shared memory file #3"); | |||
| // clear | |||
| fShmControl.clear(); | |||
| fShmAudioPool.clear(); | |||
| return false; | |||
| } | |||
| if (! fShmTime.mapData()) | |||
| { | |||
| carla_stdout("Failed to map shared memory file #3"); | |||
| // clear | |||
| fShmTime.clear(); | |||
| fShmControl.clear(); | |||
| fShmAudioPool.clear(); | |||
| return false; | |||
| } | |||
| } | |||
| // Read values from memory | |||
| PluginBridgeOpcode opcode; | |||
| @@ -233,6 +309,7 @@ public: | |||
| CarlaThread::stop(6000); | |||
| fShmTime.clear(); | |||
| fShmControl.clear(); | |||
| fShmAudioPool.clear(); | |||
| @@ -297,7 +374,7 @@ public: | |||
| case kPluginBridgeOpcodeSetAudioPool: { | |||
| const int64_t poolSize(fShmControl.readLong()); | |||
| CARLA_SAFE_ASSERT_BREAK(poolSize > 0); | |||
| fShmAudioPool.data = (float*)jackbridge_shm_map(fShmAudioPool.shm, size_t(poolSize)); | |||
| fShmAudioPool.data = (float*)jackbridge_shm_map(fShmAudioPool.shm, static_cast<size_t>(poolSize)); | |||
| break; | |||
| } | |||
| @@ -388,6 +465,9 @@ public: | |||
| CARLA_SAFE_ASSERT_BREAK(fShmAudioPool.data != nullptr); | |||
| CarlaPlugin* const plugin(getPluginUnchecked(0)); | |||
| BridgeTimeInfo* const bridgeInfo(fShmTime.info); | |||
| CARLA_SAFE_ASSERT_BREAK(bridgeInfo != nullptr); | |||
| if (plugin != nullptr && plugin->isEnabled() && plugin->tryLock(true)) // FIXME - always lock? | |||
| { | |||
| const uint32_t inCount(plugin->getAudioInCount()); | |||
| @@ -401,6 +481,27 @@ public: | |||
| for (uint32_t i=0; i < outCount; ++i) | |||
| outBuffer[i] = fShmAudioPool.data + (i+inCount)*pData->bufferSize; | |||
| EngineTimeInfo& timeInfo(pData->timeInfo); | |||
| timeInfo.playing = bridgeInfo->playing; | |||
| timeInfo.frame = bridgeInfo->frame; | |||
| timeInfo.usecs = bridgeInfo->usecs; | |||
| timeInfo.valid = bridgeInfo->valid; | |||
| if (timeInfo.valid & EngineTimeInfo::kValidBBT) | |||
| { | |||
| timeInfo.bbt.bar = bridgeInfo->bar; | |||
| timeInfo.bbt.beat = bridgeInfo->beat; | |||
| timeInfo.bbt.tick = bridgeInfo->tick; | |||
| timeInfo.bbt.beatsPerBar = bridgeInfo->beatsPerBar; | |||
| timeInfo.bbt.beatType = bridgeInfo->beatType; | |||
| timeInfo.bbt.ticksPerBeat = bridgeInfo->ticksPerBeat; | |||
| timeInfo.bbt.beatsPerMinute = bridgeInfo->beatsPerMinute; | |||
| timeInfo.bbt.barStartTick = bridgeInfo->barStartTick; | |||
| } | |||
| plugin->initBuffers(); | |||
| plugin->process(inBuffer, outBuffer, pData->bufferSize); | |||
| plugin->unlock(); | |||
| @@ -431,6 +532,7 @@ public: | |||
| private: | |||
| BridgeAudioPool fShmAudioPool; | |||
| BridgeControl fShmControl; | |||
| BridgeTime fShmTime; | |||
| volatile bool fIsRunning; | |||
| @@ -439,9 +541,9 @@ private: | |||
| // ----------------------------------------- | |||
| CarlaEngine* CarlaEngine::newBridge(const char* const audioBaseName, const char* const controlBaseName) | |||
| CarlaEngine* CarlaEngine::newBridge(const char* const audioBaseName, const char* const controlBaseName, const char* const timeBaseName) | |||
| { | |||
| return new CarlaEngineBridge(audioBaseName, controlBaseName); | |||
| return new CarlaEngineBridge(audioBaseName, controlBaseName, timeBaseName); | |||
| } | |||
| CARLA_BACKEND_END_NAMESPACE | |||
| @@ -142,6 +142,8 @@ struct BridgeAudioPool { | |||
| data = (float*)carla_shm_map(shm, size); | |||
| } | |||
| CARLA_DECLARE_NON_COPY_STRUCT(BridgeAudioPool) | |||
| }; | |||
| struct BridgeControl : public RingBufferControl<StackRingBuffer> { | |||
| @@ -220,8 +222,68 @@ struct BridgeControl : public RingBufferControl<StackRingBuffer> { | |||
| { | |||
| writeInt(static_cast<int32_t>(opcode)); | |||
| } | |||
| CARLA_DECLARE_NON_COPY_STRUCT(BridgeControl) | |||
| }; | |||
| struct BridgeTime { | |||
| CarlaString filename; | |||
| BridgeTimeInfo* info; | |||
| shm_t shm; | |||
| BridgeTime() | |||
| : info(nullptr) | |||
| { | |||
| carla_shm_init(shm); | |||
| } | |||
| ~BridgeTime() | |||
| { | |||
| // should be cleared by now | |||
| CARLA_ASSERT(info == nullptr); | |||
| clear(); | |||
| } | |||
| void clear() | |||
| { | |||
| filename.clear(); | |||
| if (! carla_is_shm_valid(shm)) | |||
| return; | |||
| if (info != nullptr) | |||
| { | |||
| carla_shm_unmap(shm, info, sizeof(BridgeTimeInfo)); | |||
| info = nullptr; | |||
| } | |||
| carla_shm_close(shm); | |||
| } | |||
| bool mapData() | |||
| { | |||
| CARLA_ASSERT(info == nullptr); | |||
| return carla_shm_map<BridgeTimeInfo>(shm, info); | |||
| } | |||
| void unmapData() | |||
| { | |||
| CARLA_ASSERT(info != nullptr); | |||
| if (info == nullptr) | |||
| return; | |||
| carla_shm_unmap(shm, info, sizeof(BridgeTimeInfo)); | |||
| info = nullptr; | |||
| } | |||
| CARLA_DECLARE_NON_COPY_STRUCT(BridgeTime) | |||
| }; | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| struct BridgeParamInfo { | |||
| float value; | |||
| QString name; | |||
| @@ -299,6 +361,7 @@ public: | |||
| fShmAudioPool.clear(); | |||
| fShmControl.clear(); | |||
| fShmTime.clear(); | |||
| clearBuffers(); | |||
| @@ -1084,6 +1147,31 @@ public: | |||
| for (uint32_t i=0; i < fInfo.aIns; ++i) | |||
| FLOAT_COPY(fShmAudioPool.data + (i * frames), inBuffer[i], frames); | |||
| // -------------------------------------------------------------------------------------------------------- | |||
| // TimeInfo | |||
| const EngineTimeInfo& timeInfo(pData->engine->getTimeInfo()); | |||
| BridgeTimeInfo* const bridgeInfo(fShmTime.info); | |||
| bridgeInfo->playing = timeInfo.playing; | |||
| bridgeInfo->frame = timeInfo.frame; | |||
| bridgeInfo->usecs = timeInfo.usecs; | |||
| bridgeInfo->valid = timeInfo.valid; | |||
| if (timeInfo.valid & EngineTimeInfo::kValidBBT) | |||
| { | |||
| bridgeInfo->bar = timeInfo.bbt.bar; | |||
| bridgeInfo->beat = timeInfo.bbt.beat; | |||
| bridgeInfo->tick = timeInfo.bbt.tick; | |||
| bridgeInfo->beatsPerBar = timeInfo.bbt.beatsPerBar; | |||
| bridgeInfo->beatType = timeInfo.bbt.beatType; | |||
| bridgeInfo->ticksPerBeat = timeInfo.bbt.ticksPerBeat; | |||
| bridgeInfo->beatsPerMinute = timeInfo.bbt.beatsPerMinute; | |||
| bridgeInfo->barStartTick = timeInfo.bbt.barStartTick; | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------- | |||
| // Run plugin | |||
| @@ -1696,12 +1784,13 @@ public: | |||
| if (bridgeBinary != nullptr) | |||
| fBridgeBinary = bridgeBinary; | |||
| std::srand(static_cast<uint>(std::time(nullptr))); | |||
| // --------------------------------------------------------------- | |||
| // SHM Audio Pool | |||
| { | |||
| char tmpFileBase[60]; | |||
| std::srand(static_cast<uint>(std::time(nullptr))); | |||
| std::sprintf(tmpFileBase, "/carla-bridge_shm_XXXXXX"); | |||
| fShmAudioPool.shm = shm_mkstemp(tmpFileBase); | |||
| @@ -1736,7 +1825,7 @@ public: | |||
| if (! fShmControl.mapData()) | |||
| { | |||
| carla_stdout("Failed to mmap shared memory file"); | |||
| carla_stdout("Failed to map shared memory file #2"); | |||
| // clear | |||
| carla_shm_close(fShmControl.shm); | |||
| carla_shm_close(fShmAudioPool.shm); | |||
| @@ -1769,6 +1858,36 @@ public: | |||
| fNeedsSemDestroy = true; | |||
| } | |||
| // --------------------------------------------------------------- | |||
| // SHM TimeInfo | |||
| { | |||
| char tmpFileBase[60]; | |||
| std::sprintf(tmpFileBase, "/carla-bridge_sht_XXXXXX"); | |||
| fShmTime.shm = shm_mkstemp(tmpFileBase); | |||
| if (! carla_is_shm_valid(fShmTime.shm)) | |||
| { | |||
| carla_stdout("Failed to open or create shared memory file #3"); | |||
| return false; | |||
| } | |||
| fShmTime.filename = tmpFileBase; | |||
| if (! fShmTime.mapData()) | |||
| { | |||
| carla_stdout("Failed to map shared memory file #3"); | |||
| // clear | |||
| jackbridge_sem_destroy(&fShmControl.data->runServer); | |||
| fShmControl.unmapData(); | |||
| carla_shm_close(fShmTime.shm); | |||
| carla_shm_close(fShmControl.shm); | |||
| carla_shm_close(fShmAudioPool.shm); | |||
| return false; | |||
| } | |||
| } | |||
| // initial values | |||
| fShmControl.writeOpcode(kPluginBridgeOpcodeNull); | |||
| fShmControl.writeInt(static_cast<int32_t>(sizeof(BridgeShmControl))); | |||
| @@ -1787,9 +1906,10 @@ public: | |||
| // init OSC | |||
| { | |||
| char shmIdStr[12+1] = { 0 }; | |||
| char shmIdStr[16+1] = { 0 }; | |||
| std::strncpy(shmIdStr, &fShmAudioPool.filename[fShmAudioPool.filename.length()-6], 6); | |||
| std::strncat(shmIdStr, &fShmControl.filename[fShmControl.filename.length()-6], 6); | |||
| std::strncat(shmIdStr, &fShmTime.filename[fShmTime.filename.length()-6], 6); | |||
| pData->osc.thread.setOscData(bridgeBinary, label, getPluginTypeAsString(fPluginType), shmIdStr); | |||
| pData->osc.thread.start(); | |||
| @@ -1858,6 +1978,7 @@ private: | |||
| BridgeAudioPool fShmAudioPool; | |||
| BridgeControl fShmControl; | |||
| BridgeTime fShmTime; | |||
| struct Info { | |||
| uint32_t aIns, aOuts; | |||
| @@ -527,12 +527,13 @@ bool carla_engine_init(const char* driverName, const char* clientName) | |||
| } | |||
| #ifdef BUILD_BRIDGE | |||
| bool carla_engine_init_bridge(const char audioBaseName[6+1], const char controlBaseName[6+1], const char* clientName) | |||
| bool carla_engine_init_bridge(const char audioBaseName[6+1], const char controlBaseName[6+1], const char timeBaseName[6+1], const char* clientName) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(audioBaseName != nullptr && audioBaseName[0] != '\0', false); | |||
| CARLA_SAFE_ASSERT_RETURN(controlBaseName != nullptr && controlBaseName[0] != '\0', false); | |||
| CARLA_SAFE_ASSERT_RETURN(timeBaseName != nullptr && timeBaseName[0] != '\0', false); | |||
| CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false); | |||
| carla_debug("carla_engine_init_bridge(\"%s\", \"%s\", \"%s\")", audioBaseName, controlBaseName, clientName); | |||
| carla_debug("carla_engine_init_bridge(\"%s\", \"%s\", \"%s\", \"%s\")", audioBaseName, controlBaseName, timeBaseName, clientName); | |||
| if (gStandalone.engine != nullptr) | |||
| { | |||
| @@ -541,7 +542,7 @@ bool carla_engine_init_bridge(const char audioBaseName[6+1], const char controlB | |||
| return false; | |||
| } | |||
| gStandalone.engine = CarlaEngine::newBridge(audioBaseName, controlBaseName); | |||
| gStandalone.engine = CarlaEngine::newBridge(audioBaseName, controlBaseName, timeBaseName); | |||
| if (gStandalone.engine == nullptr) | |||
| { | |||
| @@ -157,18 +157,18 @@ CARLA_BRIDGE_START_NAMESPACE | |||
| class CarlaPluginClient : public CarlaBridgeClient | |||
| { | |||
| public: | |||
| CarlaPluginClient(const bool useBridge, const char* const clientName, const char* audioBaseName, const char* controlBaseName) | |||
| CarlaPluginClient(const bool useBridge, const char* const clientName, const char* const audioBaseName, const char* const controlBaseName, const char* const timeBaseName) | |||
| : CarlaBridgeClient(nullptr), | |||
| fPlugin(nullptr), | |||
| fEngine(nullptr) | |||
| { | |||
| CARLA_ASSERT(clientName != nullptr && clientName[0] != '\0'); | |||
| carla_debug("CarlaPluginClient::CarlaPluginClient(%s, \"%s\", %s, %s)", bool2str(useBridge), clientName, audioBaseName, controlBaseName); | |||
| carla_debug("CarlaPluginClient::CarlaPluginClient(%s, \"%s\", %s, %s, %s)", bool2str(useBridge), clientName, audioBaseName, controlBaseName, timeBaseName); | |||
| carla_set_engine_callback(callback, this); | |||
| if (useBridge) | |||
| carla_engine_init_bridge(audioBaseName, controlBaseName, clientName); | |||
| carla_engine_init_bridge(audioBaseName, controlBaseName, timeBaseName, clientName); | |||
| else | |||
| carla_engine_init("JACK", clientName); | |||
| @@ -585,19 +585,23 @@ int main(int argc, char* argv[]) | |||
| char bridgeBaseAudioName[6+1]; | |||
| char bridgeBaseControlName[6+1]; | |||
| char bridgeBaseTimeName[6+1]; | |||
| if (useBridge) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(std::strlen(shmIds) == 6*2, 1); | |||
| std::strncpy(bridgeBaseAudioName, shmIds, 6); | |||
| std::strncpy(bridgeBaseControlName, shmIds+6, 6); | |||
| CARLA_SAFE_ASSERT_RETURN(std::strlen(shmIds) == 6*3, 1); | |||
| std::strncpy(bridgeBaseAudioName, shmIds, 6); | |||
| std::strncpy(bridgeBaseControlName, shmIds+6, 6); | |||
| std::strncpy(bridgeBaseTimeName, shmIds+12, 6); | |||
| bridgeBaseAudioName[6] = '\0'; | |||
| bridgeBaseControlName[6] = '\0'; | |||
| bridgeBaseTimeName[6] = '\0'; | |||
| } | |||
| else | |||
| { | |||
| bridgeBaseAudioName[0] = '\0'; | |||
| bridgeBaseControlName[0] = '\0'; | |||
| bridgeBaseTimeName[0] = '\0'; | |||
| } | |||
| // --------------------------------------------------------------------- | |||
| @@ -636,7 +640,7 @@ int main(int argc, char* argv[]) | |||
| // --------------------------------------------------------------------- | |||
| // Init plugin client | |||
| CarlaPluginClient client(useBridge, clientName, bridgeBaseAudioName, bridgeBaseControlName); | |||
| CarlaPluginClient client(useBridge, clientName, bridgeBaseAudioName, bridgeBaseControlName, bridgeBaseTimeName); | |||
| if (! client.isOk()) | |||
| { | |||
| @@ -18,7 +18,8 @@ | |||
| #ifndef CARLA_BRIDGE_UTILS_HPP_INCLUDED | |||
| #define CARLA_BRIDGE_UTILS_HPP_INCLUDED | |||
| #include "CarlaBackend.h" | |||
| //#include "CarlaBackend.h" | |||
| #include "CarlaEngine.hpp" | |||
| #include "CarlaRingBuffer.hpp" | |||
| // ----------------------------------------------------------------------- | |||
| @@ -74,6 +75,20 @@ const char* const CARLA_BRIDGE_MSG_SET_CUSTOM = "CarlaBridgeSetCustom"; //!< Hos | |||
| // ----------------------------------------------------------------------- | |||
| PRE_PACKED_STRUCTURE | |||
| struct BridgeTimeInfo { | |||
| bool playing; | |||
| uint64_t frame; | |||
| uint64_t usecs; | |||
| uint valid; | |||
| // bbt | |||
| int32_t bar, beat, tick; | |||
| float beatsPerBar, beatType; | |||
| double barStartTick, ticksPerBeat, beatsPerMinute; | |||
| } POST_PACKED_STRUCTURE; | |||
| // ----------------------------------------------------------------------- | |||
| PRE_PACKED_STRUCTURE | |||
| struct BridgeShmControl { | |||
| union { | |||