audout_u.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <vector>
  5. #include "common/logging/log.h"
  6. #include "core/core_timing.h"
  7. #include "core/hle/ipc_helpers.h"
  8. #include "core/hle/kernel/event.h"
  9. #include "core/hle/kernel/hle_ipc.h"
  10. #include "core/hle/service/audio/audout_u.h"
  11. namespace Service::Audio {
  12. /// Switch sample rate frequency
  13. constexpr u32 sample_rate{48000};
  14. /// TODO(st4rk): dynamic number of channels, as I think Switch has support
  15. /// to more audio channels (probably when Docked I guess)
  16. constexpr u32 audio_channels{2};
  17. /// TODO(st4rk): find a proper value for the audio_ticks
  18. constexpr u64 audio_ticks{static_cast<u64>(BASE_CLOCK_RATE / 500)};
  19. class IAudioOut final : public ServiceFramework<IAudioOut> {
  20. public:
  21. IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(AudioState::Stopped) {
  22. static const FunctionInfo functions[] = {
  23. {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
  24. {1, &IAudioOut::StartAudioOut, "StartAudioOut"},
  25. {2, &IAudioOut::StopAudioOut, "StopAudioOut"},
  26. {3, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBuffer"},
  27. {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
  28. {5, &IAudioOut::GetReleasedAudioOutBuffer, "GetReleasedAudioOutBuffer"},
  29. {6, nullptr, "ContainsAudioOutBuffer"},
  30. {7, nullptr, "AppendAudioOutBufferAuto"},
  31. {8, nullptr, "GetReleasedAudioOutBufferAuto"},
  32. {9, nullptr, "GetAudioOutBufferCount"},
  33. {10, nullptr, "GetAudioOutPlayedSampleCount"},
  34. {11, nullptr, "FlushAudioOutBuffers"},
  35. };
  36. RegisterHandlers(functions);
  37. // This is the event handle used to check if the audio buffer was released
  38. buffer_event =
  39. Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent");
  40. // Register event callback to update the Audio Buffer
  41. audio_event = CoreTiming::RegisterEvent(
  42. "IAudioOut::UpdateAudioBuffersCallback", [this](u64 userdata, int cycles_late) {
  43. UpdateAudioBuffersCallback();
  44. CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
  45. });
  46. // Start the audio event
  47. CoreTiming::ScheduleEvent(audio_ticks, audio_event);
  48. }
  49. ~IAudioOut() {
  50. CoreTiming::UnscheduleEvent(audio_event, 0);
  51. }
  52. private:
  53. void GetAudioOutState(Kernel::HLERequestContext& ctx) {
  54. NGLOG_DEBUG(Service_Audio, "called");
  55. IPC::ResponseBuilder rb{ctx, 3};
  56. rb.Push(RESULT_SUCCESS);
  57. rb.Push(static_cast<u32>(audio_out_state));
  58. }
  59. void StartAudioOut(Kernel::HLERequestContext& ctx) {
  60. NGLOG_WARNING(Service_Audio, "(STUBBED) called");
  61. // Start audio
  62. audio_out_state = AudioState::Started;
  63. IPC::ResponseBuilder rb{ctx, 2};
  64. rb.Push(RESULT_SUCCESS);
  65. }
  66. void StopAudioOut(Kernel::HLERequestContext& ctx) {
  67. NGLOG_WARNING(Service_Audio, "(STUBBED) called");
  68. // Stop audio
  69. audio_out_state = AudioState::Stopped;
  70. queue_keys.clear();
  71. IPC::ResponseBuilder rb{ctx, 2};
  72. rb.Push(RESULT_SUCCESS);
  73. }
  74. void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
  75. NGLOG_WARNING(Service_Audio, "(STUBBED) called");
  76. IPC::ResponseBuilder rb{ctx, 2, 1};
  77. rb.Push(RESULT_SUCCESS);
  78. rb.PushCopyObjects(buffer_event);
  79. }
  80. void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) {
  81. NGLOG_WARNING(Service_Audio, "(STUBBED) called");
  82. IPC::RequestParser rp{ctx};
  83. const u64 key{rp.Pop<u64>()};
  84. queue_keys.insert(queue_keys.begin(), key);
  85. IPC::ResponseBuilder rb{ctx, 2};
  86. rb.Push(RESULT_SUCCESS);
  87. }
  88. void GetReleasedAudioOutBuffer(Kernel::HLERequestContext& ctx) {
  89. NGLOG_WARNING(Service_Audio, "(STUBBED) called");
  90. // TODO(st4rk): This is how libtransistor currently implements the
  91. // GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address
  92. // is used to know which buffer should be filled with data and send again to the service
  93. // through AppendAudioOutBuffer. Check if this is the proper way to do it.
  94. u64 key{0};
  95. if (queue_keys.size()) {
  96. key = queue_keys.back();
  97. queue_keys.pop_back();
  98. }
  99. ctx.WriteBuffer(&key, sizeof(u64));
  100. IPC::ResponseBuilder rb{ctx, 3};
  101. rb.Push(RESULT_SUCCESS);
  102. // TODO(st4rk): This might be the total of released buffers, needs to be verified on
  103. // hardware
  104. rb.Push<u32>(static_cast<u32>(queue_keys.size()));
  105. }
  106. void UpdateAudioBuffersCallback() {
  107. if (audio_out_state != AudioState::Started) {
  108. return;
  109. }
  110. if (queue_keys.empty()) {
  111. return;
  112. }
  113. buffer_event->Signal();
  114. }
  115. enum class AudioState : u32 {
  116. Started,
  117. Stopped,
  118. };
  119. /// This is used to trigger the audio event callback that is going to read the samples from the
  120. /// audio_buffer list and enqueue the samples using the sink (audio_core).
  121. CoreTiming::EventType* audio_event;
  122. /// This is the evend handle used to check if the audio buffer was released
  123. Kernel::SharedPtr<Kernel::Event> buffer_event;
  124. /// (st4rk): This is just a temporary workaround for the future implementation. Libtransistor
  125. /// uses the key as an address in the App, so we need to return when the
  126. /// GetReleasedAudioOutBuffer_1 is called, otherwise we'll run in problems, because
  127. /// libtransistor uses the key returned as an pointer.
  128. std::vector<u64> queue_keys;
  129. AudioState audio_out_state;
  130. };
  131. void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
  132. NGLOG_WARNING(Service_Audio, "(STUBBED) called");
  133. IPC::RequestParser rp{ctx};
  134. const std::string audio_interface = "AudioInterface";
  135. ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size());
  136. IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
  137. rb.Push(RESULT_SUCCESS);
  138. // TODO(st4rk): We're currently returning only one audio interface (stringlist size). However,
  139. // it's highly possible to have more than one interface (despite that libtransistor requires
  140. // only one).
  141. rb.Push<u32>(1);
  142. }
  143. void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
  144. NGLOG_WARNING(Service_Audio, "(STUBBED) called");
  145. if (!audio_out_interface) {
  146. audio_out_interface = std::make_shared<IAudioOut>();
  147. }
  148. IPC::ResponseBuilder rb{ctx, 6, 0, 1};
  149. rb.Push(RESULT_SUCCESS);
  150. rb.Push<u32>(sample_rate);
  151. rb.Push<u32>(audio_channels);
  152. rb.Push<u32>(static_cast<u32>(PcmFormat::Int16));
  153. rb.Push<u32>(0); // This field is unknown
  154. rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
  155. }
  156. AudOutU::AudOutU() : ServiceFramework("audout:u") {
  157. static const FunctionInfo functions[] = {{0x00000000, &AudOutU::ListAudioOuts, "ListAudioOuts"},
  158. {0x00000001, &AudOutU::OpenAudioOut, "OpenAudioOut"},
  159. {0x00000002, nullptr, "ListAudioOutsAuto"},
  160. {0x00000003, nullptr, "OpenAudioOutAuto"}};
  161. RegisterHandlers(functions);
  162. }
  163. } // namespace Service::Audio