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 14KB

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