| 
							- /*
 -  * Carla Plugin Host
 -  * Copyright (C) 2011-2022 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.
 -  */
 - 
 - #include "CarlaEngineGraph.hpp"
 - #include "CarlaEngineInit.hpp"
 - #include "CarlaEngineInternal.hpp"
 - #include "CarlaStringList.hpp"
 - #include "CarlaBackendUtils.hpp"
 - 
 - #include <SDL.h>
 - 
 - #ifndef HAVE_SDL2
 - typedef Uint32 SDL_AudioDeviceID;
 - #endif
 - 
 - #ifndef SDL_HINT_AUDIO_DEVICE_APP_NAME
 - # define SDL_HINT_AUDIO_DEVICE_APP_NAME "SDL_AUDIO_DEVICE_APP_NAME"
 - #endif
 - 
 - #ifndef SDL_HINT_AUDIO_DEVICE_STREAM_NAME
 - # define SDL_HINT_AUDIO_DEVICE_STREAM_NAME "SDL_AUDIO_DEVICE_STREAM_NAME"
 - #endif
 - 
 - CARLA_BACKEND_START_NAMESPACE
 - 
 - // -------------------------------------------------------------------------------------------------------------------
 - // Global static data
 - 
 - static CarlaStringList gDeviceNames;
 - 
 - // -------------------------------------------------------------------------------------------------------------------
 - 
 - static void initAudioDevicesIfNeeded()
 - {
 -     static bool needsInit = true;
 - 
 -     if (! needsInit)
 -         return;
 - 
 -     needsInit = false;
 - 
 - #ifdef HAVE_SDL2
 -     SDL_InitSubSystem(SDL_INIT_AUDIO);
 - 
 -     const int numDevices = SDL_GetNumAudioDevices(0);
 - 
 -     for (int i=0; i<numDevices; ++i)
 -         gDeviceNames.append(SDL_GetAudioDeviceName(i, 0));
 - #else
 -     SDL_Init(SDL_INIT_AUDIO);
 - #endif
 - }
 - 
 - // -------------------------------------------------------------------------------------------------------------------
 - // RtAudio Engine
 - 
 - class CarlaEngineSDL : public CarlaEngine
 - {
 - public:
 -     CarlaEngineSDL()
 -         : CarlaEngine(),
 -           fDeviceId(0),
 -           fDeviceName(),
 -           fAudioOutCount(0),
 -           fAudioIntBufOut(nullptr)
 -     {
 -         carla_debug("CarlaEngineSDL::CarlaEngineSDL()");
 - 
 -         // just to make sure
 -         pData->options.transportMode = ENGINE_TRANSPORT_MODE_INTERNAL;
 -     }
 - 
 -     ~CarlaEngineSDL() override
 -     {
 -         CARLA_SAFE_ASSERT(fAudioOutCount == 0);
 -         carla_debug("CarlaEngineSDL::~CarlaEngineSDL()");
 -     }
 - 
 -     // -------------------------------------
 - 
 -     bool init(const char* const clientName) override
 -     {
 -         CARLA_SAFE_ASSERT_RETURN(fDeviceId == 0, false);
 -         CARLA_SAFE_ASSERT_RETURN(fAudioOutCount == 0, false);
 -         CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false);
 -         carla_debug("CarlaEngineSDL::init(\"%s\")", clientName);
 - 
 -         if (pData->options.processMode != ENGINE_PROCESS_MODE_CONTINUOUS_RACK && pData->options.processMode != ENGINE_PROCESS_MODE_PATCHBAY)
 -         {
 -             setLastError("Invalid process mode");
 -             return false;
 -         }
 - 
 -         SDL_AudioSpec requested, received;
 -         carla_zeroStruct(requested);
 - #ifdef HAVE_SDL2
 -         requested.format = AUDIO_F32SYS;
 - #else
 -         requested.format = AUDIO_S16SYS;
 - #endif
 -         requested.channels = 2;
 -         requested.freq = static_cast<int>(pData->options.audioSampleRate);
 -         requested.samples = static_cast<Uint16>(pData->options.audioBufferSize);
 -         requested.callback = carla_sdl_process_callback;
 -         requested.userdata = this;
 - 
 - #ifdef HAVE_SDL2
 -         SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, clientName);
 -         // SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, );
 -         SDL_SetHint(SDL_HINT_AUDIO_RESAMPLING_MODE, "2");
 - 
 -         const char* const deviceName = pData->options.audioDevice != nullptr && pData->options.audioDevice[0] != '\0'
 -                                      ? pData->options.audioDevice
 -                                      : nullptr;
 - 
 -         int flags = SDL_AUDIO_ALLOW_FREQUENCY_CHANGE;
 -         if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
 -             flags |= SDL_AUDIO_ALLOW_CHANNELS_CHANGE;
 - 
 -         fDeviceId = SDL_OpenAudioDevice(deviceName, 0, &requested, &received, flags);
 - #else
 -         fDeviceId = SDL_OpenAudio(&requested, &received) == 0 ? 1 : 0;
 - #endif
 - 
 -         if (fDeviceId == 0)
 -         {
 -             setLastError(SDL_GetError());
 -             return false;
 -         }
 - 
 -         if (received.channels == 0)
 -         {
 - #ifdef HAVE_SDL2
 -             SDL_CloseAudioDevice(fDeviceId);
 - #else
 -             SDL_CloseAudio();
 - #endif
 -             fDeviceId = 0;
 -             setLastError("No output channels available");
 -             return false;
 -         }
 - 
 -         if (! pData->init(clientName))
 -         {
 -             close();
 -             setLastError("Failed to init internal data");
 -             return false;
 -         }
 - 
 -         pData->bufferSize = received.samples;
 -         pData->sampleRate = received.freq;
 -         pData->initTime(pData->options.transportExtra);
 - 
 -         fAudioOutCount = received.channels;
 -         fAudioIntBufOut = new float*[fAudioOutCount];
 -         for (uint i=0; i<fAudioOutCount; ++i)
 -             fAudioIntBufOut[i] = new float[received.samples];
 - 
 -         pData->graph.create(0, fAudioOutCount, 0, 0);
 - 
 - #ifdef HAVE_SDL2
 -         SDL_PauseAudioDevice(fDeviceId, 0);
 - #else
 -         SDL_PauseAudio(0);
 - #endif
 -         carla_stdout("open fAudioOutCount %d %d %d | %d vs %d",
 -                      fAudioOutCount, received.samples, received.freq,
 -                      received.format, requested.format);
 - 
 -         patchbayRefresh(true, false, false);
 - 
 -         if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
 -             refreshExternalGraphPorts<PatchbayGraph>(pData->graph.getPatchbayGraph(), false, false);
 - 
 -         callback(true, true,
 -                  ENGINE_CALLBACK_ENGINE_STARTED,
 -                  0,
 -                  pData->options.processMode,
 -                  pData->options.transportMode,
 -                  static_cast<int>(pData->bufferSize),
 -                  static_cast<float>(pData->sampleRate),
 -                  getCurrentDriverName());
 -         return true;
 -     }
 - 
 -     bool close() override
 -     {
 -         carla_debug("CarlaEngineSDL::close()");
 - 
 -         // close device
 -         if (fDeviceId != 0)
 -         {
 -             // SDL_PauseAudioDevice(fDeviceId, 1);
 - #ifdef HAVE_SDL2
 -             SDL_CloseAudioDevice(fDeviceId);
 - #else
 -             SDL_CloseAudio();
 - #endif
 -             fDeviceId = 0;
 -         }
 - 
 -         // clear engine data
 -         CarlaEngine::close();
 - 
 -         pData->graph.destroy();
 - 
 -         // cleanup
 -         if (fAudioIntBufOut != nullptr)
 -         {
 -             for (uint i=0; i<fAudioOutCount; ++i)
 -                 delete[] fAudioIntBufOut[i];
 -             delete[] fAudioIntBufOut;
 -             fAudioIntBufOut = nullptr;
 -         }
 - 
 -         fAudioOutCount = 0;
 -         fDeviceName.clear();
 - 
 -         return false;
 -     }
 - 
 -     bool isRunning() const noexcept override
 -     {
 -         return fDeviceId != 0 /*&& SDL_GetAudioDeviceStatus(fDeviceId) == SDL_AUDIO_PLAYING*/;
 -     }
 - 
 -     bool isOffline() const noexcept override
 -     {
 -         return false;
 -     }
 - 
 -     EngineType getType() const noexcept override
 -     {
 -         return kEngineTypeSDL;
 -     }
 - 
 -     const char* getCurrentDriverName() const noexcept override
 -     {
 -         return "SDL";
 -     }
 - 
 -     // -------------------------------------------------------------------
 -     // Patchbay
 - 
 -     template<class Graph>
 -     bool refreshExternalGraphPorts(Graph* const graph, const bool sendHost, const bool sendOSC)
 -     {
 -         CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
 - 
 -         char strBuf[STR_MAX+1U];
 -         strBuf[STR_MAX] = '\0';
 - 
 -         ExternalGraph& extGraph(graph->extGraph);
 - 
 -         // ---------------------------------------------------------------
 -         // clear last ports
 - 
 -         extGraph.clear();
 - 
 -         // ---------------------------------------------------------------
 -         // fill in new ones
 - 
 -         // Audio Out
 -         for (uint i=0; i < fAudioOutCount; ++i)
 -         {
 -             std::snprintf(strBuf, STR_MAX, "playback_%i", i+1);
 - 
 -             PortNameToId portNameToId;
 -             portNameToId.setData(kExternalGraphGroupAudioOut, i+1, strBuf, "");
 - 
 -             extGraph.audioPorts.outs.append(portNameToId);
 -         }
 - 
 -         // ---------------------------------------------------------------
 -         // now refresh
 - 
 -         if (sendHost || sendOSC)
 -             graph->refresh(sendHost, sendOSC, true, fDeviceName.buffer());
 - 
 -         return true;
 -     }
 - 
 -     bool patchbayRefresh(const bool sendHost, const bool sendOSC, const bool external) override
 -     {
 -         CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false);
 - 
 -         if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
 -             return refreshExternalGraphPorts<RackGraph>(pData->graph.getRackGraph(), sendHost, sendOSC);
 - 
 -         if (sendHost)
 -             pData->graph.setUsingExternalHost(external);
 -         if (sendOSC)
 -             pData->graph.setUsingExternalOSC(external);
 - 
 -         if (external)
 -             return refreshExternalGraphPorts<PatchbayGraph>(pData->graph.getPatchbayGraph(), sendHost, sendOSC);
 - 
 -         return CarlaEngine::patchbayRefresh(sendHost, sendOSC, false);
 -     }
 - 
 -     // -------------------------------------------------------------------
 - 
 - protected:
 -     void handleAudioProcessCallback(uchar* const stream, const int len)
 -     {
 -         // safety checks
 -         CARLA_SAFE_ASSERT_RETURN(stream != nullptr,);
 -         CARLA_SAFE_ASSERT_RETURN(len > 0,);
 - 
 - #ifdef HAVE_SDL2
 -         // direct float type
 -         float* const fstream = (float*)stream;
 -         const uint ulen = static_cast<uint>(static_cast<uint>(len) / sizeof(float) / fAudioOutCount);
 - #else
 -         // signed 16bit int
 -         int16_t* const istream = (int16_t*)stream;
 -         const uint ulen = static_cast<uint>(static_cast<uint>(len) / sizeof(int16_t) / fAudioOutCount);
 - #endif
 - 
 -         const PendingRtEventsRunner prt(this, ulen, true);
 - 
 -         // init our deinterleaved audio buffers
 -         for (uint i=0, count=fAudioOutCount; i<count; ++i)
 -             carla_zeroFloats(fAudioIntBufOut[i], ulen);
 - 
 -         // initialize events
 -         carla_zeroStructs(pData->events.in,  kMaxEngineEventInternalCount);
 -         carla_zeroStructs(pData->events.out, kMaxEngineEventInternalCount);
 - 
 -         pData->graph.process(pData, nullptr, fAudioIntBufOut, ulen);
 - 
 -         // interleave audio back
 -         for (uint i=0; i < fAudioOutCount; ++i)
 -         {
 -             for (uint j=0; j < ulen; ++j)
 -             {
 - #ifdef HAVE_SDL2
 -                 // direct float type
 -                 fstream[j * fAudioOutCount + i] = fAudioIntBufOut[i][j];
 - #else
 -                 // signed 16bit int
 -                 istream[j * fAudioOutCount + i] = lrintf(carla_fixedValue(-1.0f, 1.0f, fAudioIntBufOut[i][j]) * 32767.0f);
 - #endif
 -             }
 -         }
 -     }
 - 
 -     // -------------------------------------------------------------------
 - 
 -     bool connectExternalGraphPort(const uint connectionType, const uint portId, const char* const portName) override
 -     {
 -         CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
 -         carla_debug("CarlaEngineSDL::connectExternalGraphPort(%u, %u, \"%s\")", connectionType, portId, portName);
 - 
 -         switch (connectionType)
 -         {
 -         case kExternalGraphConnectionAudioIn1:
 -         case kExternalGraphConnectionAudioIn2:
 -         case kExternalGraphConnectionAudioOut1:
 -         case kExternalGraphConnectionAudioOut2:
 -             return CarlaEngine::connectExternalGraphPort(connectionType, portId, portName);
 - 
 -         case kExternalGraphConnectionMidiInput:
 -         case kExternalGraphConnectionMidiOutput:
 -             break;
 -         }
 - 
 -         return false;
 -     }
 - 
 -     bool disconnectExternalGraphPort(const uint connectionType, const uint portId, const char* const portName) override
 -     {
 -         CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
 -         carla_debug("CarlaEngineSDL::disconnectExternalGraphPort(%u, %u, \"%s\")", connectionType, portId, portName);
 - 
 -         switch (connectionType)
 -         {
 -         case kExternalGraphConnectionAudioIn1:
 -         case kExternalGraphConnectionAudioIn2:
 -         case kExternalGraphConnectionAudioOut1:
 -         case kExternalGraphConnectionAudioOut2:
 -             return CarlaEngine::disconnectExternalGraphPort(connectionType, portId, portName);
 - 
 -         case kExternalGraphConnectionMidiInput:
 -         case kExternalGraphConnectionMidiOutput:
 -             break;
 -         }
 - 
 -         return false;
 -     }
 - 
 -     // -------------------------------------------------------------------
 - 
 - private:
 -     SDL_AudioDeviceID fDeviceId;
 - 
 -     // current device name
 -     CarlaString fDeviceName;
 - 
 -     // deinterleaved buffers
 -     uint fAudioOutCount;
 -     float** fAudioIntBufOut;
 - 
 -     #define handlePtr ((CarlaEngineSDL*)userData)
 - 
 -     static void carla_sdl_process_callback(void* userData, uchar* stream, int len)
 -     {
 -         handlePtr->handleAudioProcessCallback(stream, len);
 -     }
 - 
 -     #undef handlePtr
 - 
 -     CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineSDL)
 - };
 - 
 - // -----------------------------------------
 - 
 - namespace EngineInit {
 - 
 - CarlaEngine* newSDL()
 - {
 -     initAudioDevicesIfNeeded();
 -     return new CarlaEngineSDL();
 - }
 - 
 - const char* const* getSDLDeviceNames()
 - {
 -     initAudioDevicesIfNeeded();
 - 
 -     if (gDeviceNames.count() == 0)
 -     {
 -         static const char* deviceNames[] = { "Default", nullptr };
 -         return deviceNames;
 -     }
 - 
 -     static const CharStringListPtr deviceNames = gDeviceNames.toCharStringListPtr();
 -     return deviceNames;
 - }
 - 
 - }
 - 
 - // -----------------------------------------
 - 
 - CARLA_BACKEND_END_NAMESPACE
 
 
  |