|  | /*
 * Carla Engine Bridge
 * 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 doc/GPL.txt file.
 */
#ifdef BUILD_BRIDGE
#include "CarlaEngineInternal.hpp"
#include "CarlaBackendUtils.hpp"
#include "CarlaMIDI.h"
#include "CarlaBridgeUtils.hpp"
#include "CarlaShmUtils.hpp"
#include "jackbridge/JackBridge.hpp"
#include <cerrno>
#include <ctime>
CARLA_BACKEND_START_NAMESPACE
#if 0
} // Fix editor indentation
#endif
// -------------------------------------------------------------------
struct BridgeAudioPool {
    CarlaString filename;
    float* data;
    shm_t shm;
    BridgeAudioPool()
        : data(nullptr)
    {
        carla_shm_init(shm);
    }
    ~BridgeAudioPool()
    {
        // should be cleared by now
        CARLA_ASSERT(data == nullptr);
        clear();
    }
    bool attach()
    {
#ifdef CARLA_OS_WIN
        // TESTING!
        shm = carla_shm_attach_linux((const char*)filename);
#else
        shm = carla_shm_attach((const char*)filename);
#endif
        return carla_is_shm_valid(shm);
    }
    void clear()
    {
        filename.clear();
        data = nullptr;
        if (carla_is_shm_valid(shm))
            carla_shm_close(shm);
    }
};
struct BridgeControl : public RingBufferControl {
    CarlaString filename;
    BridgeShmControl* data;
    shm_t shm;
    BridgeControl()
        : RingBufferControl(nullptr),
          data(nullptr)
    {
        carla_shm_init(shm);
    }
    ~BridgeControl()
    {
        // should be cleared by now
        CARLA_ASSERT(data == nullptr);
        clear();
    }
    bool attach()
    {
#ifdef CARLA_OS_WIN
        // TESTING!
        shm = carla_shm_attach_linux((const char*)filename);
#else
        shm = carla_shm_attach((const char*)filename);
#endif
        return carla_is_shm_valid(shm);
    }
    void clear()
    {
        filename.clear();
        data = nullptr;
        if (carla_is_shm_valid(shm))
            carla_shm_close(shm);
    }
    bool mapData()
    {
        CARLA_ASSERT(data == nullptr);
        if (carla_shm_map<BridgeShmControl>(shm, data))
        {
            setRingBuffer(&data->ringBuffer, false);
            return true;
        }
        return false;
    }
    PluginBridgeOpcode readOpcode()
    {
        return static_cast<PluginBridgeOpcode>(readInt());
    }
};
// -------------------------------------------------------------------
class CarlaEngineBridge : public CarlaEngine,
                          public juce::Thread
{
public:
    CarlaEngineBridge(const char* const audioBaseName, const char* const controlBaseName)
        : CarlaEngine(),
          juce::Thread("CarlaEngineBridge"),
          fIsRunning(false)
    {
        carla_debug("CarlaEngineBridge::CarlaEngineBridge()");
        fShmAudioPool.filename  = "/carla-bridge_shm_";
        fShmAudioPool.filename += audioBaseName;
        fShmControl.filename    = "/carla-bridge_shc_";
        fShmControl.filename   += controlBaseName;
    }
    ~CarlaEngineBridge() override
    {
        carla_debug("CarlaEngineBridge::~CarlaEngineBridge()");
    }
    // -------------------------------------
    // CarlaEngine virtual calls
    bool init(const char* const clientName) override
    {
        carla_debug("CarlaEngineBridge::init(\"%s\")", clientName);
        // SHM Audio Pool
        {
            if (! fShmAudioPool.attach())
            {
                carla_stdout("Failed to open or create shared memory file #1");
                return false;
            }
        }
        // SHM Control
        {
            if (! fShmControl.attach())
            {
                carla_stdout("Failed to open or create shared memory file #2");
                // clear
                fShmAudioPool.clear();
                return false;
            }
            if (! fShmControl.mapData())
            {
                carla_stdout("Failed to mmap shared memory file");
                // clear
                fShmControl.clear();
                fShmAudioPool.clear();
                return false;
            }
        }
        // Read values from memory
        PluginBridgeOpcode opcode;
        opcode = fShmControl.readOpcode();
        CARLA_ASSERT_INT(opcode == kPluginBridgeOpcodeSetBufferSize, opcode);
        fBufferSize = fShmControl.readInt();
        carla_stderr("BufferSize: %i", fBufferSize);
        opcode = fShmControl.readOpcode();
        CARLA_ASSERT_INT(opcode == kPluginBridgeOpcodeSetSampleRate, opcode);
        fSampleRate = fShmControl.readFloat();
        carla_stderr("SampleRate: %f", fSampleRate);
        fIsRunning = true;
        startThread(10);
        CarlaEngine::init(clientName);
        return true;
    }
    bool close() override
    {
        carla_debug("CarlaEnginePlugin::close()");
        CarlaEngine::close();
        stopThread(6000);
        fShmControl.clear();
        fShmAudioPool.clear();
        return true;
    }
    bool isRunning() const noexcept override
    {
        return fIsRunning;
    }
    bool isOffline() const noexcept override
    {
        return false;
    }
    EngineType getType() const noexcept override
    {
        return kEngineTypeBridge;
    }
    // -------------------------------------
    // CarlaThread virtual calls
    void run() override
    {
        // TODO - set RT permissions
        carla_debug("CarlaEngineBridge::run()");
        while (! threadShouldExit())
        {
            if (! jackbridge_sem_timedwait(&fShmControl.data->runServer, 5))
            {
                if (errno == ETIMEDOUT)
                {
                    fIsRunning = false;
                    signalThreadShouldExit();
                    return;
                }
            }
            while (fShmControl.dataAvailable())
            {
                const PluginBridgeOpcode opcode(fShmControl.readOpcode());
                if (opcode != kPluginBridgeOpcodeProcess)
                    carla_debug("CarlaEngineBridge::run() - got opcode: %s", PluginBridgeOpcode2str(opcode));
                switch (opcode)
                {
                case kPluginBridgeOpcodeNull:
                    break;
                case kPluginBridgeOpcodeSetAudioPool:
                {
                    const long poolSize(fShmControl.readLong());
                    fShmAudioPool.data = (float*)carla_shm_map(fShmAudioPool.shm, poolSize);
                    break;
                }
                case kPluginBridgeOpcodeSetBufferSize:
                {
                    const int bufferSize(fShmControl.readInt());
                    bufferSizeChanged(bufferSize);
                    break;
                }
                case kPluginBridgeOpcodeSetSampleRate:
                {
                    const float sampleRate(fShmControl.readFloat());
                    sampleRateChanged(sampleRate);
                    break;
                }
                case kPluginBridgeOpcodeSetParameter:
                {
                    const int   index(fShmControl.readInt());
                    const float value(fShmControl.readFloat());
                    CarlaPlugin* const plugin(getPluginUnchecked(0));
                    if (plugin != nullptr && plugin->isEnabled())
                    {
                        //plugin->setParameterValueByRealIndex(index, value, false, false, false);
                        //plugin->postponeRtEvent(kPluginPostRtEventParameterChange, index, 0, value);
                    }
                    break;
                }
                case kPluginBridgeOpcodeSetProgram:
                {
                    const int index(fShmControl.readInt());
                    CarlaPlugin* const plugin(getPluginUnchecked(0));
                    if (plugin != nullptr && plugin->isEnabled())
                    {
                        //plugin->setProgram(index, false, false, false);
                        //plugin->postponeRtEvent(kPluginPostRtEventProgramChange, index, 0, 0.0f);
                    }
                    break;
                }
                case kPluginBridgeOpcodeSetMidiProgram:
                {
                    const int index(fShmControl.readInt());
                    CarlaPlugin* const plugin(getPluginUnchecked(0));
                    if (plugin != nullptr && plugin->isEnabled())
                    {
                        //plugin->setMidiProgram(index, false, false, false);
                        //plugin->postponeRtEvent(kPluginPostRtEventMidiProgramChange, index, 0, 0.0f);
                    }
                    break;
                }
                case kPluginBridgeOpcodeMidiEvent:
                {
                    uint8_t data[4] = { 0 };
                    const long time(fShmControl.readLong());
                    const int  dataSize(fShmControl.readInt());
                    CARLA_ASSERT_INT(dataSize >= 1 && dataSize <= 4, dataSize);
                    for (int i=0; i < dataSize && i < 4; ++i)
                        data[i] = fShmControl.readChar();
                    CARLA_ASSERT(pData->bufEvents.in != nullptr);
                    if (pData->bufEvents.in != nullptr)
                    {
                        // TODO
                    }
                    break;
                }
                case kPluginBridgeOpcodeProcess:
                {
                    CARLA_ASSERT(fShmAudioPool.data != nullptr);
                    CarlaPlugin* const plugin(getPluginUnchecked(0));
                    if (plugin != nullptr && plugin->isEnabled() && plugin->tryLock())
                    {
                        const uint32_t inCount(plugin->getAudioInCount());
                        const uint32_t outCount(plugin->getAudioOutCount());
                        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->process(inBuffer, outBuffer, fBufferSize);
                        plugin->unlock();
                    }
                    break;
                }
                case kPluginBridgeOpcodeQuit:
                    signalThreadShouldExit();
                    break;
                }
            }
            if (jackbridge_sem_post(&fShmControl.data->runClient) != 0)
                pass(); //carla_stderr2("Could not post to semaphore");
        }
        fIsRunning = false;
    }
private:
    BridgeAudioPool fShmAudioPool;
    BridgeControl   fShmControl;
    volatile bool fIsRunning;
    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
 |