CubebStream.cpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // Copyright 2017 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "AudioCommon/CubebStream.h"
  4. #include <cubeb/cubeb.h>
  5. #include "AudioCommon/CubebUtils.h"
  6. #include "Common/CommonTypes.h"
  7. #include "Common/Event.h"
  8. #include "Common/Logging/Log.h"
  9. #include "Common/ScopeGuard.h"
  10. #include "Common/Thread.h"
  11. #include "Core/Config/MainSettings.h"
  12. #ifdef _WIN32
  13. #include <Objbase.h>
  14. #endif
  15. // ~10 ms - needs to be at least 240 for surround
  16. constexpr u32 BUFFER_SAMPLES = 512;
  17. long CubebStream::DataCallback(cubeb_stream* stream, void* user_data, const void* /*input_buffer*/,
  18. void* output_buffer, long num_frames)
  19. {
  20. auto* self = static_cast<CubebStream*>(user_data);
  21. if (self->m_stereo)
  22. self->m_mixer->Mix(static_cast<short*>(output_buffer), num_frames);
  23. else
  24. self->m_mixer->MixSurround(static_cast<float*>(output_buffer), num_frames);
  25. return num_frames;
  26. }
  27. void CubebStream::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state)
  28. {
  29. }
  30. CubebStream::CubebStream()
  31. #ifdef _WIN32
  32. : m_work_queue("Cubeb Worker", [](const std::function<void()>& func) { func(); })
  33. {
  34. Common::Event sync_event;
  35. m_work_queue.EmplaceItem([this, &sync_event] {
  36. Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
  37. auto result = ::CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
  38. m_coinit_success = result == S_OK;
  39. m_should_couninit = result == S_OK || result == S_FALSE;
  40. });
  41. sync_event.Wait();
  42. }
  43. #else
  44. = default;
  45. #endif
  46. bool CubebStream::Init()
  47. {
  48. bool return_value = false;
  49. #ifdef _WIN32
  50. if (!m_coinit_success)
  51. return false;
  52. Common::Event sync_event;
  53. m_work_queue.EmplaceItem([this, &return_value, &sync_event] {
  54. Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
  55. #endif
  56. m_ctx = CubebUtils::GetContext();
  57. if (m_ctx)
  58. {
  59. m_stereo = !Config::ShouldUseDPL2Decoder();
  60. cubeb_stream_params params{};
  61. params.rate = m_mixer->GetSampleRate();
  62. if (m_stereo)
  63. {
  64. params.channels = 2;
  65. params.format = CUBEB_SAMPLE_S16NE;
  66. params.layout = CUBEB_LAYOUT_STEREO;
  67. }
  68. else
  69. {
  70. params.channels = 6;
  71. params.format = CUBEB_SAMPLE_FLOAT32NE;
  72. params.layout = CUBEB_LAYOUT_3F2_LFE;
  73. }
  74. u32 minimum_latency = 0;
  75. if (cubeb_get_min_latency(m_ctx.get(), &params, &minimum_latency) != CUBEB_OK)
  76. ERROR_LOG_FMT(AUDIO, "Error getting minimum latency");
  77. INFO_LOG_FMT(AUDIO, "Minimum latency: {} frames", minimum_latency);
  78. return_value =
  79. cubeb_stream_init(m_ctx.get(), &m_stream, "Dolphin Audio Output", nullptr, nullptr,
  80. nullptr, &params, std::max(BUFFER_SAMPLES, minimum_latency),
  81. DataCallback, StateCallback, this) == CUBEB_OK;
  82. }
  83. #ifdef _WIN32
  84. });
  85. sync_event.Wait();
  86. #endif
  87. return return_value;
  88. }
  89. bool CubebStream::SetRunning(bool running)
  90. {
  91. bool return_value = false;
  92. #ifdef _WIN32
  93. if (!m_coinit_success)
  94. return false;
  95. Common::Event sync_event;
  96. m_work_queue.EmplaceItem([this, running, &return_value, &sync_event] {
  97. Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
  98. #endif
  99. if (running)
  100. return_value = cubeb_stream_start(m_stream) == CUBEB_OK;
  101. else
  102. return_value = cubeb_stream_stop(m_stream) == CUBEB_OK;
  103. #ifdef _WIN32
  104. });
  105. sync_event.Wait();
  106. #endif
  107. return return_value;
  108. }
  109. CubebStream::~CubebStream()
  110. {
  111. #ifdef _WIN32
  112. Common::Event sync_event;
  113. m_work_queue.EmplaceItem([this, &sync_event] {
  114. Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
  115. #endif
  116. cubeb_stream_stop(m_stream);
  117. cubeb_stream_destroy(m_stream);
  118. #ifdef _WIN32
  119. if (m_should_couninit)
  120. {
  121. m_should_couninit = false;
  122. CoUninitialize();
  123. }
  124. m_coinit_success = false;
  125. });
  126. sync_event.Wait();
  127. #endif
  128. m_ctx.reset();
  129. }
  130. void CubebStream::SetVolume(int volume)
  131. {
  132. #ifdef _WIN32
  133. if (!m_coinit_success)
  134. return;
  135. Common::Event sync_event;
  136. m_work_queue.EmplaceItem([this, volume, &sync_event] {
  137. Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
  138. #endif
  139. cubeb_stream_set_volume(m_stream, volume / 100.0f);
  140. #ifdef _WIN32
  141. });
  142. sync_event.Wait();
  143. #endif
  144. }