| 
							- /*
 -  * 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 "CarlaEngineInternal.hpp"
 - #include "CarlaBackendUtils.hpp"
 - #include "CarlaMIDI.h"
 - #include "RtList.hpp"
 - 
 - #include "RtAudio.h"
 - #include "RtMidi.h"
 - 
 - CARLA_BACKEND_START_NAMESPACE
 - 
 - #if 0
 - } // Fix editor indentation
 - #endif
 - 
 - // -------------------------------------------------------------------------------------------------------------------
 - 
 - RtMidi::Api getMatchedAudioMidiAPi(const RtAudio::Api rtApi)
 - {
 -     switch (rtApi)
 -     {
 -     case RtAudio::UNSPECIFIED:
 -         return RtMidi::UNSPECIFIED;
 - 
 -     case RtAudio::LINUX_ALSA:
 -     case RtAudio::LINUX_OSS:
 -     case RtAudio::LINUX_PULSE:
 -         return RtMidi::LINUX_ALSA;
 - 
 -     case RtAudio::UNIX_JACK:
 -         return RtMidi::UNIX_JACK;
 - 
 -     case RtAudio::MACOSX_CORE:
 -         return RtMidi::MACOSX_CORE;
 - 
 -     case RtAudio::WINDOWS_ASIO:
 -     case RtAudio::WINDOWS_DS:
 -         return RtMidi::WINDOWS_MM;
 - 
 -     case RtAudio::RTAUDIO_DUMMY:
 -         return RtMidi::RTMIDI_DUMMY;
 -     }
 - 
 -     return RtMidi::UNSPECIFIED;
 - }
 - 
 - // -------------------------------------------------------------------------------------------------------------------
 - // RtAudio Engine
 - 
 - class CarlaEngineRtAudio : public CarlaEngine
 - {
 - public:
 -     CarlaEngineRtAudio(const RtAudio::Api api)
 -         : CarlaEngine(),
 -           fAudio(api),
 -           fAudioIsInterleaved(false),
 -           fAudioIsReady(false),
 -           fAudioInBuf1(nullptr),
 -           fAudioInBuf2(nullptr),
 -           fAudioOutBuf1(nullptr),
 -           fAudioOutBuf2(nullptr),
 -           fMidiIn(getMatchedAudioMidiAPi(api), "CarlaIn"),
 -           fMidiOut(getMatchedAudioMidiAPi(api), "CarlaOut")
 -     {
 -         carla_debug("CarlaEngineRtAudio::CarlaEngineRtAudio(%i)", api);
 - 
 -         // just to make sure
 -         fOptions.forceStereo = true;
 -         fOptions.processMode = PROCESS_MODE_CONTINUOUS_RACK;
 -     }
 - 
 -     ~CarlaEngineRtAudio()
 -     {
 -         carla_debug("CarlaEngineRtAudio::~CarlaEngineRtAudio()");
 -         CARLA_ASSERT(fAudioInBuf1 == nullptr);
 -         CARLA_ASSERT(fAudioInBuf2 == nullptr);
 -         CARLA_ASSERT(fAudioOutBuf1 == nullptr);
 -         CARLA_ASSERT(fAudioOutBuf2 == nullptr);
 -     }
 - 
 -     // -------------------------------------
 - 
 -     bool init(const char* const clientName)
 -     {
 -         carla_debug("CarlaEngineRtAudio::init(\"%s\")", clientName);
 -         CARLA_ASSERT(! fAudioIsReady);
 -         CARLA_ASSERT(fAudioInBuf1 == nullptr);
 -         CARLA_ASSERT(fAudioInBuf2 == nullptr);
 -         CARLA_ASSERT(fAudioOutBuf1 == nullptr);
 -         CARLA_ASSERT(fAudioOutBuf2 == nullptr);
 -         CARLA_ASSERT(clientName != nullptr);
 - 
 -         if (fAudio.getDeviceCount() == 0)
 -         {
 -             setLastError("No audio devices available for this driver");
 -             return false;
 -         }
 - 
 -         fBufferSize = fOptions.preferredBufferSize;
 - 
 -         // Audio
 -         {
 -             RtAudio::StreamParameters iParams, oParams;
 -             iParams.deviceId = fAudio.getDefaultInputDevice();
 -             oParams.deviceId = fAudio.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 (fAudio.getCurrentApi() != RtAudio::LINUX_PULSE)
 -             {
 -                 rtOptions.flags |= RTAUDIO_NONINTERLEAVED;
 -                 fAudioIsInterleaved = false;
 - 
 -                 if (fAudio.getCurrentApi() == RtAudio::LINUX_ALSA)
 -                     rtOptions.flags |= RTAUDIO_ALSA_USE_DEFAULT;
 -             }
 -             else
 -                 fAudioIsInterleaved = true;
 - 
 -             try {
 -                 fAudio.openStream(&oParams, &iParams, RTAUDIO_FLOAT32, fOptions.preferredSampleRate, &fBufferSize, carla_rtaudio_process_callback, this, &rtOptions);
 -             }
 -             catch (RtError& e)
 -             {
 -                 carla_stderr2("RtAudio::openStream() failed");
 -                 if (e.getType() == RtError::SYSTEM_ERROR)
 -                     setLastError("Stream cannot be opened with the specified parameters");
 -                 else if (e.getType() == RtError::INVALID_USE)
 -                     setLastError("Invalid device ID");
 -                 else
 -                     setLastError("Unknown error");
 -                 return false;
 -             }
 - 
 -             try {
 -                 fAudio.startStream();
 -             }
 -             catch (RtError& e)
 -             {
 -                 carla_stderr2("RtAudio::startStream() failed");
 -                 setLastError(e.what());
 -                 fAudio.closeStream();
 -                 return false;
 -             }
 - 
 -             fAudioInBuf1  = new float[fBufferSize];
 -             fAudioInBuf2  = new float[fBufferSize];
 -             fAudioOutBuf1 = new float[fBufferSize];
 -             fAudioOutBuf2 = new float[fBufferSize];
 - 
 -             fSampleRate = fAudio.getStreamSampleRate();
 -         }
 - 
 -         // MIDI
 -         {
 -             fMidiIn.setCallback(carla_rtmidi_callback, this);
 -             fMidiIn.openVirtualPort("events-in");
 -             fMidiOut.openVirtualPort("events-out");
 -         }
 - 
 -         fAudioIsReady = true;
 - 
 -         return CarlaEngine::init(clientName);
 -     }
 - 
 -     bool close()
 -     {
 -         carla_debug("CarlaEngineRtAudio::close()");
 -         CARLA_ASSERT(fAudioIsReady);
 -         CARLA_ASSERT(fAudioInBuf1 != nullptr);
 -         CARLA_ASSERT(fAudioInBuf2 != nullptr);
 -         CARLA_ASSERT(fAudioOutBuf1 != nullptr);
 -         CARLA_ASSERT(fAudioOutBuf2 != nullptr);
 - 
 -         CarlaEngine::close();
 - 
 -         fAudioIsReady = false;
 - 
 -         if (fAudio.isStreamRunning())
 -         {
 -             try {
 -                 fAudio.stopStream();
 -             }
 -             catch (...) {}
 -         }
 - 
 -         if (fAudio.isStreamOpen())
 -         {
 -             try {
 -                 fAudio.closeStream();
 -             }
 -             catch (...) {}
 -         }
 - 
 -         fMidiIn.cancelCallback();
 -         fMidiIn.closePort();
 -         fMidiOut.closePort();
 - 
 -         if (fAudioInBuf1 != nullptr)
 -         {
 -             delete[] fAudioInBuf1;
 -             fAudioInBuf1 = nullptr;
 -         }
 - 
 -         if (fAudioInBuf2 != nullptr)
 -         {
 -             delete[] fAudioInBuf2;
 -             fAudioInBuf2 = nullptr;
 -         }
 - 
 -         if (fAudioOutBuf1 != nullptr)
 -         {
 -             delete[] fAudioOutBuf1;
 -             fAudioOutBuf1 = nullptr;
 -         }
 - 
 -         if (fAudioOutBuf2 != nullptr)
 -         {
 -             delete[] fAudioOutBuf2;
 -             fAudioOutBuf2 = nullptr;
 -         }
 - 
 -         fMidiInEvents.clear();
 -         fMidiOutEvents.clear();
 - 
 -         return true;
 -     }
 - 
 -     bool isRunning() const
 -     {
 -         return fAudio.isStreamRunning();
 -     }
 - 
 -     bool isOffline() const
 -     {
 -         return false;
 -     }
 - 
 -     EngineType type() const
 -     {
 -         return kEngineTypeRtAudio;
 -     }
 - 
 -     // -------------------------------------
 - 
 - protected:
 -     void handleAudioProcessCallback(void* outputBuffer, void* inputBuffer, unsigned int nframes, double streamTime, RtAudioStreamStatus status)
 -     {
 -         // get buffers from RtAudio
 -         float* insPtr  = (float*)inputBuffer;
 -         float* outsPtr = (float*)outputBuffer;
 - 
 -         // assert buffers
 -         CARLA_ASSERT(insPtr != nullptr);
 -         CARLA_ASSERT(outsPtr != nullptr);
 - 
 -         if (! fAudioIsReady)
 -         {
 -             carla_zeroFloat(outsPtr, nframes*2);
 -             return proccessPendingEvents();
 -         }
 - 
 -         if (kData->curPluginCount == 0)
 -         {
 -             // pass-through
 -             if (fOptions.processMode == PROCESS_MODE_CONTINUOUS_RACK)
 -                 carla_copyFloat(outsPtr, insPtr, nframes*2);
 - 
 -             return proccessPendingEvents();
 -         }
 - 
 -         // initialize audio input
 -         if (fAudioIsInterleaved)
 -         {
 -             for (unsigned int i=0; i < nframes*2; i++)
 -             {
 -                 if (i % 2 == 0)
 -                     fAudioInBuf1[i/2] = insPtr[i];
 -                 else
 -                     fAudioInBuf2[i/2] = insPtr[i];
 -             }
 -         }
 -         else
 -         {
 -             carla_copyFloat(fAudioInBuf1, insPtr, nframes);
 -             carla_copyFloat(fAudioInBuf2, insPtr+nframes, nframes);
 -         }
 - 
 -         // initialize audio output
 -         carla_zeroFloat(fAudioOutBuf1, fBufferSize);
 -         carla_zeroFloat(fAudioOutBuf2, fBufferSize);
 - 
 -         // initialize input events
 -         carla_zeroMem(kData->rack.in, sizeof(EngineEvent)*RACK_EVENT_COUNT);
 - 
 -         if (fMidiInEvents.mutex.tryLock())
 -         {
 -             uint32_t engineEventIndex = 0;
 -             fMidiInEvents.splice();
 - 
 -             while (! fMidiInEvents.data.isEmpty())
 -             {
 -                 const RtMidiEvent& midiEvent = fMidiInEvents.data.getFirst(true);
 - 
 -                 EngineEvent* const engineEvent = &kData->rack.in[engineEventIndex++];
 -                 engineEvent->clear();
 - 
 -                 const uint8_t midiStatus  = MIDI_GET_STATUS_FROM_DATA(midiEvent.data);
 -                 const uint8_t midiChannel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent.data);
 - 
 -                 engineEvent->channel = midiChannel;
 - 
 -                 if (midiEvent.time < fTimeInfo.frame)
 -                     engineEvent->time = 0;
 -                 else if (midiEvent.time >= fTimeInfo.frame + nframes)
 -                 {
 -                     engineEvent->time = fTimeInfo.frame + nframes-1;
 -                     carla_stderr("MIDI Event in the future!, %i vs %i", engineEvent->time, fTimeInfo.frame);
 -                 }
 -                 else
 -                     engineEvent->time = midiEvent.time - fTimeInfo.frame;
 - 
 -                 //carla_stdout("Got midi, time %f vs %i", midiEvent.time, engineEvent->time);
 - 
 -                 if (MIDI_IS_STATUS_CONTROL_CHANGE(midiStatus))
 -                 {
 -                     const uint8_t midiControl = midiEvent.data[1];
 -                     engineEvent->type         = kEngineEventTypeControl;
 - 
 -                     if (MIDI_IS_CONTROL_BANK_SELECT(midiControl))
 -                     {
 -                         const uint8_t midiBank  = midiEvent.data[2];
 - 
 -                         engineEvent->ctrl.type  = kEngineControlEventTypeMidiBank;
 -                         engineEvent->ctrl.param = midiBank;
 -                         engineEvent->ctrl.value = 0.0;
 -                     }
 -                     else if (midiControl == MIDI_CONTROL_ALL_SOUND_OFF)
 -                     {
 -                         engineEvent->ctrl.type  = kEngineControlEventTypeAllSoundOff;
 -                         engineEvent->ctrl.param = 0;
 -                         engineEvent->ctrl.value = 0.0;
 -                     }
 -                     else if (midiControl == MIDI_CONTROL_ALL_NOTES_OFF)
 -                     {
 -                         engineEvent->ctrl.type  = kEngineControlEventTypeAllNotesOff;
 -                         engineEvent->ctrl.param = 0;
 -                         engineEvent->ctrl.value = 0.0;
 -                     }
 -                     else
 -                     {
 -                         const uint8_t midiValue = midiEvent.data[2];
 - 
 -                         engineEvent->ctrl.type  = kEngineControlEventTypeParameter;
 -                         engineEvent->ctrl.param = midiControl;
 -                         engineEvent->ctrl.value = double(midiValue)/127.0;
 -                     }
 -                 }
 -                 else if (MIDI_IS_STATUS_PROGRAM_CHANGE(midiStatus))
 -                 {
 -                     const uint8_t midiProgram = midiEvent.data[1];
 -                     engineEvent->type         = kEngineEventTypeControl;
 - 
 -                     engineEvent->ctrl.type  = kEngineControlEventTypeMidiProgram;
 -                     engineEvent->ctrl.param = midiProgram;
 -                     engineEvent->ctrl.value = 0.0;
 -                 }
 -                 else
 -                 {
 -                     engineEvent->type = kEngineEventTypeMidi;
 - 
 -                     engineEvent->midi.data[0] = midiStatus;
 -                     engineEvent->midi.data[1] = midiEvent.data[1];
 -                     engineEvent->midi.data[2] = midiEvent.data[2];
 -                     engineEvent->midi.size    = midiEvent.size;
 -                 }
 - 
 -                 if (engineEventIndex >= RACK_EVENT_COUNT)
 -                     break;
 -             }
 - 
 -             fMidiInEvents.mutex.unlock();
 -         }
 - 
 -         // create audio buffers
 -         float* inBuf[2]  = { fAudioInBuf1, fAudioInBuf2 };
 -         float* outBuf[2] = { fAudioOutBuf1, fAudioOutBuf2 };
 - 
 -         processRack(inBuf, outBuf, nframes);
 - 
 -         // output audio
 -         if (fAudioIsInterleaved)
 -         {
 -             for (unsigned int i=0; i < nframes*2; i++)
 -             {
 -                 if (i % 2 == 0)
 -                     outsPtr[i] = fAudioOutBuf1[i/2];
 -                 else
 -                     outsPtr[i] = fAudioOutBuf2[i/2];
 -             }
 -         }
 -         else
 -         {
 -             carla_copyFloat(outsPtr, fAudioOutBuf1, nframes);
 -             carla_copyFloat(outsPtr+nframes, fAudioOutBuf2, nframes);
 -         }
 - 
 -         // output events
 -         {
 -             // TODO
 -             //fMidiOut.sendMessage();
 -         }
 - 
 -         proccessPendingEvents();
 - 
 -         return;
 - 
 -         // unused
 -         (void)streamTime;
 -         (void)status;
 -     }
 - 
 -     void handleMidiCallback(double timeStamp, std::vector<unsigned char>* const message)
 -     {
 -         const size_t messageSize = message->size();
 -         static uint32_t lastTime = 0;
 - 
 -         if (messageSize == 0 || messageSize > 3)
 -             return;
 - 
 -         timeStamp /= 2;
 - 
 -         if (timeStamp > 0.95)
 -             timeStamp = 0.95;
 - 
 -         RtMidiEvent midiEvent;
 -         midiEvent.time = fTimeInfo.frame + (timeStamp*(double)fBufferSize);
 -         carla_stdout("Put midi, frame:%09i/%09i, stamp:%g", fTimeInfo.frame, midiEvent.time, timeStamp);
 - 
 -         if (midiEvent.time < lastTime)
 -             midiEvent.time = lastTime;
 -         else
 -             lastTime = midiEvent.time;
 - 
 -         if (messageSize == 1)
 -         {
 -             midiEvent.data[0] = message->at(0);
 -             midiEvent.data[1] = 0;
 -             midiEvent.data[2] = 0;
 -             midiEvent.size    = 1;
 -         }
 -         else if (messageSize == 2)
 -         {
 -             midiEvent.data[0] = message->at(0);
 -             midiEvent.data[1] = message->at(1);
 -             midiEvent.data[2] = 0;
 -             midiEvent.size    = 2;
 -         }
 -         else
 -         {
 -             midiEvent.data[0] = message->at(0);
 -             midiEvent.data[1] = message->at(1);
 -             midiEvent.data[2] = message->at(2);
 -             midiEvent.size    = 3;
 -         }
 - 
 -         fMidiInEvents.append(midiEvent);
 -     }
 - 
 -     // -------------------------------------
 - 
 - private:
 -     RtAudio fAudio;
 -     bool    fAudioIsInterleaved;
 -     bool    fAudioIsReady;
 -     float*  fAudioInBuf1;
 -     float*  fAudioInBuf2;
 -     float*  fAudioOutBuf1;
 -     float*  fAudioOutBuf2;
 - 
 -     RtMidiIn  fMidiIn;
 -     RtMidiOut fMidiOut;
 - 
 -     struct RtMidiEvent {
 -         uint32_t time;
 -         unsigned char data[3];
 -         unsigned char size;
 -     };
 - 
 -     struct RtMidiEvents {
 -         CarlaMutex mutex;
 -         RtList<RtMidiEvent>::Pool dataPool;
 -         RtList<RtMidiEvent> data;
 -         RtList<RtMidiEvent> dataPending;
 - 
 -         RtMidiEvents()
 -             : dataPool(512, 512),
 -               data(&dataPool),
 -               dataPending(&dataPool) {}
 - 
 -         ~RtMidiEvents()
 -         {
 -             clear();
 -         }
 - 
 -         void append(const RtMidiEvent& event)
 -         {
 -             mutex.lock();
 -             dataPending.append(event);
 -             mutex.unlock();
 -         }
 - 
 -         void clear()
 -         {
 -             mutex.lock();
 -             data.clear();
 -             dataPending.clear();
 -             mutex.unlock();
 -         }
 - 
 -         void splice()
 -         {
 -             dataPending.spliceAppend(data, true);
 -         }
 -     };
 - 
 -     RtMidiEvents fMidiInEvents;
 -     RtMidiEvents fMidiOutEvents;
 - 
 -     #define handlePtr ((CarlaEngineRtAudio*)userData)
 - 
 -     static int carla_rtaudio_process_callback(void* outputBuffer, void* inputBuffer, unsigned int nframes, double streamTime, RtAudioStreamStatus status, void* userData)
 -     {
 -         handlePtr->handleAudioProcessCallback(outputBuffer, inputBuffer, nframes, streamTime, status);
 -         return 0;
 -     }
 - 
 -     static void carla_rtmidi_callback(double timeStamp, std::vector<unsigned char>* message, void* userData)
 -     {
 -         handlePtr->handleMidiCallback(timeStamp, message);
 -     }
 - 
 -     #undef handlePtr
 - 
 -     CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineRtAudio)
 - };
 - 
 - // -----------------------------------------
 - 
 - static std::vector<RtAudio::Api> sRtAudioApis;
 - 
 - static void initRtApis()
 - {
 -     static bool initiated = false;
 - 
 -     if (! initiated)
 -     {
 -         initiated = true;
 - 
 -         RtAudio::getCompiledApi(sRtAudioApis);
 -     }
 - }
 - 
 - 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);
 - }
 - 
 - size_t CarlaEngine::getRtAudioApiCount()
 - {
 -     initRtApis();
 - 
 -     return sRtAudioApis.size();
 - }
 - 
 - const char* CarlaEngine::getRtAudioApiName(const unsigned int index)
 - {
 -     initRtApis();
 - 
 -     if (index < sRtAudioApis.size())
 -     {
 -         const RtAudio::Api& api(sRtAudioApis[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
 
 
  |