audout_u.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <array>
  5. #include <cstring>
  6. #include <vector>
  7. #include "audio_core/audio_out.h"
  8. #include "audio_core/codec.h"
  9. #include "common/common_funcs.h"
  10. #include "common/logging/log.h"
  11. #include "common/swap.h"
  12. #include "core/core.h"
  13. #include "core/hle/ipc_helpers.h"
  14. #include "core/hle/kernel/k_event.h"
  15. #include "core/hle/kernel/kernel.h"
  16. #include "core/hle/service/audio/audout_u.h"
  17. #include "core/hle/service/audio/errors.h"
  18. #include "core/hle/service/kernel_helpers.h"
  19. #include "core/memory.h"
  20. namespace Service::Audio {
  21. constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}};
  22. constexpr int DefaultSampleRate{48000};
  23. struct AudoutParams {
  24. s32_le sample_rate;
  25. u16_le channel_count;
  26. INSERT_PADDING_BYTES_NOINIT(2);
  27. };
  28. static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size");
  29. enum class AudioState : u32 {
  30. Started,
  31. Stopped,
  32. };
  33. class IAudioOut final : public ServiceFramework<IAudioOut> {
  34. public:
  35. explicit IAudioOut(Core::System& system_, AudoutParams audio_params_,
  36. AudioCore::AudioOut& audio_core_, std::string&& device_name_,
  37. std::string&& unique_name)
  38. : ServiceFramework{system_, "IAudioOut"}, audio_core{audio_core_},
  39. device_name{std::move(device_name_)}, audio_params{audio_params_},
  40. main_memory{system.Memory()}, service_context{system_, "IAudioOut"} {
  41. // clang-format off
  42. static const FunctionInfo functions[] = {
  43. {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
  44. {1, &IAudioOut::StartAudioOut, "Start"},
  45. {2, &IAudioOut::StopAudioOut, "Stop"},
  46. {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"},
  47. {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
  48. {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffers"},
  49. {6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"},
  50. {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
  51. {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
  52. {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
  53. {10, &IAudioOut::GetAudioOutPlayedSampleCount, "GetAudioOutPlayedSampleCount"},
  54. {11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"},
  55. {12, &IAudioOut::SetAudioOutVolume, "SetAudioOutVolume"},
  56. {13, &IAudioOut::GetAudioOutVolume, "GetAudioOutVolume"},
  57. };
  58. // clang-format on
  59. RegisterHandlers(functions);
  60. // This is the event handle used to check if the audio buffer was released
  61. buffer_event = service_context.CreateEvent("IAudioOutBufferReleased");
  62. stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
  63. audio_params.channel_count, std::move(unique_name), [this] {
  64. const auto guard = LockService();
  65. buffer_event->GetWritableEvent().Signal();
  66. });
  67. }
  68. ~IAudioOut() override {
  69. service_context.CloseEvent(buffer_event);
  70. }
  71. private:
  72. struct AudioBuffer {
  73. u64_le next;
  74. u64_le buffer;
  75. u64_le buffer_capacity;
  76. u64_le buffer_size;
  77. u64_le offset;
  78. };
  79. static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size");
  80. void GetAudioOutState(Kernel::HLERequestContext& ctx) {
  81. LOG_DEBUG(Service_Audio, "called");
  82. IPC::ResponseBuilder rb{ctx, 3};
  83. rb.Push(ResultSuccess);
  84. rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped));
  85. }
  86. void StartAudioOut(Kernel::HLERequestContext& ctx) {
  87. LOG_DEBUG(Service_Audio, "called");
  88. if (stream->IsPlaying()) {
  89. IPC::ResponseBuilder rb{ctx, 2};
  90. rb.Push(ERR_OPERATION_FAILED);
  91. return;
  92. }
  93. audio_core.StartStream(stream);
  94. IPC::ResponseBuilder rb{ctx, 2};
  95. rb.Push(ResultSuccess);
  96. }
  97. void StopAudioOut(Kernel::HLERequestContext& ctx) {
  98. LOG_DEBUG(Service_Audio, "called");
  99. if (stream->IsPlaying()) {
  100. audio_core.StopStream(stream);
  101. }
  102. IPC::ResponseBuilder rb{ctx, 2};
  103. rb.Push(ResultSuccess);
  104. }
  105. void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
  106. LOG_DEBUG(Service_Audio, "called");
  107. IPC::ResponseBuilder rb{ctx, 2, 1};
  108. rb.Push(ResultSuccess);
  109. rb.PushCopyObjects(buffer_event->GetReadableEvent());
  110. }
  111. void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
  112. LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description());
  113. IPC::RequestParser rp{ctx};
  114. const auto& input_buffer{ctx.ReadBuffer()};
  115. ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer),
  116. "AudioBuffer input is an invalid size!");
  117. AudioBuffer audio_buffer{};
  118. std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
  119. const u64 tag{rp.Pop<u64>()};
  120. std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
  121. main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
  122. if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
  123. IPC::ResponseBuilder rb{ctx, 2};
  124. rb.Push(ERR_BUFFER_COUNT_EXCEEDED);
  125. return;
  126. }
  127. IPC::ResponseBuilder rb{ctx, 2};
  128. rb.Push(ResultSuccess);
  129. }
  130. void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
  131. LOG_DEBUG(Service_Audio, "called {}", ctx.Description());
  132. const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)};
  133. const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
  134. std::vector<u64> tags{released_buffers};
  135. tags.resize(max_count);
  136. ctx.WriteBuffer(tags);
  137. IPC::ResponseBuilder rb{ctx, 3};
  138. rb.Push(ResultSuccess);
  139. rb.Push<u32>(static_cast<u32>(released_buffers.size()));
  140. }
  141. void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) {
  142. LOG_DEBUG(Service_Audio, "called");
  143. IPC::RequestParser rp{ctx};
  144. const u64 tag{rp.Pop<u64>()};
  145. IPC::ResponseBuilder rb{ctx, 3};
  146. rb.Push(ResultSuccess);
  147. rb.Push(stream->ContainsBuffer(tag));
  148. }
  149. void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) {
  150. LOG_DEBUG(Service_Audio, "called");
  151. IPC::ResponseBuilder rb{ctx, 3};
  152. rb.Push(ResultSuccess);
  153. rb.Push(static_cast<u32>(stream->GetQueueSize()));
  154. }
  155. void GetAudioOutPlayedSampleCount(Kernel::HLERequestContext& ctx) {
  156. LOG_DEBUG(Service_Audio, "called");
  157. IPC::ResponseBuilder rb{ctx, 4};
  158. rb.Push(ResultSuccess);
  159. rb.Push(stream->GetPlayedSampleCount());
  160. }
  161. void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) {
  162. LOG_DEBUG(Service_Audio, "called");
  163. IPC::ResponseBuilder rb{ctx, 3};
  164. rb.Push(ResultSuccess);
  165. rb.Push(stream->Flush());
  166. }
  167. void SetAudioOutVolume(Kernel::HLERequestContext& ctx) {
  168. IPC::RequestParser rp{ctx};
  169. const float volume = rp.Pop<float>();
  170. LOG_DEBUG(Service_Audio, "called, volume={}", volume);
  171. stream->SetVolume(volume);
  172. IPC::ResponseBuilder rb{ctx, 2};
  173. rb.Push(ResultSuccess);
  174. }
  175. void GetAudioOutVolume(Kernel::HLERequestContext& ctx) {
  176. LOG_DEBUG(Service_Audio, "called");
  177. IPC::ResponseBuilder rb{ctx, 3};
  178. rb.Push(ResultSuccess);
  179. rb.Push(stream->GetVolume());
  180. }
  181. AudioCore::AudioOut& audio_core;
  182. AudioCore::StreamPtr stream;
  183. std::string device_name;
  184. [[maybe_unused]] AudoutParams audio_params{};
  185. Core::Memory::Memory& main_memory;
  186. KernelHelpers::ServiceContext service_context;
  187. /// This is the event handle used to check if the audio buffer was released
  188. Kernel::KEvent* buffer_event;
  189. };
  190. AudOutU::AudOutU(Core::System& system_) : ServiceFramework{system_, "audout:u"} {
  191. // clang-format off
  192. static const FunctionInfo functions[] = {
  193. {0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"},
  194. {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"},
  195. {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
  196. {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"},
  197. };
  198. // clang-format on
  199. RegisterHandlers(functions);
  200. audio_core = std::make_unique<AudioCore::AudioOut>();
  201. }
  202. AudOutU::~AudOutU() = default;
  203. void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
  204. LOG_DEBUG(Service_Audio, "called");
  205. ctx.WriteBuffer(DefaultDevice);
  206. IPC::ResponseBuilder rb{ctx, 3};
  207. rb.Push(ResultSuccess);
  208. rb.Push<u32>(1); // Amount of audio devices
  209. }
  210. void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
  211. LOG_DEBUG(Service_Audio, "called");
  212. const auto device_name_data{ctx.ReadBuffer()};
  213. std::string device_name;
  214. if (device_name_data[0] != '\0') {
  215. device_name.assign(device_name_data.begin(), device_name_data.end());
  216. } else {
  217. device_name.assign(DefaultDevice.begin(), DefaultDevice.end());
  218. }
  219. ctx.WriteBuffer(device_name);
  220. IPC::RequestParser rp{ctx};
  221. auto params{rp.PopRaw<AudoutParams>()};
  222. if (params.channel_count <= 2) {
  223. // Mono does not exist for audout
  224. params.channel_count = 2;
  225. } else {
  226. params.channel_count = 6;
  227. }
  228. if (!params.sample_rate) {
  229. params.sample_rate = DefaultSampleRate;
  230. }
  231. std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())};
  232. auto audio_out_interface = std::make_shared<IAudioOut>(
  233. system, params, *audio_core, std::move(device_name), std::move(unique_name));
  234. IPC::ResponseBuilder rb{ctx, 6, 0, 1};
  235. rb.Push(ResultSuccess);
  236. rb.Push<u32>(DefaultSampleRate);
  237. rb.Push<u32>(params.channel_count);
  238. rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
  239. rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
  240. rb.PushIpcInterface<IAudioOut>(audio_out_interface);
  241. audio_out_interfaces.push_back(std::move(audio_out_interface));
  242. }
  243. } // namespace Service::Audio