audren_u.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <array>
  6. #include <memory>
  7. #include "audio_core/audio_renderer.h"
  8. #include "common/alignment.h"
  9. #include "common/common_funcs.h"
  10. #include "common/logging/log.h"
  11. #include "core/core.h"
  12. #include "core/hle/ipc_helpers.h"
  13. #include "core/hle/kernel/hle_ipc.h"
  14. #include "core/hle/kernel/kernel.h"
  15. #include "core/hle/kernel/readable_event.h"
  16. #include "core/hle/kernel/writable_event.h"
  17. #include "core/hle/service/audio/audren_u.h"
  18. namespace Service::Audio {
  19. class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
  20. public:
  21. explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params)
  22. : ServiceFramework("IAudioRenderer") {
  23. // clang-format off
  24. static const FunctionInfo functions[] = {
  25. {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
  26. {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
  27. {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
  28. {3, &IAudioRenderer::GetState, "GetState"},
  29. {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"},
  30. {5, &IAudioRenderer::Start, "Start"},
  31. {6, &IAudioRenderer::Stop, "Stop"},
  32. {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
  33. {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
  34. {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
  35. {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"},
  36. {11, &IAudioRenderer::ExecuteAudioRendererRendering, "ExecuteAudioRendererRendering"},
  37. };
  38. // clang-format on
  39. RegisterHandlers(functions);
  40. auto& system = Core::System::GetInstance();
  41. system_event = Kernel::WritableEvent::CreateEventPair(
  42. system.Kernel(), Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
  43. renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), audren_params,
  44. system_event.writable);
  45. }
  46. private:
  47. void UpdateAudioCallback() {
  48. system_event.writable->Signal();
  49. }
  50. void GetSampleRate(Kernel::HLERequestContext& ctx) {
  51. LOG_DEBUG(Service_Audio, "called");
  52. IPC::ResponseBuilder rb{ctx, 3};
  53. rb.Push(RESULT_SUCCESS);
  54. rb.Push<u32>(renderer->GetSampleRate());
  55. }
  56. void GetSampleCount(Kernel::HLERequestContext& ctx) {
  57. LOG_DEBUG(Service_Audio, "called");
  58. IPC::ResponseBuilder rb{ctx, 3};
  59. rb.Push(RESULT_SUCCESS);
  60. rb.Push<u32>(renderer->GetSampleCount());
  61. }
  62. void GetState(Kernel::HLERequestContext& ctx) {
  63. LOG_DEBUG(Service_Audio, "called");
  64. IPC::ResponseBuilder rb{ctx, 3};
  65. rb.Push(RESULT_SUCCESS);
  66. rb.Push<u32>(static_cast<u32>(renderer->GetStreamState()));
  67. }
  68. void GetMixBufferCount(Kernel::HLERequestContext& ctx) {
  69. LOG_DEBUG(Service_Audio, "called");
  70. IPC::ResponseBuilder rb{ctx, 3};
  71. rb.Push(RESULT_SUCCESS);
  72. rb.Push<u32>(renderer->GetMixBufferCount());
  73. }
  74. void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
  75. LOG_WARNING(Service_Audio, "(STUBBED) called");
  76. ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
  77. IPC::ResponseBuilder rb{ctx, 2};
  78. rb.Push(RESULT_SUCCESS);
  79. }
  80. void Start(Kernel::HLERequestContext& ctx) {
  81. LOG_WARNING(Service_Audio, "(STUBBED) called");
  82. IPC::ResponseBuilder rb{ctx, 2};
  83. rb.Push(RESULT_SUCCESS);
  84. }
  85. void Stop(Kernel::HLERequestContext& ctx) {
  86. LOG_WARNING(Service_Audio, "(STUBBED) called");
  87. IPC::ResponseBuilder rb{ctx, 2};
  88. rb.Push(RESULT_SUCCESS);
  89. }
  90. void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
  91. LOG_WARNING(Service_Audio, "(STUBBED) called");
  92. IPC::ResponseBuilder rb{ctx, 2, 1};
  93. rb.Push(RESULT_SUCCESS);
  94. rb.PushCopyObjects(system_event.readable);
  95. }
  96. void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
  97. IPC::RequestParser rp{ctx};
  98. rendering_time_limit_percent = rp.Pop<u32>();
  99. LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}",
  100. rendering_time_limit_percent);
  101. ASSERT(rendering_time_limit_percent >= 0 && rendering_time_limit_percent <= 100);
  102. IPC::ResponseBuilder rb{ctx, 2};
  103. rb.Push(RESULT_SUCCESS);
  104. }
  105. void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
  106. LOG_DEBUG(Service_Audio, "called");
  107. IPC::ResponseBuilder rb{ctx, 3};
  108. rb.Push(RESULT_SUCCESS);
  109. rb.Push(rendering_time_limit_percent);
  110. }
  111. void ExecuteAudioRendererRendering(Kernel::HLERequestContext& ctx) {
  112. LOG_DEBUG(Service_Audio, "called");
  113. // This service command currently only reports an unsupported operation
  114. // error code, or aborts. Given that, we just always return an error
  115. // code in this case.
  116. IPC::ResponseBuilder rb{ctx, 2};
  117. rb.Push(ResultCode{ErrorModule::Audio, 201});
  118. }
  119. Kernel::EventPair system_event;
  120. std::unique_ptr<AudioCore::AudioRenderer> renderer;
  121. u32 rendering_time_limit_percent = 100;
  122. };
  123. class IAudioDevice final : public ServiceFramework<IAudioDevice> {
  124. public:
  125. IAudioDevice() : ServiceFramework("IAudioDevice") {
  126. static const FunctionInfo functions[] = {
  127. {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
  128. {1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
  129. {2, nullptr, "GetAudioDeviceOutputVolume"},
  130. {3, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceName"},
  131. {4, &IAudioDevice::QueryAudioDeviceSystemEvent, "QueryAudioDeviceSystemEvent"},
  132. {5, &IAudioDevice::GetActiveChannelCount, "GetActiveChannelCount"},
  133. {6, &IAudioDevice::ListAudioDeviceName,
  134. "ListAudioDeviceNameAuto"}, // TODO(ogniK): Confirm if autos are identical to non auto
  135. {7, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolumeAuto"},
  136. {8, nullptr, "GetAudioDeviceOutputVolumeAuto"},
  137. {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
  138. {11, nullptr, "QueryAudioDeviceInputEvent"},
  139. {12, nullptr, "QueryAudioDeviceOutputEvent"},
  140. {13, nullptr, "GetAudioSystemMasterVolumeSetting"},
  141. };
  142. RegisterHandlers(functions);
  143. auto& kernel = Core::System::GetInstance().Kernel();
  144. buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
  145. "IAudioOutBufferReleasedEvent");
  146. }
  147. private:
  148. void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
  149. LOG_WARNING(Service_Audio, "(STUBBED) called");
  150. IPC::RequestParser rp{ctx};
  151. constexpr std::array<char, 15> audio_interface{{"AudioInterface"}};
  152. ctx.WriteBuffer(audio_interface);
  153. IPC::ResponseBuilder rb{ctx, 3};
  154. rb.Push(RESULT_SUCCESS);
  155. rb.Push<u32>(1);
  156. }
  157. void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
  158. LOG_WARNING(Service_Audio, "(STUBBED) called");
  159. IPC::RequestParser rp{ctx};
  160. f32 volume = static_cast<f32>(rp.Pop<u32>());
  161. auto file_buffer = ctx.ReadBuffer();
  162. auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
  163. IPC::ResponseBuilder rb{ctx, 2};
  164. rb.Push(RESULT_SUCCESS);
  165. }
  166. void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
  167. LOG_WARNING(Service_Audio, "(STUBBED) called");
  168. IPC::RequestParser rp{ctx};
  169. constexpr std::array<char, 12> audio_interface{{"AudioDevice"}};
  170. ctx.WriteBuffer(audio_interface);
  171. IPC::ResponseBuilder rb{ctx, 3};
  172. rb.Push(RESULT_SUCCESS);
  173. rb.Push<u32>(1);
  174. }
  175. void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
  176. LOG_WARNING(Service_Audio, "(STUBBED) called");
  177. buffer_event.writable->Signal();
  178. IPC::ResponseBuilder rb{ctx, 2, 1};
  179. rb.Push(RESULT_SUCCESS);
  180. rb.PushCopyObjects(buffer_event.readable);
  181. }
  182. void GetActiveChannelCount(Kernel::HLERequestContext& ctx) {
  183. LOG_WARNING(Service_Audio, "(STUBBED) called");
  184. IPC::ResponseBuilder rb{ctx, 3};
  185. rb.Push(RESULT_SUCCESS);
  186. rb.Push<u32>(1);
  187. }
  188. Kernel::EventPair buffer_event;
  189. }; // namespace Audio
  190. AudRenU::AudRenU() : ServiceFramework("audren:u") {
  191. // clang-format off
  192. static const FunctionInfo functions[] = {
  193. {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
  194. {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"},
  195. {2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"},
  196. {3, nullptr, "OpenAudioRendererAuto"},
  197. {4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"},
  198. };
  199. // clang-format on
  200. RegisterHandlers(functions);
  201. }
  202. AudRenU::~AudRenU() = default;
  203. void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
  204. LOG_DEBUG(Service_Audio, "called");
  205. IPC::RequestParser rp{ctx};
  206. auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
  207. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  208. rb.Push(RESULT_SUCCESS);
  209. rb.PushIpcInterface<Audio::IAudioRenderer>(std::move(params));
  210. }
  211. void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
  212. IPC::RequestParser rp{ctx};
  213. auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
  214. LOG_DEBUG(Service_Audio, "called");
  215. u64 buffer_sz = Common::AlignUp(4 * params.mix_buffer_count, 0x40);
  216. buffer_sz += params.submix_count * 1024;
  217. buffer_sz += 0x940 * (params.submix_count + 1);
  218. buffer_sz += 0x3F0 * params.voice_count;
  219. buffer_sz += Common::AlignUp(8 * (params.submix_count + 1), 0x10);
  220. buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10);
  221. buffer_sz += Common::AlignUp(
  222. (0x3C0 * (params.sink_count + params.submix_count) + 4 * params.sample_count) *
  223. (params.mix_buffer_count + 6),
  224. 0x40);
  225. if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
  226. const u32 count = params.submix_count + 1;
  227. u64 node_count = Common::AlignUp(count, 0x40);
  228. const u64 node_state_buffer_sz =
  229. 4 * (node_count * node_count) + 0xC * node_count + 2 * (node_count / 8);
  230. u64 edge_matrix_buffer_sz = 0;
  231. node_count = Common::AlignUp(count * count, 0x40);
  232. if (node_count >> 31 != 0) {
  233. edge_matrix_buffer_sz = (node_count | 7) / 8;
  234. } else {
  235. edge_matrix_buffer_sz = node_count / 8;
  236. }
  237. buffer_sz += Common::AlignUp(node_state_buffer_sz + edge_matrix_buffer_sz, 0x10);
  238. }
  239. buffer_sz += 0x20 * (params.effect_count + 4 * params.voice_count) + 0x50;
  240. if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
  241. buffer_sz += 0xE0 * params.num_splitter_send_channels;
  242. buffer_sz += 0x20 * params.splitter_count;
  243. buffer_sz += Common::AlignUp(4 * params.num_splitter_send_channels, 0x10);
  244. }
  245. buffer_sz = Common::AlignUp(buffer_sz, 0x40) + 0x170 * params.sink_count;
  246. u64 output_sz = buffer_sz + 0x280 * params.sink_count + 0x4B0 * params.effect_count +
  247. ((params.voice_count * 256) | 0x40);
  248. if (params.performance_frame_count >= 1) {
  249. output_sz = Common::AlignUp(((16 * params.sink_count + 16 * params.effect_count +
  250. 16 * params.voice_count + 16) +
  251. 0x658) *
  252. (params.performance_frame_count + 1) +
  253. 0xc0,
  254. 0x40) +
  255. output_sz;
  256. }
  257. output_sz = Common::AlignUp(output_sz + 0x1807e, 0x1000);
  258. IPC::ResponseBuilder rb{ctx, 4};
  259. rb.Push(RESULT_SUCCESS);
  260. rb.Push<u64>(output_sz);
  261. LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", output_sz);
  262. }
  263. void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
  264. LOG_DEBUG(Service_Audio, "called");
  265. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  266. rb.Push(RESULT_SUCCESS);
  267. rb.PushIpcInterface<Audio::IAudioDevice>();
  268. }
  269. void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) {
  270. LOG_WARNING(Service_Audio, "(STUBBED) called");
  271. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  272. rb.Push(RESULT_SUCCESS);
  273. rb.PushIpcInterface<Audio::IAudioDevice>(); // TODO(ogniK): Figure out what is different
  274. // based on the current revision
  275. }
  276. bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const {
  277. u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap
  278. switch (feature) {
  279. case AudioFeatures::Splitter:
  280. return version_num >= 2u;
  281. default:
  282. return false;
  283. }
  284. }
  285. } // namespace Service::Audio