|  | /*
 * Carla Backend
 * 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.h"
#include "carla_plugin.h"
CARLA_BACKEND_START_NAMESPACE
// -------------------------------------------------------------------------------------------------------------------
// static RtAudio<->Engine calls
static int carla_rtaudio_process_callback(void* outputBuffer, void* inputBuffer, unsigned int nframes, double streamTime, RtAudioStreamStatus status, void* userData)
{
    CarlaEngineRtAudio* const engine = (CarlaEngineRtAudio*)userData;
    engine->handleProcessCallback(outputBuffer, inputBuffer, nframes, streamTime, status);
    return 0;
}
// -------------------------------------------------------------------------------------------------------------------
// Carla Engine (RtAudio)
CarlaEngineRtAudio::CarlaEngineRtAudio(RtAudio::Api api)
    : CarlaEngine(),
      adac(api)
{
    qDebug("CarlaEngineRtAudio::CarlaEngineRtAudio()");
    type = CarlaEngineTypeRtAudio;
}
CarlaEngineRtAudio::~CarlaEngineRtAudio()
{
    qDebug("CarlaEngineRtAudio::~CarlaEngineRtAudio()");
}
bool CarlaEngineRtAudio::init(const char* const clientName)
{
    qDebug("CarlaEngineRtAudio::init(\"%s\")", clientName);
    if (adac.getDeviceCount() < 1)
    {
        setLastError("No audio devices available");
        return false;
    }
    sampleRate = 48000;
    unsigned int rtBufferFrames = 512;
    RtAudio::StreamParameters iParams, oParams;
    //iParams.deviceId = 3;
    //oParams.deviceId = 2;
    iParams.nChannels = 2;
    oParams.nChannels = 2;
    RtAudio::StreamOptions options;
    options.flags = /*RTAUDIO_NONINTERLEAVED |*/ RTAUDIO_MINIMIZE_LATENCY /*| RTAUDIO_HOG_DEVICE*/ | RTAUDIO_SCHEDULE_REALTIME | RTAUDIO_ALSA_USE_DEFAULT;
    options.streamName = clientName;
    options.priority = 85;
    try {
        adac.openStream(&oParams, &iParams, RTAUDIO_FLOAT32, sampleRate, &rtBufferFrames, carla_rtaudio_process_callback, this, &options);
    }
    catch (RtError& e)
    {
        setLastError(e.what());
        return false;
    }
    bufferSize = rtBufferFrames;
    // set client name, fixed for OSC usage
    // FIXME - put this in shared?
    char* fixedName = strdup(clientName);
    for (size_t i=0; i < strlen(fixedName); i++)
    {
        if (! (std::isalpha(fixedName[i]) || std::isdigit(fixedName[i])))
            fixedName[i] = '_';
    }
    name = strdup(fixedName);
    free((void*)fixedName);
    qDebug("RtAudio bufferSize = %i", bufferSize);
    try {
        adac.startStream();
    }
    catch (RtError& e)
    {
        setLastError(e.what());
        return false;
    }
    CarlaEngine::init(name);
    return true;
}
bool CarlaEngineRtAudio::close()
{
    qDebug("CarlaEngineRtAudio::close()");
    CarlaEngine::close();
    if (name)
    {
        free((void*)name);
        name = nullptr;
    }
    if (adac.isStreamRunning())
        adac.stopStream();
    if (adac.isStreamOpen())
        adac.closeStream();
    return true;
}
bool CarlaEngineRtAudio::isOffline()
{
    return false;
}
bool CarlaEngineRtAudio::isRunning()
{
    return adac.isStreamRunning();
}
CarlaEngineClient* CarlaEngineRtAudio::addClient(CarlaPlugin* const plugin)
{
    CarlaEngineClientNativeHandle handle;
    handle.type = type;
//    unsigned int rtBufferFrames = getBufferSize();
//    RtAudio::StreamParameters iParams, oParams;
//    iParams.nChannels = plugin->audioInCount();
//    oParams.nChannels = plugin->audioOutCount();
//    RtAudio::StreamOptions options;
//    options.flags = /*RTAUDIO_NONINTERLEAVED |*/ RTAUDIO_MINIMIZE_LATENCY /*| RTAUDIO_HOG_DEVICE*/ | RTAUDIO_SCHEDULE_REALTIME | RTAUDIO_ALSA_USE_DEFAULT;
//    options.streamName = plugin->name();
//    options.priority = 85;
//    try {
//        adac.openStream(&oParams, &iParams, RTAUDIO_FLOAT32, getSampleRate(), &rtBufferFrames, carla_rtaudio_process_callback, this, &options);
//    }
//    catch (RtError& e)
//    {
//        setLastError(e.what());
//        return false;
//    }
    return new CarlaEngineClient(handle);
    Q_UNUSED(plugin);
}
// -------------------------------------------------------------------------------------------------------------------
void CarlaEngineRtAudio::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);
    // create temporary audio buffers
    float ains_tmp_buf1[nframes];
    float ains_tmp_buf2[nframes];
    float aouts_tmp_buf1[nframes];
    float aouts_tmp_buf2[nframes];
    float* ains_tmp[2]  = { ains_tmp_buf1, ains_tmp_buf2 };
    float* aouts_tmp[2] = { aouts_tmp_buf1, aouts_tmp_buf2 };
    // initialize audio input
    for (unsigned int i=0; i < nframes*2; i++)
    {
        if (i % 2)
            ains_tmp_buf2[i/2] = insPtr[i];
        else
            ains_tmp_buf1[i/2] = insPtr[i];
    }
    // initialize control input
    memset(rackControlEventsIn, 0, sizeof(CarlaEngineControlEvent)*MAX_ENGINE_CONTROL_EVENTS);
    {
        // TODO
    }
    // initialize midi input
    memset(rackMidiEventsIn, 0, sizeof(CarlaEngineMidiEvent)*MAX_ENGINE_MIDI_EVENTS);
    {
        // TODO
    }
    // initialize outputs (zero)
    zeroF(aouts_tmp_buf1, nframes);
    zeroF(aouts_tmp_buf2, nframes);
    memset(rackControlEventsOut, 0, sizeof(CarlaEngineControlEvent)*MAX_ENGINE_CONTROL_EVENTS);
    memset(rackMidiEventsOut, 0, sizeof(CarlaEngineMidiEvent)*MAX_ENGINE_MIDI_EVENTS);
    bool processed = false;
    // process plugins
    for (unsigned short i=0, max=maxPluginNumber(); i < max; i++)
    {
        CarlaPlugin* const plugin = getPluginUnchecked(i);
        if (plugin && plugin->enabled())
        {
            if (processed)
            {
                // initialize inputs (from previous outputs)
                memcpy(ains_tmp_buf1, aouts_tmp_buf1, sizeof(float)*nframes);
                memcpy(ains_tmp_buf2, aouts_tmp_buf2, sizeof(float)*nframes);
                memcpy(rackMidiEventsIn, rackMidiEventsOut, sizeof(CarlaEngineMidiEvent)*MAX_ENGINE_MIDI_EVENTS);
                // initialize outputs (zero)
                zeroF(aouts_tmp_buf1, nframes);
                zeroF(aouts_tmp_buf2, nframes);
                memset(rackMidiEventsOut, 0, sizeof(CarlaEngineMidiEvent)*MAX_ENGINE_MIDI_EVENTS);
            }
            // process
            plugin->engineProcessLock();
            plugin->initBuffers();
            if (carlaOptions.processHighPrecision)
            {
                float* ains_buffer2[2];
                float* aouts_buffer2[2];
                for (uint32_t j=0; j < nframes; j += 8)
                {
                    ains_buffer2[0] = ains_tmp_buf1 + j;
                    ains_buffer2[1] = ains_tmp_buf2 + j;
                    aouts_buffer2[0] = aouts_tmp_buf1 + j;
                    aouts_buffer2[1] = aouts_tmp_buf2 + j;
                    plugin->process(ains_buffer2, aouts_buffer2, 8, j);
                }
            }
            else
                plugin->process(ains_tmp, aouts_tmp, nframes);
            plugin->engineProcessUnlock();
            // if plugin has no audio inputs, add previous buffers
            if (plugin->audioInCount() == 0)
            {
                for (uint32_t j=0; j < nframes; j++)
                {
                    aouts_tmp_buf1[j] += ains_tmp_buf1[j];
                    aouts_tmp_buf2[j] += ains_tmp_buf2[j];
                }
            }
            processed = true;
        }
    }
    // if no plugins in the rack, copy inputs over outputs
    if (! processed)
    {
        memcpy(aouts_tmp_buf1, ains_tmp_buf1, sizeof(float)*nframes);
        memcpy(aouts_tmp_buf2, ains_tmp_buf2, sizeof(float)*nframes);
        memcpy(rackMidiEventsOut, rackMidiEventsIn, sizeof(CarlaEngineMidiEvent)*MAX_ENGINE_MIDI_EVENTS);
    }
    // output audio
    for (unsigned int i=0; i < nframes*2; i++)
    {
        if (i % 2)
            outsPtr[i] = aouts_tmp_buf2[i/2];
        else
            outsPtr[i] = aouts_tmp_buf1[i/2];
    }
    // output control
    {
        // TODO
    }
    // output midi
    {
        // TODO
    }
    Q_UNUSED(streamTime);
    Q_UNUSED(status);
}
CARLA_BACKEND_END_NAMESPACE
#endif // CARLA_ENGINE_RTAUDIO
 |