/* * Carla Engine Bridge * Copyright (C) 2013 Filipe Coelho * * 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" #include "jackbridge/jackbridge.h" #include #ifdef CARLA_OS_WIN # include #endif CARLA_BACKEND_START_NAMESPACE #if 0 } // Fix editor indentation #endif // ----------------------------------------- class CarlaEngineBridge : public CarlaEngine, public CarlaThread { public: CarlaEngineBridge(const char* const audioBaseName, const char* const controlBaseName) : CarlaEngine(), fIsRunning(false), fQuitNow(false) { carla_debug("CarlaEngineBridge::CarlaEngineBridge()"); fShmAudioPool.filename = "/carla-bridge_shm_"; fShmAudioPool.filename += audioBaseName; fShmControl.filename = "/carla-bridge_shc_"; fShmControl.filename += controlBaseName; } ~CarlaEngineBridge() { carla_debug("CarlaEngineBridge::~CarlaEngineBridge()"); } // ------------------------------------- // CarlaEngine virtual calls bool init(const char* const clientName) { carla_debug("CarlaEngineBridge::init(\"%s\")", clientName); // 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)) { _cleanup(); carla_stdout("Failed to open or create shared memory file #1"); return false; } } // 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)) { _cleanup(); carla_stdout("Failed to open or create shared memory file #2"); return false; } if (! carla_shm_map(fShmControl.shm, fShmControl.data)) { _cleanup(); carla_stdout("Failed to mmap shared memory file"); return false; } } // 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; } bool close() { carla_debug("CarlaEnginePlugin::close()"); CarlaEngine::close(); fQuitNow = true; CarlaThread::stop(); _cleanup(); return true; } bool isRunning() const { return fIsRunning; } bool isOffline() const { return false; } EngineType type() const { return kEngineTypeBridge; } // ------------------------------------- // CarlaThread virtual calls void run() { // TODO - set RT permissions const int timeout = 5000; while (! fQuitNow) { timespec ts_timeout; #ifdef CARLA_OS_WIN timeval now; gettimeofday(&now, nullptr); ts_timeout.tv_sec = now.tv_sec; ts_timeout.tv_nsec = now.tv_usec * 1000; #else clock_gettime(CLOCK_REALTIME, &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; } } while (rdwr_dataAvailable(&fShmControl.data->ringBuffer)) { 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)); 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; shm_t shm; BridgeAudioPool() : data(nullptr) { carla_shm_init(shm); } } fShmAudioPool; struct BridgeControl { CarlaString filename; BridgeShmControl* data; shm_t shm; BridgeControl() : data(nullptr) { carla_shm_init(shm); } } fShmControl; bool fIsRunning; bool fQuitNow; void _cleanup() { if (fShmAudioPool.filename.isNotEmpty()) fShmAudioPool.filename.clear(); if (fShmControl.filename.isNotEmpty()) fShmControl.filename.clear(); fShmAudioPool.data = nullptr; 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