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.

450 lines
13KB

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