|  | /*
 * Carla RtAudio Engine
 * Copyright (C) 2012-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 GPL.txt file
 */
#ifdef WANT_RTAUDIO
#include "carla_engine_internal.hpp"
#include "carla_backend_utils.hpp"
#include "carla_midi.h"
#include "RtAudio.h"
#include "RtMidi.h"
CARLA_BACKEND_START_NAMESPACE
#if 0
} // Fix editor indentation
#endif
// -------------------------------------------------------------------------------------------------------------------
// RtAudio Engine
class CarlaEngineRtAudio : public CarlaEngine
{
public:
    CarlaEngineRtAudio(RtAudio::Api api)
        : CarlaEngine(),
          audio(api)
    {
        qDebug("CarlaEngineRtAudio::CarlaEngineRtAudio(%i)", api);
        evIn  = nullptr;
        evOut = nullptr;
        m_audioInterleaved = false;
        m_inBuf1  = nullptr;
        m_inBuf2  = nullptr;
        m_outBuf1 = nullptr;
        m_outBuf2 = nullptr;
        // just to make sure
        fOptions.forceStereo = true;
        fOptions.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;
        }
        fBufferSize = fOptions.preferredBufferSize;
        fSampleRate = fOptions.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, fSampleRate, &fBufferSize, 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;
        }
        fSampleRate = audio.getStreamSampleRate();
        m_inBuf1  = new float[fBufferSize];
        m_inBuf2  = new float[fBufferSize];
        m_outBuf1 = new float[fBufferSize];
        m_outBuf2 = new float[fBufferSize];
        //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");
        fName = clientName;
        fName.toBasic();
        CarlaEngine::init(fName);
        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 isRunning() const
    {
        return audio.isStreamRunning();
    }
    bool isOffline() const
    {
        return false;
    }
    EngineType type() const
    {
        return kEngineTypeRtAudio;
    }
    // -------------------------------------
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 events input
        //memset(rackEventsIn, 0, sizeof(EngineEvent)*MAX_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;
    ScopedPointer<MidiInApi>  evIn;
    ScopedPointer<MidiOutApi> evOut;
    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;
    }
    CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineRtAudio)
};
// -----------------------------------------
static std::vector<RtAudio::Api> rtApis;
static void initRtApis()
{
    static bool initiated = false;
    if (! initiated)
    {
        initiated = true;
        RtAudio::getCompiledApi(rtApis);
    }
}
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()
{
    initRtApis();
    return rtApis.size();
}
const char* CarlaEngine::getRtAudioApiName(unsigned int index)
{
    initRtApis();
    if (index < rtApis.size())
    {
        const RtAudio::Api& api(rtApis[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
#ifdef QTCREATOR_TEST
int main()
{
    return 0;
}
#endif
#endif // CARLA_ENGINE_RTAUDIO
 |