Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CarlaEngineSDL.cpp 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. /*
  2. * Carla Plugin Host
  3. * Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #include "CarlaEngineGraph.hpp"
  18. #include "CarlaEngineInit.hpp"
  19. #include "CarlaEngineInternal.hpp"
  20. #include "CarlaStringList.hpp"
  21. #include "CarlaBackendUtils.hpp"
  22. #include <SDL.h>
  23. #ifndef HAVE_SDL2
  24. typedef Uint32 SDL_AudioDeviceID;
  25. #endif
  26. CARLA_BACKEND_START_NAMESPACE
  27. // -------------------------------------------------------------------------------------------------------------------
  28. // Global static data
  29. static CarlaStringList gDeviceNames;
  30. // -------------------------------------------------------------------------------------------------------------------
  31. static void initAudioDevicesIfNeeded()
  32. {
  33. static bool needsInit = true;
  34. if (! needsInit)
  35. return;
  36. needsInit = false;
  37. #ifdef HAVE_SDL2
  38. SDL_InitSubSystem(SDL_INIT_AUDIO);
  39. const int numDevices = SDL_GetNumAudioDevices(0);
  40. for (int i=0; i<numDevices; ++i)
  41. gDeviceNames.append(SDL_GetAudioDeviceName(i, 0));
  42. #else
  43. SDL_Init(SDL_INIT_AUDIO);
  44. #endif
  45. }
  46. // -------------------------------------------------------------------------------------------------------------------
  47. // RtAudio Engine
  48. class CarlaEngineSDL : public CarlaEngine
  49. {
  50. public:
  51. CarlaEngineSDL()
  52. : CarlaEngine(),
  53. fDeviceId(0),
  54. fDeviceName(),
  55. fAudioOutCount(0),
  56. fAudioIntBufOut(nullptr)
  57. {
  58. carla_debug("CarlaEngineSDL::CarlaEngineSDL()");
  59. // just to make sure
  60. pData->options.transportMode = ENGINE_TRANSPORT_MODE_INTERNAL;
  61. }
  62. ~CarlaEngineSDL() override
  63. {
  64. CARLA_SAFE_ASSERT(fAudioOutCount == 0);
  65. carla_debug("CarlaEngineSDL::~CarlaEngineSDL()");
  66. }
  67. // -------------------------------------
  68. bool init(const char* const clientName) override
  69. {
  70. CARLA_SAFE_ASSERT_RETURN(fDeviceId == 0, false);
  71. CARLA_SAFE_ASSERT_RETURN(fAudioOutCount == 0, false);
  72. CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false);
  73. carla_debug("CarlaEngineSDL::init(\"%s\")", clientName);
  74. if (pData->options.processMode != ENGINE_PROCESS_MODE_CONTINUOUS_RACK && pData->options.processMode != ENGINE_PROCESS_MODE_PATCHBAY)
  75. {
  76. setLastError("Invalid process mode");
  77. return false;
  78. }
  79. SDL_AudioSpec requested, received;
  80. carla_zeroStruct(requested);
  81. #ifdef HAVE_SDL2
  82. requested.format = AUDIO_F32SYS;
  83. #else
  84. requested.format = AUDIO_S16SYS;
  85. #endif
  86. requested.channels = 2;
  87. requested.freq = static_cast<int>(pData->options.audioSampleRate);
  88. requested.samples = static_cast<Uint16>(pData->options.audioBufferSize);
  89. requested.callback = carla_sdl_process_callback;
  90. requested.userdata = this;
  91. #ifdef HAVE_SDL2
  92. SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, clientName);
  93. // SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, );
  94. SDL_SetHint(SDL_HINT_AUDIO_RESAMPLING_MODE, "2");
  95. const char* const deviceName = pData->options.audioDevice != nullptr && pData->options.audioDevice[0] != '\0'
  96. ? pData->options.audioDevice
  97. : nullptr;
  98. int flags = SDL_AUDIO_ALLOW_FREQUENCY_CHANGE;
  99. if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
  100. flags |= SDL_AUDIO_ALLOW_CHANNELS_CHANGE;
  101. fDeviceId = SDL_OpenAudioDevice(deviceName, 0, &requested, &received, flags);
  102. #else
  103. fDeviceId = SDL_OpenAudio(&requested, &received) == 0 ? 1 : 0;
  104. #endif
  105. if (fDeviceId == 0)
  106. {
  107. setLastError(SDL_GetError());
  108. return false;
  109. }
  110. if (received.channels == 0)
  111. {
  112. #ifdef HAVE_SDL2
  113. SDL_CloseAudioDevice(fDeviceId);
  114. #else
  115. SDL_CloseAudio();
  116. #endif
  117. fDeviceId = 0;
  118. setLastError("No output channels available");
  119. return false;
  120. }
  121. if (! pData->init(clientName))
  122. {
  123. close();
  124. setLastError("Failed to init internal data");
  125. return false;
  126. }
  127. pData->bufferSize = received.samples;
  128. pData->sampleRate = received.freq;
  129. pData->initTime(pData->options.transportExtra);
  130. fAudioOutCount = received.channels;
  131. fAudioIntBufOut = new float*[fAudioOutCount];
  132. for (uint i=0; i<fAudioOutCount; ++i)
  133. fAudioIntBufOut[i] = new float[received.samples];
  134. pData->graph.create(0, fAudioOutCount, 0, 0);
  135. #ifdef HAVE_SDL2
  136. SDL_PauseAudioDevice(fDeviceId, 0);
  137. #else
  138. SDL_PauseAudio(0);
  139. #endif
  140. carla_stdout("open fAudioOutCount %d %d %d | %d vs %d",
  141. fAudioOutCount, received.samples, received.freq,
  142. received.format, requested.format);
  143. patchbayRefresh(true, false, false);
  144. if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
  145. refreshExternalGraphPorts<PatchbayGraph>(pData->graph.getPatchbayGraph(), false, false);
  146. callback(true, true,
  147. ENGINE_CALLBACK_ENGINE_STARTED,
  148. 0,
  149. pData->options.processMode,
  150. pData->options.transportMode,
  151. static_cast<int>(pData->bufferSize),
  152. static_cast<float>(pData->sampleRate),
  153. getCurrentDriverName());
  154. return true;
  155. }
  156. bool close() override
  157. {
  158. carla_debug("CarlaEngineSDL::close()");
  159. // close device
  160. if (fDeviceId != 0)
  161. {
  162. // SDL_PauseAudioDevice(fDeviceId, 1);
  163. #ifdef HAVE_SDL2
  164. SDL_CloseAudioDevice(fDeviceId);
  165. #else
  166. SDL_CloseAudio();
  167. #endif
  168. fDeviceId = 0;
  169. }
  170. // clear engine data
  171. CarlaEngine::close();
  172. pData->graph.destroy();
  173. // cleanup
  174. if (fAudioIntBufOut != nullptr)
  175. {
  176. for (uint i=0; i<fAudioOutCount; ++i)
  177. delete[] fAudioIntBufOut[i];
  178. delete[] fAudioIntBufOut;
  179. fAudioIntBufOut = nullptr;
  180. }
  181. fAudioOutCount = 0;
  182. fDeviceName.clear();
  183. return false;
  184. }
  185. bool isRunning() const noexcept override
  186. {
  187. return fDeviceId != 0 /*&& SDL_GetAudioDeviceStatus(fDeviceId) == SDL_AUDIO_PLAYING*/;
  188. }
  189. bool isOffline() const noexcept override
  190. {
  191. return false;
  192. }
  193. EngineType getType() const noexcept override
  194. {
  195. return kEngineTypeSDL;
  196. }
  197. const char* getCurrentDriverName() const noexcept override
  198. {
  199. return "SDL";
  200. }
  201. // -------------------------------------------------------------------
  202. // Patchbay
  203. template<class Graph>
  204. bool refreshExternalGraphPorts(Graph* const graph, const bool sendHost, const bool sendOSC)
  205. {
  206. CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
  207. char strBuf[STR_MAX+1U];
  208. strBuf[STR_MAX] = '\0';
  209. ExternalGraph& extGraph(graph->extGraph);
  210. // ---------------------------------------------------------------
  211. // clear last ports
  212. extGraph.clear();
  213. // ---------------------------------------------------------------
  214. // fill in new ones
  215. // Audio Out
  216. for (uint i=0; i < fAudioOutCount; ++i)
  217. {
  218. std::snprintf(strBuf, STR_MAX, "playback_%i", i+1);
  219. PortNameToId portNameToId;
  220. portNameToId.setData(kExternalGraphGroupAudioOut, i+1, strBuf, "");
  221. extGraph.audioPorts.outs.append(portNameToId);
  222. }
  223. // ---------------------------------------------------------------
  224. // now refresh
  225. if (sendHost || sendOSC)
  226. graph->refresh(sendHost, sendOSC, true, fDeviceName.buffer());
  227. return true;
  228. }
  229. bool patchbayRefresh(const bool sendHost, const bool sendOSC, const bool external) override
  230. {
  231. CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false);
  232. if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
  233. return refreshExternalGraphPorts<RackGraph>(pData->graph.getRackGraph(), sendHost, sendOSC);
  234. if (sendHost)
  235. pData->graph.setUsingExternalHost(external);
  236. if (sendOSC)
  237. pData->graph.setUsingExternalOSC(external);
  238. if (external)
  239. return refreshExternalGraphPorts<PatchbayGraph>(pData->graph.getPatchbayGraph(), sendHost, sendOSC);
  240. return CarlaEngine::patchbayRefresh(sendHost, sendOSC, false);
  241. }
  242. // -------------------------------------------------------------------
  243. protected:
  244. void handleAudioProcessCallback(uchar* const stream, const int len)
  245. {
  246. // safety checks
  247. CARLA_SAFE_ASSERT_RETURN(stream != nullptr,);
  248. CARLA_SAFE_ASSERT_RETURN(len > 0,);
  249. #ifdef HAVE_SDL2
  250. // direct float type
  251. float* const fstream = (float*)stream;
  252. const uint ulen = static_cast<uint>(static_cast<uint>(len) / sizeof(float) / fAudioOutCount);
  253. #else
  254. // signed 16bit int
  255. int16_t* const istream = (int16_t*)stream;
  256. const uint ulen = static_cast<uint>(static_cast<uint>(len) / sizeof(int16_t) / fAudioOutCount);
  257. #endif
  258. const PendingRtEventsRunner prt(this, ulen, true);
  259. // init our deinterleaved audio buffers
  260. for (uint i=0, count=fAudioOutCount; i<count; ++i)
  261. carla_zeroFloats(fAudioIntBufOut[i], ulen);
  262. // initialize events
  263. carla_zeroStructs(pData->events.in, kMaxEngineEventInternalCount);
  264. carla_zeroStructs(pData->events.out, kMaxEngineEventInternalCount);
  265. pData->graph.process(pData, nullptr, fAudioIntBufOut, ulen);
  266. // interleave audio back
  267. for (uint i=0; i < fAudioOutCount; ++i)
  268. {
  269. for (uint j=0; j < ulen; ++j)
  270. {
  271. #ifdef HAVE_SDL2
  272. // direct float type
  273. fstream[j * fAudioOutCount + i] = fAudioIntBufOut[i][j];
  274. #else
  275. // signed 16bit int
  276. istream[j * fAudioOutCount + i] = lrintf(carla_fixedValue(-1.0f, 1.0f, fAudioIntBufOut[i][j]) * 32767.0f);
  277. #endif
  278. }
  279. }
  280. }
  281. // -------------------------------------------------------------------
  282. bool connectExternalGraphPort(const uint connectionType, const uint portId, const char* const portName) override
  283. {
  284. CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
  285. carla_debug("CarlaEngineSDL::connectExternalGraphPort(%u, %u, \"%s\")", connectionType, portId, portName);
  286. switch (connectionType)
  287. {
  288. case kExternalGraphConnectionAudioIn1:
  289. case kExternalGraphConnectionAudioIn2:
  290. case kExternalGraphConnectionAudioOut1:
  291. case kExternalGraphConnectionAudioOut2:
  292. return CarlaEngine::connectExternalGraphPort(connectionType, portId, portName);
  293. case kExternalGraphConnectionMidiInput:
  294. case kExternalGraphConnectionMidiOutput:
  295. break;
  296. }
  297. return false;
  298. }
  299. bool disconnectExternalGraphPort(const uint connectionType, const uint portId, const char* const portName) override
  300. {
  301. CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
  302. carla_debug("CarlaEngineSDL::disconnectExternalGraphPort(%u, %u, \"%s\")", connectionType, portId, portName);
  303. switch (connectionType)
  304. {
  305. case kExternalGraphConnectionAudioIn1:
  306. case kExternalGraphConnectionAudioIn2:
  307. case kExternalGraphConnectionAudioOut1:
  308. case kExternalGraphConnectionAudioOut2:
  309. return CarlaEngine::disconnectExternalGraphPort(connectionType, portId, portName);
  310. case kExternalGraphConnectionMidiInput:
  311. case kExternalGraphConnectionMidiOutput:
  312. break;
  313. }
  314. return false;
  315. }
  316. // -------------------------------------------------------------------
  317. private:
  318. SDL_AudioDeviceID fDeviceId;
  319. // current device name
  320. CarlaString fDeviceName;
  321. // deinterleaved buffers
  322. uint fAudioOutCount;
  323. float** fAudioIntBufOut;
  324. #define handlePtr ((CarlaEngineSDL*)userData)
  325. static void carla_sdl_process_callback(void* userData, uchar* stream, int len)
  326. {
  327. handlePtr->handleAudioProcessCallback(stream, len);
  328. }
  329. #undef handlePtr
  330. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineSDL)
  331. };
  332. // -----------------------------------------
  333. namespace EngineInit {
  334. CarlaEngine* newSDL()
  335. {
  336. initAudioDevicesIfNeeded();
  337. return new CarlaEngineSDL();
  338. }
  339. const char* const* getSDLDeviceNames()
  340. {
  341. initAudioDevicesIfNeeded();
  342. if (gDeviceNames.count() == 0)
  343. {
  344. static const char* deviceNames[] = { "Default", nullptr };
  345. return deviceNames;
  346. }
  347. static const CharStringListPtr deviceNames = gDeviceNames.toCharStringListPtr();
  348. return deviceNames;
  349. }
  350. }
  351. // -----------------------------------------
  352. CARLA_BACKEND_END_NAMESPACE