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.

278 lines
7.8KB

  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 "distrho/extra/Time.hpp"
  7. CARLA_BACKEND_START_NAMESPACE
  8. // -------------------------------------------------------------------------------------------------------------------
  9. // Dummy Engine
  10. class CarlaEngineDummy : public CarlaEngine,
  11. public CarlaThread
  12. {
  13. public:
  14. CarlaEngineDummy()
  15. : CarlaEngine(),
  16. CarlaThread("CarlaEngineDummy"),
  17. fRunning(false)
  18. {
  19. carla_debug("CarlaEngineDummy::CarlaEngineDummy()");
  20. // just to make sure
  21. pData->options.transportMode = ENGINE_TRANSPORT_MODE_INTERNAL;
  22. }
  23. ~CarlaEngineDummy() override
  24. {
  25. carla_debug("CarlaEngineDummy::~CarlaEngineDummy()");
  26. }
  27. // -------------------------------------
  28. bool init(const char* const clientName) override
  29. {
  30. CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false);
  31. carla_debug("CarlaEngineDummy::init(\"%s\")", clientName);
  32. if (pData->options.processMode != ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
  33. {
  34. setLastError("Invalid process mode");
  35. return false;
  36. }
  37. fRunning = true;
  38. if (! pData->init(clientName))
  39. {
  40. close();
  41. setLastError("Failed to init internal data");
  42. return false;
  43. }
  44. pData->bufferSize = pData->options.audioBufferSize;
  45. pData->sampleRate = pData->options.audioSampleRate;
  46. pData->initTime(pData->options.transportExtra);
  47. pData->graph.create(2, 2, 0, 0);
  48. if (! startThread())
  49. {
  50. close();
  51. setLastError("Failed to start dummy audio thread");
  52. return false;
  53. }
  54. patchbayRefresh(true, false, false);
  55. callback(true, true,
  56. ENGINE_CALLBACK_ENGINE_STARTED,
  57. 0,
  58. pData->options.processMode,
  59. pData->options.transportMode,
  60. static_cast<int>(pData->bufferSize),
  61. static_cast<float>(pData->sampleRate),
  62. getCurrentDriverName());
  63. return true;
  64. }
  65. bool close() override
  66. {
  67. carla_debug("CarlaEngineDummy::close()");
  68. fRunning = false;
  69. stopThread(-1);
  70. CarlaEngine::close();
  71. pData->graph.destroy();
  72. return true;
  73. }
  74. bool hasIdleOnMainThread() const noexcept override
  75. {
  76. return true;
  77. }
  78. bool isRunning() const noexcept override
  79. {
  80. return fRunning;
  81. }
  82. bool isOffline() const noexcept override
  83. {
  84. return false;
  85. }
  86. EngineType getType() const noexcept override
  87. {
  88. return kEngineTypeDummy;
  89. }
  90. const char* getCurrentDriverName() const noexcept override
  91. {
  92. return "Dummy";
  93. }
  94. // -------------------------------------------------------------------
  95. // Patchbay
  96. bool patchbayRefresh(const bool sendHost, const bool sendOSC, const bool) override
  97. {
  98. CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false);
  99. RackGraph* const graph = pData->graph.getRackGraph();
  100. CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
  101. ExternalGraph& extGraph(graph->extGraph);
  102. // ---------------------------------------------------------------
  103. // clear last ports
  104. extGraph.clear();
  105. // ---------------------------------------------------------------
  106. // fill in new ones
  107. {
  108. PortNameToId portNameToId;
  109. portNameToId.setData(kExternalGraphGroupAudioIn, 1, "capture_1", "");
  110. extGraph.audioPorts.ins.append(portNameToId);
  111. }
  112. {
  113. PortNameToId portNameToId;
  114. portNameToId.setData(kExternalGraphGroupAudioIn, 2, "capture_2", "");
  115. extGraph.audioPorts.ins.append(portNameToId);
  116. }
  117. {
  118. PortNameToId portNameToId;
  119. portNameToId.setData(kExternalGraphGroupAudioOut, 1, "playback_1", "");
  120. extGraph.audioPorts.outs.append(portNameToId);
  121. }
  122. {
  123. PortNameToId portNameToId;
  124. portNameToId.setData(kExternalGraphGroupAudioOut, 2, "playback_2", "");
  125. extGraph.audioPorts.outs.append(portNameToId);
  126. }
  127. // ---------------------------------------------------------------
  128. // now refresh
  129. if (sendHost || sendOSC)
  130. graph->refresh(sendHost, sendOSC, false, "Dummy");
  131. return true;
  132. }
  133. // -------------------------------------------------------------------
  134. protected:
  135. void run() override
  136. {
  137. const uint32_t bufferSize = pData->bufferSize;
  138. const int64_t cycleTime = static_cast<int64_t>(
  139. static_cast<double>(bufferSize) / pData->sampleRate * 1000000 + 0.5);
  140. int delay = 0;
  141. if (const char* const delaystr = std::getenv("CARLA_BRIDGE_DUMMY"))
  142. if ((delay = atoi(delaystr)) == 1)
  143. delay = 0;
  144. carla_stdout("CarlaEngineDummy audio thread started, cycle time: " P_INT64 "ms, delay %ds",
  145. cycleTime / 1000, delay);
  146. float* audioIns[2] = {
  147. (float*)std::malloc(sizeof(float)*bufferSize),
  148. (float*)std::malloc(sizeof(float)*bufferSize),
  149. };
  150. CARLA_SAFE_ASSERT_RETURN(audioIns[0] != nullptr,);
  151. CARLA_SAFE_ASSERT_RETURN(audioIns[1] != nullptr,);
  152. float* audioOuts[2] = {
  153. (float*)std::malloc(sizeof(float)*bufferSize),
  154. (float*)std::malloc(sizeof(float)*bufferSize),
  155. };
  156. CARLA_SAFE_ASSERT_RETURN(audioOuts[0] != nullptr,);
  157. CARLA_SAFE_ASSERT_RETURN(audioOuts[1] != nullptr,);
  158. carla_zeroFloats(audioIns[0], bufferSize);
  159. carla_zeroFloats(audioIns[1], bufferSize);
  160. carla_zeroStructs(pData->events.in, kMaxEngineEventInternalCount);
  161. int64_t oldTime, newTime;
  162. while (! shouldThreadExit())
  163. {
  164. if (delay > 0)
  165. d_sleep(static_cast<uint>(delay));
  166. oldTime = d_gettime_us();
  167. const PendingRtEventsRunner prt(this, bufferSize, true);
  168. carla_zeroFloats(audioOuts[0], bufferSize);
  169. carla_zeroFloats(audioOuts[1], bufferSize);
  170. carla_zeroStructs(pData->events.out, kMaxEngineEventInternalCount);
  171. pData->graph.process(pData, audioIns, audioOuts, bufferSize);
  172. newTime = d_gettime_us();
  173. CARLA_SAFE_ASSERT_CONTINUE(newTime >= oldTime);
  174. const int64_t remainingTime = cycleTime - (newTime - oldTime);
  175. if (remainingTime <= 0)
  176. {
  177. ++pData->xruns;
  178. carla_stdout("XRUN! remaining time: " P_INT64 ", old: " P_INT64 ", new: " P_INT64 ")",
  179. remainingTime, oldTime, newTime);
  180. }
  181. else if (remainingTime >= 1000)
  182. {
  183. CARLA_SAFE_ASSERT_CONTINUE(remainingTime < 1000000); // 1 sec
  184. d_msleep(static_cast<uint>(remainingTime / 1000));
  185. }
  186. }
  187. std::free(audioIns[0]);
  188. std::free(audioIns[1]);
  189. std::free(audioOuts[0]);
  190. std::free(audioOuts[1]);
  191. carla_stdout("CarlaEngineDummy audio thread finished with %u Xruns", pData->xruns);
  192. }
  193. // -------------------------------------------------------------------
  194. private:
  195. bool fRunning;
  196. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineDummy)
  197. };
  198. // -----------------------------------------
  199. namespace EngineInit {
  200. CarlaEngine* newDummy()
  201. {
  202. carla_debug("EngineInit::newDummy()");
  203. return new CarlaEngineDummy();
  204. }
  205. }
  206. // -----------------------------------------
  207. CARLA_BACKEND_END_NAMESPACE