|  | /*
 * Carla Engine
 * Copyright (C) 2012 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 COPYING file
 */
#ifdef CARLA_ENGINE_RTAUDIO
#include "carla_engine.hpp"
#include "carla_plugin.hpp"
#include "RtAudio.h"
#include "RtMidi.h"
CARLA_BACKEND_START_NAMESPACE
// -------------------------------------------------------------------------------------------------------------------
// RtAudio Engine client
class CarlaEngineRtAudioClient : public CarlaEngineClient
{
public:
    CarlaEngineRtAudioClient(const CarlaEngineType engineType, const ProcessMode processMode)
        : CarlaEngineClient(engineType, processMode)
    {
    }
    ~CarlaEngineRtAudioClient()
    {
    }
    const CarlaEngineBasePort* addPort(const CarlaEnginePortType portType, const char* const name, const bool isInput)
    {
        qDebug("CarlaEngineRtAudioClient::addPort(%i, \"%s\", %s)", portType, name, bool2str(isInput));
        switch (portType)
        {
        case CarlaEnginePortTypeNull:
            break;
        case CarlaEnginePortTypeAudio:
            return new CarlaEngineAudioPort(isInput, processMode);
        case CarlaEnginePortTypeControl:
            return new CarlaEngineControlPort(isInput, processMode);
        case CarlaEnginePortTypeMIDI:
            return new CarlaEngineMidiPort(isInput, processMode);
        }
        qCritical("CarlaEngineRtAudioClient::addPort(%i, \"%s\", %s) - invalid type", portType, name, bool2str(isInput));
        return nullptr;
    }
};
// -------------------------------------------------------------------------------------------------------------------
// RtAudio Engine
class CarlaEngineRtAudio : public CarlaEngine
{
public:
    CarlaEngineRtAudio(RtAudio::Api api)
        : CarlaEngine(),
          audio(api)
    {
        qDebug("CarlaEngineRtAudio::CarlaEngineRtAudio()");
        midiIn  = nullptr;
        midiOut = nullptr;
        m_audioInterleaved = false;
        m_inBuf1  = nullptr;
        m_inBuf2  = nullptr;
        m_outBuf1 = nullptr;
        m_outBuf2 = nullptr;
        // just to make sure
        options.forceStereo = true;
        options.processMode = PROCESS_MODE_CONTINUOUS_RACK;
    }
    ~CarlaEngineRtAudio()
    {
        qDebug("CarlaEngineRtAudio::~CarlaEngineRtAudio()");
    }
    // -------------------------------------
    bool init(const char* const clientName)
    {
        qDebug("CarlaEngineRtAudio::init(\"%s\")", clientName);
        if (audio.getDeviceCount() < 1)
        {
            setLastError("No audio devices available for this driver");
            return false;
        }
        bufferSize = options.preferredBufferSize;
        sampleRate = options.preferredSampleRate;
        RtAudio::StreamParameters iParams, oParams;
        iParams.deviceId = audio.getDefaultInputDevice();
        oParams.deviceId = audio.getDefaultOutputDevice();
        iParams.nChannels = 2;
        oParams.nChannels = 2;
        RtAudio::StreamOptions rtOptions;
        rtOptions.flags = RTAUDIO_MINIMIZE_LATENCY | RTAUDIO_HOG_DEVICE | RTAUDIO_SCHEDULE_REALTIME;
        rtOptions.numberOfBuffers = 2;
        rtOptions.streamName = clientName;
        rtOptions.priority = 85;
        if (audio.getCurrentApi() != RtAudio::LINUX_PULSE)
        {
            rtOptions.flags |= RTAUDIO_NONINTERLEAVED;
            m_audioInterleaved = false;
            if (audio.getCurrentApi() == RtAudio::LINUX_ALSA)
                rtOptions.flags |= RTAUDIO_ALSA_USE_DEFAULT;
        }
        else
            m_audioInterleaved = true;
        try {
            audio.openStream(&oParams, &iParams, RTAUDIO_FLOAT32, sampleRate, &bufferSize, carla_rtaudio_process_callback, this, &rtOptions);
        }
        catch (RtError& e)
        {
            setLastError(e.what());
            return false;
        }
        try {
            audio.startStream();
        }
        catch (RtError& e)
        {
            setLastError(e.what());
            return false;
        }
        sampleRate = audio.getStreamSampleRate();
        m_inBuf1  = new float [bufferSize];
        m_inBuf2  = new float [bufferSize];
        m_outBuf1 = new float [bufferSize];
        m_outBuf2 = new float [bufferSize];
        //midiIn = new MidiInAlsa(clientName, 512);
        //midiIn->openVirtualPort("control-in");
        //midiIn->openVirtualPort("midi-in");
        //midiOut = new MidiOutAlsa(clientName);
        //midiOut->openVirtualPort("control-out");
        //midiOut->openVirtualPort("midi-out");
        name = clientName;
        name.toBasic();
        CarlaEngine::init(name);
        return true;
    }
    bool close()
    {
        qDebug("CarlaEngineRtAudio::close()");
        CarlaEngine::close();
        if (audio.isStreamRunning())
            audio.stopStream();
        if (audio.isStreamOpen())
            audio.closeStream();
#if 0
        if (midiIn)
        {
            midiIn->cancelCallback();
            midiIn->closePort();
            delete midiIn;
            midiIn = nullptr;
        }
        if (midiOut)
        {
            midiOut->closePort();
            delete midiOut;
            midiOut = nullptr;
        }
#endif
        if (m_inBuf1)
        {
            delete[] m_inBuf1;
            m_inBuf1 = nullptr;
        }
        if (m_inBuf2)
        {
            delete[] m_inBuf2;
            m_inBuf2 = nullptr;
        }
        if (m_outBuf1)
        {
            delete[] m_outBuf1;
            m_outBuf1 = nullptr;
        }
        if (m_outBuf2)
        {
            delete[] m_outBuf2;
            m_outBuf2 = nullptr;
        }
        return true;
    }
    bool isOffline() const
    {
        return false;
    }
    bool isRunning() const
    {
        return audio.isStreamRunning();
    }
    CarlaEngineType type() const
    {
        return CarlaEngineTypeRtAudio;
    }
    CarlaEngineClient* addClient(CarlaPlugin* const)
    {
        return new CarlaEngineRtAudioClient(CarlaEngineTypeRtAudio, options.processMode);
    }
    // -------------------------------------
protected:
    void handleProcessCallback(void* outputBuffer, void* inputBuffer, unsigned int nframes, double streamTime, RtAudioStreamStatus status)
    {
        if (maxPluginNumber() == 0)
            return;
        // get buffers from RtAudio
        float* insPtr  = (float*)inputBuffer;
        float* outsPtr = (float*)outputBuffer;
        // assert buffers
        CARLA_ASSERT(insPtr);
        CARLA_ASSERT(outsPtr);
        // initialize audio input
        if (m_audioInterleaved)
        {
            for (unsigned int i=0; i < nframes*2; i++)
            {
                if (i % 2)
                    m_inBuf2[i/2] = insPtr[i];
                else
                    m_inBuf1[i/2] = insPtr[i];
            }
        }
        else
        {
            for (unsigned int i=0; i < nframes; i++)
                m_inBuf1[i] = insPtr[i];
            for (unsigned int i=0, j=nframes; i < nframes; i++, j++)
                m_inBuf2[i] = insPtr[j];
        }
        // create audio buffers
        float* inBuf[2]  = { m_inBuf1, m_inBuf2 };
        float* outBuf[2] = { m_outBuf1, m_outBuf2 };
        // initialize control input
        memset(rackControlEventsIn, 0, sizeof(CarlaEngineControlEvent)*MAX_CONTROL_EVENTS);
        {
            // TODO
        }
        // initialize midi input
        memset(rackMidiEventsIn, 0, sizeof(CarlaEngineMidiEvent)*MAX_MIDI_EVENTS);
        {
            // TODO
        }
        processRack(inBuf, outBuf, nframes);
        // output audio
        if (m_audioInterleaved)
        {
            for (unsigned int i=0; i < nframes*2; i++)
            {
                if (i % 2)
                    outsPtr[i] = m_outBuf2[i/2];
                else
                    outsPtr[i] = m_outBuf1[i/2];
            }
        }
        else
        {
            for (unsigned int i=0; i < nframes; i++)
                outsPtr[i] = m_outBuf1[i];
            for (unsigned int i=0, j=nframes; i < nframes; i++, j++)
                outsPtr[j] = m_outBuf2[i];
        }
        // output control
        {
            // TODO
        }
        // output midi
        {
            // TODO
        }
        Q_UNUSED(streamTime);
        Q_UNUSED(status);
    }
    // -------------------------------------
private:
    RtAudio audio;
    MidiInApi*  midiIn;
    MidiOutApi* midiOut;
    bool   m_audioInterleaved;
    float* m_inBuf1;
    float* m_inBuf2;
    float* m_outBuf1;
    float* m_outBuf2;
    static int carla_rtaudio_process_callback(void* outputBuffer, void* inputBuffer, unsigned int nframes, double streamTime, RtAudioStreamStatus status, void* userData)
    {
        CarlaEngineRtAudio* const _this_ = (CarlaEngineRtAudio*)userData;
        _this_->handleProcessCallback(outputBuffer, inputBuffer, nframes, streamTime, status);
        return 0;
    }
};
// -----------------------------------------
CarlaEngine* CarlaEngine::newRtAudio(RtAudioApi api)
{
    RtAudio::Api rtApi = RtAudio::UNSPECIFIED;
    switch (api)
    {
    case RTAUDIO_DUMMY:
        rtApi = RtAudio::RTAUDIO_DUMMY;
        break;
    case RTAUDIO_LINUX_ALSA:
        rtApi = RtAudio::LINUX_ALSA;
        break;
    case RTAUDIO_LINUX_PULSE:
        rtApi = RtAudio::LINUX_PULSE;
        break;
    case RTAUDIO_LINUX_OSS:
        rtApi = RtAudio::LINUX_OSS;
        break;
    case RTAUDIO_UNIX_JACK:
        rtApi = RtAudio::UNIX_JACK;
        break;
    case RTAUDIO_MACOSX_CORE:
        rtApi = RtAudio::MACOSX_CORE;
        break;
    case RTAUDIO_WINDOWS_ASIO:
        rtApi = RtAudio::WINDOWS_ASIO;
        break;
    case RTAUDIO_WINDOWS_DS:
        rtApi = RtAudio::WINDOWS_DS;
        break;
    }
    return new CarlaEngineRtAudio(rtApi);
}
unsigned int CarlaEngine::getRtAudioApiCount()
{
    std::vector<RtAudio::Api> apis;
    RtAudio::getCompiledApi(apis);
    return apis.size();
}
const char* CarlaEngine::getRtAudioApiName(unsigned int index)
{
    std::vector<RtAudio::Api> apis;
    RtAudio::getCompiledApi(apis);
    if (index < apis.size())
    {
        const RtAudio::Api& api(apis[index]);
        switch (api)
        {
        case RtAudio::UNSPECIFIED:
            return "Unspecified";
        case RtAudio::LINUX_ALSA:
            return "ALSA";
        case RtAudio::LINUX_PULSE:
            return "PulseAudio";
        case RtAudio::LINUX_OSS:
            return "OSS";
        case RtAudio::UNIX_JACK:
            return "JACK (RtAudio)";
        case RtAudio::MACOSX_CORE:
            return "CoreAudio";
        case RtAudio::WINDOWS_ASIO:
            return "ASIO";
        case RtAudio::WINDOWS_DS:
            return "DirectSound";
        case RtAudio::RTAUDIO_DUMMY:
            return "Dummy";
        }
    }
    return nullptr;
}
CARLA_BACKEND_END_NAMESPACE
#endif // CARLA_ENGINE_RTAUDIO
 |