Project Andio
This commit is contained in:
		| @@ -8,6 +8,7 @@ | ||||
| #include <memory> | ||||
| #include <utility> | ||||
|  | ||||
| #include "audio_core/audio_core.h" | ||||
| #include "common/fs/fs.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/microprofile.h" | ||||
| @@ -140,6 +141,8 @@ struct System::Impl { | ||||
|         core_timing.SyncPause(false); | ||||
|         is_paused = false; | ||||
|  | ||||
|         audio_core->PauseSinks(false); | ||||
|  | ||||
|         return status; | ||||
|     } | ||||
|  | ||||
| @@ -147,6 +150,8 @@ struct System::Impl { | ||||
|         std::unique_lock<std::mutex> lk(suspend_guard); | ||||
|         status = SystemResultStatus::Success; | ||||
|  | ||||
|         audio_core->PauseSinks(true); | ||||
|  | ||||
|         core_timing.SyncPause(true); | ||||
|         kernel.Suspend(true); | ||||
|         is_paused = true; | ||||
| @@ -154,6 +159,11 @@ struct System::Impl { | ||||
|         return status; | ||||
|     } | ||||
|  | ||||
|     bool IsPaused() const { | ||||
|         std::unique_lock lk(suspend_guard); | ||||
|         return is_paused; | ||||
|     } | ||||
|  | ||||
|     std::unique_lock<std::mutex> StallProcesses() { | ||||
|         std::unique_lock<std::mutex> lk(suspend_guard); | ||||
|         kernel.Suspend(true); | ||||
| @@ -214,6 +224,8 @@ struct System::Impl { | ||||
|             return SystemResultStatus::ErrorVideoCore; | ||||
|         } | ||||
|  | ||||
|         audio_core = std::make_unique<AudioCore::AudioCore>(system); | ||||
|  | ||||
|         service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); | ||||
|         services = std::make_unique<Service::Services>(service_manager, system); | ||||
|         interrupt_manager = std::make_unique<Hardware::InterruptManager>(system); | ||||
| @@ -290,7 +302,7 @@ struct System::Impl { | ||||
|             if (Settings::values.gamecard_current_game) { | ||||
|                 fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath)); | ||||
|             } else if (!Settings::values.gamecard_path.GetValue().empty()) { | ||||
|                 const auto gamecard_path = Settings::values.gamecard_path.GetValue(); | ||||
|                 const auto& gamecard_path = Settings::values.gamecard_path.GetValue(); | ||||
|                 fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, gamecard_path)); | ||||
|             } | ||||
|         } | ||||
| @@ -308,6 +320,8 @@ struct System::Impl { | ||||
|     } | ||||
|  | ||||
|     void Shutdown() { | ||||
|         SetShuttingDown(true); | ||||
|  | ||||
|         // Log last frame performance stats if game was loded | ||||
|         if (perf_stats) { | ||||
|             const auto perf_results = GetAndResetPerfStats(); | ||||
| @@ -333,14 +347,15 @@ struct System::Impl { | ||||
|         kernel.ShutdownCores(); | ||||
|         cpu_manager.Shutdown(); | ||||
|         debugger.reset(); | ||||
|         kernel.CloseServices(); | ||||
|         services.reset(); | ||||
|         service_manager.reset(); | ||||
|         cheat_engine.reset(); | ||||
|         telemetry_session.reset(); | ||||
|         cpu_manager.Shutdown(); | ||||
|         time_manager.Shutdown(); | ||||
|         core_timing.Shutdown(); | ||||
|         app_loader.reset(); | ||||
|         audio_core.reset(); | ||||
|         gpu_core.reset(); | ||||
|         perf_stats.reset(); | ||||
|         kernel.Shutdown(); | ||||
| @@ -350,6 +365,14 @@ struct System::Impl { | ||||
|         LOG_DEBUG(Core, "Shutdown OK"); | ||||
|     } | ||||
|  | ||||
|     bool IsShuttingDown() const { | ||||
|         return is_shutting_down; | ||||
|     } | ||||
|  | ||||
|     void SetShuttingDown(bool shutting_down) { | ||||
|         is_shutting_down = shutting_down; | ||||
|     } | ||||
|  | ||||
|     Loader::ResultStatus GetGameName(std::string& out) const { | ||||
|         if (app_loader == nullptr) | ||||
|             return Loader::ResultStatus::ErrorNotInitialized; | ||||
| @@ -392,8 +415,9 @@ struct System::Impl { | ||||
|         return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs()); | ||||
|     } | ||||
|  | ||||
|     std::mutex suspend_guard; | ||||
|     mutable std::mutex suspend_guard; | ||||
|     bool is_paused{}; | ||||
|     std::atomic<bool> is_shutting_down{}; | ||||
|  | ||||
|     Timing::CoreTiming core_timing; | ||||
|     Kernel::KernelCore kernel; | ||||
| @@ -407,6 +431,7 @@ struct System::Impl { | ||||
|     std::unique_ptr<Tegra::GPU> gpu_core; | ||||
|     std::unique_ptr<Hardware::InterruptManager> interrupt_manager; | ||||
|     std::unique_ptr<Core::DeviceMemory> device_memory; | ||||
|     std::unique_ptr<AudioCore::AudioCore> audio_core; | ||||
|     Core::Memory::Memory memory; | ||||
|     Core::HID::HIDCore hid_core; | ||||
|     CpuManager cpu_manager; | ||||
| @@ -479,6 +504,10 @@ SystemResultStatus System::Pause() { | ||||
|     return impl->Pause(); | ||||
| } | ||||
|  | ||||
| bool System::IsPaused() const { | ||||
|     return impl->IsPaused(); | ||||
| } | ||||
|  | ||||
| void System::InvalidateCpuInstructionCaches() { | ||||
|     impl->kernel.InvalidateAllInstructionCaches(); | ||||
| } | ||||
| @@ -491,6 +520,14 @@ void System::Shutdown() { | ||||
|     impl->Shutdown(); | ||||
| } | ||||
|  | ||||
| bool System::IsShuttingDown() const { | ||||
|     return impl->IsShuttingDown(); | ||||
| } | ||||
|  | ||||
| void System::SetShuttingDown(bool shutting_down) { | ||||
|     impl->SetShuttingDown(shutting_down); | ||||
| } | ||||
|  | ||||
| void System::DetachDebugger() { | ||||
|     if (impl->debugger) { | ||||
|         impl->debugger->NotifyShutdown(); | ||||
| @@ -640,6 +677,14 @@ const HID::HIDCore& System::HIDCore() const { | ||||
|     return impl->hid_core; | ||||
| } | ||||
|  | ||||
| AudioCore::AudioCore& System::AudioCore() { | ||||
|     return *impl->audio_core; | ||||
| } | ||||
|  | ||||
| const AudioCore::AudioCore& System::AudioCore() const { | ||||
|     return *impl->audio_core; | ||||
| } | ||||
|  | ||||
| Timing::CoreTiming& System::CoreTiming() { | ||||
|     return impl->core_timing; | ||||
| } | ||||
|   | ||||
| @@ -81,6 +81,10 @@ namespace VideoCore { | ||||
| class RendererBase; | ||||
| } // namespace VideoCore | ||||
|  | ||||
| namespace AudioCore { | ||||
| class AudioCore; | ||||
| } // namespace AudioCore | ||||
|  | ||||
| namespace Core::Timing { | ||||
| class CoreTiming; | ||||
| } | ||||
| @@ -148,6 +152,9 @@ public: | ||||
|      */ | ||||
|     [[nodiscard]] SystemResultStatus Pause(); | ||||
|  | ||||
|     /// Check if the core is currently paused. | ||||
|     [[nodiscard]] bool IsPaused() const; | ||||
|  | ||||
|     /** | ||||
|      * Invalidate the CPU instruction caches | ||||
|      * This function should only be used by GDB Stub to support breakpoints, memory updates and | ||||
| @@ -160,6 +167,12 @@ public: | ||||
|     /// Shutdown the emulated system. | ||||
|     void Shutdown(); | ||||
|  | ||||
|     /// Check if the core is shutting down. | ||||
|     [[nodiscard]] bool IsShuttingDown() const; | ||||
|  | ||||
|     /// Set the shutting down state. | ||||
|     void SetShuttingDown(bool shutting_down); | ||||
|  | ||||
|     /// Forcibly detach the debugger if it is running. | ||||
|     void DetachDebugger(); | ||||
|  | ||||
| @@ -250,6 +263,12 @@ public: | ||||
|     /// Gets an immutable reference to the renderer. | ||||
|     [[nodiscard]] const VideoCore::RendererBase& Renderer() const; | ||||
|  | ||||
|     /// Gets a mutable reference to the audio interface | ||||
|     [[nodiscard]] AudioCore::AudioCore& AudioCore(); | ||||
|  | ||||
|     /// Gets an immutable reference to the audio interface. | ||||
|     [[nodiscard]] const AudioCore::AudioCore& AudioCore() const; | ||||
|  | ||||
|     /// Gets the global scheduler | ||||
|     [[nodiscard]] Kernel::GlobalSchedulerContext& GlobalSchedulerContext(); | ||||
|  | ||||
|   | ||||
| @@ -287,18 +287,52 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, | ||||
|             BufferDescriptorB().size() > buffer_index && | ||||
|                 BufferDescriptorB()[buffer_index].Size() >= size, | ||||
|             { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size); | ||||
|         memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); | ||||
|         WriteBufferB(buffer, size, buffer_index); | ||||
|     } else { | ||||
|         ASSERT_OR_EXECUTE_MSG( | ||||
|             BufferDescriptorC().size() > buffer_index && | ||||
|                 BufferDescriptorC()[buffer_index].Size() >= size, | ||||
|             { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size); | ||||
|         memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); | ||||
|         WriteBufferC(buffer, size, buffer_index); | ||||
|     } | ||||
|  | ||||
|     return size; | ||||
| } | ||||
|  | ||||
| std::size_t HLERequestContext::WriteBufferB(const void* buffer, std::size_t size, | ||||
|                                             std::size_t buffer_index) const { | ||||
|     if (buffer_index >= BufferDescriptorB().size() || size == 0) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     const auto buffer_size{BufferDescriptorB()[buffer_index].Size()}; | ||||
|     if (size > buffer_size) { | ||||
|         LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, | ||||
|                      buffer_size); | ||||
|         size = buffer_size; // TODO(bunnei): This needs to be HW tested | ||||
|     } | ||||
|  | ||||
|     memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); | ||||
|     return size; | ||||
| } | ||||
|  | ||||
| std::size_t HLERequestContext::WriteBufferC(const void* buffer, std::size_t size, | ||||
|                                             std::size_t buffer_index) const { | ||||
|     if (buffer_index >= BufferDescriptorC().size() || size == 0) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     const auto buffer_size{BufferDescriptorC()[buffer_index].Size()}; | ||||
|     if (size > buffer_size) { | ||||
|         LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, | ||||
|                      buffer_size); | ||||
|         size = buffer_size; // TODO(bunnei): This needs to be HW tested | ||||
|     } | ||||
|  | ||||
|     memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); | ||||
|     return size; | ||||
| } | ||||
|  | ||||
| std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const { | ||||
|     const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && | ||||
|                            BufferDescriptorA()[buffer_index].Size()}; | ||||
|   | ||||
| @@ -277,6 +277,14 @@ public: | ||||
|     std::size_t WriteBuffer(const void* buffer, std::size_t size, | ||||
|                             std::size_t buffer_index = 0) const; | ||||
|  | ||||
|     /// Helper function to write buffer B | ||||
|     std::size_t WriteBufferB(const void* buffer, std::size_t size, | ||||
|                              std::size_t buffer_index = 0) const; | ||||
|  | ||||
|     /// Helper function to write buffer C | ||||
|     std::size_t WriteBufferC(const void* buffer, std::size_t size, | ||||
|                              std::size_t buffer_index = 0) const; | ||||
|  | ||||
|     /* Helper function to write a buffer using the appropriate buffer descriptor | ||||
|      * | ||||
|      * @tparam T an arbitrary container that satisfies the | ||||
|   | ||||
| @@ -95,19 +95,7 @@ struct KernelCore::Impl { | ||||
|  | ||||
|         process_list.clear(); | ||||
|  | ||||
|         // Close all open server sessions and ports. | ||||
|         std::unordered_set<KAutoObject*> server_objects_; | ||||
|         { | ||||
|             std::scoped_lock lk(server_objects_lock); | ||||
|             server_objects_ = server_objects; | ||||
|             server_objects.clear(); | ||||
|         } | ||||
|         for (auto* server_object : server_objects_) { | ||||
|             server_object->Close(); | ||||
|         } | ||||
|  | ||||
|         // Ensures all service threads gracefully shutdown. | ||||
|         ClearServiceThreads(); | ||||
|         CloseServices(); | ||||
|  | ||||
|         next_object_id = 0; | ||||
|         next_kernel_process_id = KProcess::InitialKIPIDMin; | ||||
| @@ -191,6 +179,22 @@ struct KernelCore::Impl { | ||||
|         global_object_list_container.reset(); | ||||
|     } | ||||
|  | ||||
|     void CloseServices() { | ||||
|         // Close all open server sessions and ports. | ||||
|         std::unordered_set<KAutoObject*> server_objects_; | ||||
|         { | ||||
|             std::scoped_lock lk(server_objects_lock); | ||||
|             server_objects_ = server_objects; | ||||
|             server_objects.clear(); | ||||
|         } | ||||
|         for (auto* server_object : server_objects_) { | ||||
|             server_object->Close(); | ||||
|         } | ||||
|  | ||||
|         // Ensures all service threads gracefully shutdown. | ||||
|         ClearServiceThreads(); | ||||
|     } | ||||
|  | ||||
|     void InitializePhysicalCores() { | ||||
|         exclusive_monitor = | ||||
|             Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); | ||||
| @@ -813,6 +817,10 @@ void KernelCore::Shutdown() { | ||||
|     impl->Shutdown(); | ||||
| } | ||||
|  | ||||
| void KernelCore::CloseServices() { | ||||
|     impl->CloseServices(); | ||||
| } | ||||
|  | ||||
| const KResourceLimit* KernelCore::GetSystemResourceLimit() const { | ||||
|     return impl->system_resource_limit; | ||||
| } | ||||
|   | ||||
| @@ -109,6 +109,9 @@ public: | ||||
|     /// Clears all resources in use by the kernel instance. | ||||
|     void Shutdown(); | ||||
|  | ||||
|     /// Close all active services in use by the kernel instance. | ||||
|     void CloseServices(); | ||||
|  | ||||
|     /// Retrieves a shared pointer to the system resource limit instance. | ||||
|     const KResourceLimit* GetSystemResourceLimit() const; | ||||
|  | ||||
|   | ||||
| @@ -5,7 +5,6 @@ | ||||
| #include <array> | ||||
| #include <cinttypes> | ||||
| #include <cstring> | ||||
| #include "audio_core/audio_renderer.h" | ||||
| #include "common/settings.h" | ||||
| #include "core/core.h" | ||||
| #include "core/file_sys/control_metadata.h" | ||||
| @@ -286,7 +285,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv | ||||
|         {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"}, | ||||
|         {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"}, | ||||
|         {64, nullptr, "SetInputDetectionSourceSet"}, | ||||
|         {65, nullptr, "ReportUserIsActive"}, | ||||
|         {65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"}, | ||||
|         {66, nullptr, "GetCurrentIlluminance"}, | ||||
|         {67, nullptr, "IsIlluminanceAvailable"}, | ||||
|         {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"}, | ||||
| @@ -518,6 +517,13 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c | ||||
|     rb.Push<u32>(idle_time_detection_extension); | ||||
| } | ||||
|  | ||||
| void ISelfController::ReportUserIsActive(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_WARNING(Service_AM, "(STUBBED) called"); | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
|     rb.Push(ResultSuccess); | ||||
| } | ||||
|  | ||||
| void ISelfController::SetAutoSleepDisabled(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     is_auto_sleep_disabled = rp.Pop<bool>(); | ||||
|   | ||||
| @@ -175,6 +175,7 @@ private: | ||||
|     void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); | ||||
|     void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); | ||||
|     void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); | ||||
|     void ReportUserIsActive(Kernel::HLERequestContext& ctx); | ||||
|     void SetAutoSleepDisabled(Kernel::HLERequestContext& ctx); | ||||
|     void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx); | ||||
|     void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); | ||||
|   | ||||
| @@ -1,68 +1,211 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include "audio_core/in/audio_in_system.h" | ||||
| #include "audio_core/renderer/audio_device.h" | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/string_util.h" | ||||
| #include "core/core.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/kernel/k_event.h" | ||||
| #include "core/hle/service/audio/audin_u.h" | ||||
|  | ||||
| namespace Service::Audio { | ||||
| using namespace AudioCore::AudioIn; | ||||
|  | ||||
| IAudioIn::IAudioIn(Core::System& system_) | ||||
|     : ServiceFramework{system_, "IAudioIn"}, service_context{system_, "IAudioIn"} { | ||||
|     // clang-format off | ||||
|     static const FunctionInfo functions[] = { | ||||
|         {0, nullptr, "GetAudioInState"}, | ||||
|         {1, &IAudioIn::Start, "Start"}, | ||||
|         {2, nullptr, "Stop"}, | ||||
|         {3, nullptr, "AppendAudioInBuffer"}, | ||||
|         {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"}, | ||||
|         {5, nullptr, "GetReleasedAudioInBuffer"}, | ||||
|         {6, nullptr, "ContainsAudioInBuffer"}, | ||||
|         {7, nullptr, "AppendUacInBuffer"}, | ||||
|         {8, &IAudioIn::AppendAudioInBufferAuto, "AppendAudioInBufferAuto"}, | ||||
|         {9, nullptr, "GetReleasedAudioInBuffersAuto"}, | ||||
|         {10, nullptr, "AppendUacInBufferAuto"}, | ||||
|         {11, nullptr, "GetAudioInBufferCount"}, | ||||
|         {12, nullptr, "SetDeviceGain"}, | ||||
|         {13, nullptr, "GetDeviceGain"}, | ||||
|         {14, nullptr, "FlushAudioInBuffers"}, | ||||
|     }; | ||||
|     // clang-format on | ||||
| class IAudioIn final : public ServiceFramework<IAudioIn> { | ||||
| public: | ||||
|     explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id, | ||||
|                       std::string& device_name, const AudioInParameter& in_params, u32 handle, | ||||
|                       u64 applet_resource_user_id) | ||||
|         : ServiceFramework{system_, "IAudioIn"}, | ||||
|           service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")}, | ||||
|           impl{std::make_shared<In>(system_, manager, event, session_id)} { | ||||
|         // clang-format off | ||||
|         static const FunctionInfo functions[] = { | ||||
|             {0, &IAudioIn::GetAudioInState, "GetAudioInState"}, | ||||
|             {1, &IAudioIn::Start, "Start"}, | ||||
|             {2, &IAudioIn::Stop, "Stop"}, | ||||
|             {3, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBuffer"}, | ||||
|             {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"}, | ||||
|             {5, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffer"}, | ||||
|             {6, &IAudioIn::ContainsAudioInBuffer, "ContainsAudioInBuffer"}, | ||||
|             {7, &IAudioIn::AppendAudioInBuffer, "AppendUacInBuffer"}, | ||||
|             {8, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBufferAuto"}, | ||||
|             {9, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffersAuto"}, | ||||
|             {10, &IAudioIn::AppendAudioInBuffer, "AppendUacInBufferAuto"}, | ||||
|             {11, &IAudioIn::GetAudioInBufferCount, "GetAudioInBufferCount"}, | ||||
|             {12, &IAudioIn::SetDeviceGain, "SetDeviceGain"}, | ||||
|             {13, &IAudioIn::GetDeviceGain, "GetDeviceGain"}, | ||||
|             {14, &IAudioIn::FlushAudioInBuffers, "FlushAudioInBuffers"}, | ||||
|         }; | ||||
|         // clang-format on | ||||
|  | ||||
|     RegisterHandlers(functions); | ||||
|         RegisterHandlers(functions); | ||||
|  | ||||
|     buffer_event = service_context.CreateEvent("IAudioIn:BufferEvent"); | ||||
| } | ||||
|         if (impl->GetSystem() | ||||
|                 .Initialize(device_name, in_params, handle, applet_resource_user_id) | ||||
|                 .IsError()) { | ||||
|             LOG_ERROR(Service_Audio, "Failed to initialize the AudioIn System!"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| IAudioIn::~IAudioIn() { | ||||
|     service_context.CloseEvent(buffer_event); | ||||
| } | ||||
|     ~IAudioIn() override { | ||||
|         impl->Free(); | ||||
|         service_context.CloseEvent(event); | ||||
|     } | ||||
|  | ||||
| void IAudioIn::Start(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_WARNING(Service_Audio, "(STUBBED) called"); | ||||
|     [[nodiscard]] std::shared_ptr<In> GetImpl() { | ||||
|         return impl; | ||||
|     } | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
|     rb.Push(ResultSuccess); | ||||
| } | ||||
| private: | ||||
|     void GetAudioInState(Kernel::HLERequestContext& ctx) { | ||||
|         const auto state = static_cast<u32>(impl->GetState()); | ||||
|  | ||||
| void IAudioIn::RegisterBufferEvent(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_WARNING(Service_Audio, "(STUBBED) called"); | ||||
|         LOG_DEBUG(Service_Audio, "called. State={}", state); | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2, 1}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.PushCopyObjects(buffer_event->GetReadableEvent()); | ||||
| } | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(state); | ||||
|     } | ||||
|  | ||||
| void IAudioIn::AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_WARNING(Service_Audio, "(STUBBED) called"); | ||||
|     void Start(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
|     rb.Push(ResultSuccess); | ||||
| } | ||||
|         auto result = impl->StartSystem(); | ||||
|  | ||||
| AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} { | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(result); | ||||
|     } | ||||
|  | ||||
|     void Stop(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|         auto result = impl->StopSystem(); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(result); | ||||
|     } | ||||
|  | ||||
|     void AppendAudioInBuffer(Kernel::HLERequestContext& ctx) { | ||||
|         IPC::RequestParser rp{ctx}; | ||||
|         u64 tag = rp.PopRaw<u64>(); | ||||
|  | ||||
|         const auto in_buffer_size{ctx.GetReadBufferSize()}; | ||||
|         if (in_buffer_size < sizeof(AudioInBuffer)) { | ||||
|             LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!"); | ||||
|         } | ||||
|  | ||||
|         const auto& in_buffer = ctx.ReadBuffer(); | ||||
|         AudioInBuffer buffer{}; | ||||
|         std::memcpy(&buffer, in_buffer.data(), sizeof(AudioInBuffer)); | ||||
|  | ||||
|         [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; | ||||
|         LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag); | ||||
|  | ||||
|         auto result = impl->AppendBuffer(buffer, tag); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(result); | ||||
|     } | ||||
|  | ||||
|     void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|         auto& buffer_event = impl->GetBufferEvent(); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2, 1}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.PushCopyObjects(buffer_event); | ||||
|     } | ||||
|  | ||||
|     void GetReleasedAudioInBuffer(Kernel::HLERequestContext& ctx) { | ||||
|         auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64); | ||||
|         std::vector<u64> released_buffers(write_buffer_size, 0); | ||||
|  | ||||
|         auto count = impl->GetReleasedBuffers(released_buffers); | ||||
|  | ||||
|         [[maybe_unused]] std::string tags{}; | ||||
|         for (u32 i = 0; i < count; i++) { | ||||
|             tags += fmt::format("{:08X}, ", released_buffers[i]); | ||||
|         } | ||||
|         [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; | ||||
|         LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count, | ||||
|                   tags); | ||||
|  | ||||
|         ctx.WriteBuffer(released_buffers); | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(count); | ||||
|     } | ||||
|  | ||||
|     void ContainsAudioInBuffer(Kernel::HLERequestContext& ctx) { | ||||
|         IPC::RequestParser rp{ctx}; | ||||
|  | ||||
|         const u64 tag{rp.Pop<u64>()}; | ||||
|         const auto buffer_queued{impl->ContainsAudioBuffer(tag)}; | ||||
|  | ||||
|         LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(buffer_queued); | ||||
|     } | ||||
|  | ||||
|     void GetAudioInBufferCount(Kernel::HLERequestContext& ctx) { | ||||
|         const auto buffer_count = impl->GetBufferCount(); | ||||
|  | ||||
|         LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|  | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(buffer_count); | ||||
|     } | ||||
|  | ||||
|     void SetDeviceGain(Kernel::HLERequestContext& ctx) { | ||||
|         IPC::RequestParser rp{ctx}; | ||||
|  | ||||
|         const auto volume{rp.Pop<f32>()}; | ||||
|         LOG_DEBUG(Service_Audio, "called. Gain {}", volume); | ||||
|  | ||||
|         impl->SetVolume(volume); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(ResultSuccess); | ||||
|     } | ||||
|  | ||||
|     void GetDeviceGain(Kernel::HLERequestContext& ctx) { | ||||
|         auto volume{impl->GetVolume()}; | ||||
|  | ||||
|         LOG_DEBUG(Service_Audio, "called. Gain {}", volume); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(volume); | ||||
|     } | ||||
|  | ||||
|     void FlushAudioInBuffers(Kernel::HLERequestContext& ctx) { | ||||
|         bool flushed{impl->FlushAudioInBuffers()}; | ||||
|  | ||||
|         LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(flushed); | ||||
|     } | ||||
|  | ||||
|     KernelHelpers::ServiceContext service_context; | ||||
|     Kernel::KEvent* event; | ||||
|     std::shared_ptr<AudioCore::AudioIn::In> impl; | ||||
| }; | ||||
|  | ||||
| AudInU::AudInU(Core::System& system_) | ||||
|     : ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew}, | ||||
|       service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>( | ||||
|                                               system_)} { | ||||
|     // clang-format off | ||||
|     static const FunctionInfo functions[] = { | ||||
|         {0, &AudInU::ListAudioIns, "ListAudioIns"}, | ||||
| @@ -80,59 +223,152 @@ AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} { | ||||
| AudInU::~AudInU() = default; | ||||
|  | ||||
| void AudInU::ListAudioIns(Kernel::HLERequestContext& ctx) { | ||||
|     using namespace AudioCore::AudioRenderer; | ||||
|  | ||||
|     LOG_DEBUG(Service_Audio, "called"); | ||||
|     const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioInDeviceName); | ||||
|  | ||||
|     const std::size_t device_count = std::min(count, audio_device_names.size()); | ||||
|     std::vector<AudioInDeviceName> device_names; | ||||
|     device_names.reserve(device_count); | ||||
|     const auto write_count = | ||||
|         static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); | ||||
|     std::vector<AudioDevice::AudioDeviceName> device_names{}; | ||||
|  | ||||
|     for (std::size_t i = 0; i < device_count; i++) { | ||||
|         const auto& device_name = audio_device_names[i]; | ||||
|         auto& entry = device_names.emplace_back(); | ||||
|         device_name.copy(entry.data(), device_name.size()); | ||||
|     u32 out_count{0}; | ||||
|     if (write_count > 0) { | ||||
|         out_count = impl->GetDeviceNames(device_names, write_count, false); | ||||
|         ctx.WriteBuffer(device_names); | ||||
|     } | ||||
|  | ||||
|     ctx.WriteBuffer(device_names); | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 3}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(static_cast<u32>(device_names.size())); | ||||
|     rb.Push(out_count); | ||||
| } | ||||
|  | ||||
| void AudInU::ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_DEBUG(Service_Audio, "called"); | ||||
|     constexpr u32 device_count = 0; | ||||
|     using namespace AudioCore::AudioRenderer; | ||||
|  | ||||
|     // Since we don't actually use any other audio input devices, we return 0 devices. Filtered | ||||
|     // device listing just omits the default input device | ||||
|     LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|     const auto write_count = | ||||
|         static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); | ||||
|     std::vector<AudioDevice::AudioDeviceName> device_names{}; | ||||
|  | ||||
|     u32 out_count{0}; | ||||
|     if (write_count > 0) { | ||||
|         out_count = impl->GetDeviceNames(device_names, write_count, true); | ||||
|         ctx.WriteBuffer(device_names); | ||||
|     } | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 3}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(static_cast<u32>(device_count)); | ||||
| } | ||||
|  | ||||
| void AudInU::OpenInOutImpl(Kernel::HLERequestContext& ctx) { | ||||
|     AudInOutParams params{}; | ||||
|     params.channel_count = 2; | ||||
|     params.sample_format = SampleFormat::PCM16; | ||||
|     params.sample_rate = 48000; | ||||
|     params.state = State::Started; | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 6, 0, 1}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.PushRaw<AudInOutParams>(params); | ||||
|     rb.PushIpcInterface<IAudioIn>(system); | ||||
|     rb.Push(out_count); | ||||
| } | ||||
|  | ||||
| void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_WARNING(Service_Audio, "(STUBBED) called"); | ||||
|     OpenInOutImpl(ctx); | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     auto in_params{rp.PopRaw<AudioInParameter>()}; | ||||
|     auto applet_resource_user_id{rp.PopRaw<u64>()}; | ||||
|     const auto device_name_data{ctx.ReadBuffer()}; | ||||
|     auto device_name = Common::StringFromBuffer(device_name_data); | ||||
|     auto handle{ctx.GetCopyHandle(0)}; | ||||
|  | ||||
|     std::scoped_lock l{impl->mutex}; | ||||
|     auto link{impl->LinkToManager()}; | ||||
|     if (link.IsError()) { | ||||
|         LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager"); | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(link); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     size_t new_session_id{}; | ||||
|     auto result{impl->AcquireSessionId(new_session_id)}; | ||||
|     if (result.IsError()) { | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(result); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, | ||||
|               impl->num_free_sessions); | ||||
|  | ||||
|     auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, | ||||
|                                                in_params, handle, applet_resource_user_id); | ||||
|     impl->sessions[new_session_id] = audio_in->GetImpl(); | ||||
|     impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; | ||||
|  | ||||
|     auto& out_system = impl->sessions[new_session_id]->GetSystem(); | ||||
|     AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(), | ||||
|                                         .channel_count = out_system.GetChannelCount(), | ||||
|                                         .sample_format = | ||||
|                                             static_cast<u32>(out_system.GetSampleFormat()), | ||||
|                                         .state = static_cast<u32>(out_system.GetState())}; | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 6, 0, 1}; | ||||
|  | ||||
|     std::string out_name{out_system.GetName()}; | ||||
|     ctx.WriteBuffer(out_name); | ||||
|  | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.PushRaw<AudioInParameterInternal>(out_params); | ||||
|     rb.PushIpcInterface<IAudioIn>(audio_in); | ||||
| } | ||||
|  | ||||
| void AudInU::OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_WARNING(Service_Audio, "(STUBBED) called"); | ||||
|     OpenInOutImpl(ctx); | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     auto protocol_specified{rp.PopRaw<u64>()}; | ||||
|     auto in_params{rp.PopRaw<AudioInParameter>()}; | ||||
|     auto applet_resource_user_id{rp.PopRaw<u64>()}; | ||||
|     const auto device_name_data{ctx.ReadBuffer()}; | ||||
|     auto device_name = Common::StringFromBuffer(device_name_data); | ||||
|     auto handle{ctx.GetCopyHandle(0)}; | ||||
|  | ||||
|     std::scoped_lock l{impl->mutex}; | ||||
|     auto link{impl->LinkToManager()}; | ||||
|     if (link.IsError()) { | ||||
|         LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager"); | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(link); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     size_t new_session_id{}; | ||||
|     auto result{impl->AcquireSessionId(new_session_id)}; | ||||
|     if (result.IsError()) { | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(result); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, | ||||
|               impl->num_free_sessions); | ||||
|  | ||||
|     auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, | ||||
|                                                in_params, handle, applet_resource_user_id); | ||||
|     impl->sessions[new_session_id] = audio_in->GetImpl(); | ||||
|     impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; | ||||
|  | ||||
|     auto& out_system = impl->sessions[new_session_id]->GetSystem(); | ||||
|     AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(), | ||||
|                                         .channel_count = out_system.GetChannelCount(), | ||||
|                                         .sample_format = | ||||
|                                             static_cast<u32>(out_system.GetSampleFormat()), | ||||
|                                         .state = static_cast<u32>(out_system.GetState())}; | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 6, 0, 1}; | ||||
|  | ||||
|     std::string out_name{out_system.GetName()}; | ||||
|     if (protocol_specified == 0) { | ||||
|         if (out_system.IsUac()) { | ||||
|             out_name = "UacIn"; | ||||
|         } else { | ||||
|             out_name = "DeviceIn"; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ctx.WriteBuffer(out_name); | ||||
|  | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.PushRaw<AudioInParameterInternal>(out_params); | ||||
|     rb.PushIpcInterface<IAudioIn>(audio_in); | ||||
| } | ||||
|  | ||||
| } // namespace Service::Audio | ||||
|   | ||||
| @@ -3,6 +3,8 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "audio_core/audio_in_manager.h" | ||||
| #include "audio_core/in/audio_in.h" | ||||
| #include "core/hle/service/kernel_helpers.h" | ||||
| #include "core/hle/service/service.h" | ||||
|  | ||||
| @@ -14,56 +16,27 @@ namespace Kernel { | ||||
| class HLERequestContext; | ||||
| } | ||||
|  | ||||
| namespace AudioCore::AudioOut { | ||||
| class Manager; | ||||
| class In; | ||||
| } // namespace AudioCore::AudioOut | ||||
|  | ||||
| namespace Service::Audio { | ||||
|  | ||||
| class IAudioIn final : public ServiceFramework<IAudioIn> { | ||||
| public: | ||||
|     explicit IAudioIn(Core::System& system_); | ||||
|     ~IAudioIn() override; | ||||
|  | ||||
| private: | ||||
|     void Start(Kernel::HLERequestContext& ctx); | ||||
|     void RegisterBufferEvent(Kernel::HLERequestContext& ctx); | ||||
|     void AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx); | ||||
|  | ||||
|     KernelHelpers::ServiceContext service_context; | ||||
|  | ||||
|     Kernel::KEvent* buffer_event; | ||||
| }; | ||||
|  | ||||
| class AudInU final : public ServiceFramework<AudInU> { | ||||
| public: | ||||
|     explicit AudInU(Core::System& system_); | ||||
|     ~AudInU() override; | ||||
|  | ||||
| private: | ||||
|     enum class SampleFormat : u32_le { | ||||
|         PCM16 = 2, | ||||
|     }; | ||||
|  | ||||
|     enum class State : u32_le { | ||||
|         Started = 0, | ||||
|         Stopped = 1, | ||||
|     }; | ||||
|  | ||||
|     struct AudInOutParams { | ||||
|         u32_le sample_rate{}; | ||||
|         u32_le channel_count{}; | ||||
|         SampleFormat sample_format{}; | ||||
|         State state{}; | ||||
|     }; | ||||
|     static_assert(sizeof(AudInOutParams) == 0x10, "AudInOutParams is an invalid size"); | ||||
|  | ||||
|     using AudioInDeviceName = std::array<char, 256>; | ||||
|     static constexpr std::array<std::string_view, 1> audio_device_names{{ | ||||
|         "BuiltInHeadset", | ||||
|     }}; | ||||
|  | ||||
|     void ListAudioIns(Kernel::HLERequestContext& ctx); | ||||
|     void ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx); | ||||
|     void OpenInOutImpl(Kernel::HLERequestContext& ctx); | ||||
|     void OpenAudioIn(Kernel::HLERequestContext& ctx); | ||||
|     void OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx); | ||||
|  | ||||
|     KernelHelpers::ServiceContext service_context; | ||||
|     std::unique_ptr<AudioCore::AudioIn::Manager> impl; | ||||
| }; | ||||
|  | ||||
| } // namespace Service::Audio | ||||
|   | ||||
| @@ -5,56 +5,43 @@ | ||||
| #include <cstring> | ||||
| #include <vector> | ||||
|  | ||||
| #include "audio_core/audio_out.h" | ||||
| #include "audio_core/codec.h" | ||||
| #include "audio_core/out/audio_out_system.h" | ||||
| #include "audio_core/renderer/audio_device.h" | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/string_util.h" | ||||
| #include "common/swap.h" | ||||
| #include "core/core.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/kernel/k_event.h" | ||||
| #include "core/hle/service/audio/audout_u.h" | ||||
| #include "core/hle/service/audio/errors.h" | ||||
| #include "core/hle/service/kernel_helpers.h" | ||||
| #include "core/memory.h" | ||||
|  | ||||
| namespace Service::Audio { | ||||
|  | ||||
| constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}}; | ||||
| constexpr int DefaultSampleRate{48000}; | ||||
|  | ||||
| struct AudoutParams { | ||||
|     s32_le sample_rate; | ||||
|     u16_le channel_count; | ||||
|     INSERT_PADDING_BYTES_NOINIT(2); | ||||
| }; | ||||
| static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size"); | ||||
|  | ||||
| enum class AudioState : u32 { | ||||
|     Started, | ||||
|     Stopped, | ||||
| }; | ||||
| using namespace AudioCore::AudioOut; | ||||
|  | ||||
| class IAudioOut final : public ServiceFramework<IAudioOut> { | ||||
| public: | ||||
|     explicit IAudioOut(Core::System& system_, AudoutParams audio_params_, | ||||
|                        AudioCore::AudioOut& audio_core_, std::string&& device_name_, | ||||
|                        std::string&& unique_name) | ||||
|     explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, | ||||
|                        size_t session_id, std::string& device_name, | ||||
|                        const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) | ||||
|         : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, | ||||
|           audio_core{audio_core_}, device_name{std::move(device_name_)}, | ||||
|           audio_params{audio_params_}, main_memory{system.Memory()}, service_context{system_, | ||||
|                                                                                      "IAudioOut"} { | ||||
|           service_context{system_, "IAudioOut"}, event{service_context.CreateEvent( | ||||
|                                                      "AudioOutEvent")}, | ||||
|           impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { | ||||
|  | ||||
|         // clang-format off | ||||
|         static const FunctionInfo functions[] = { | ||||
|             {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, | ||||
|             {1, &IAudioOut::StartAudioOut, "Start"}, | ||||
|             {2, &IAudioOut::StopAudioOut, "Stop"}, | ||||
|             {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"}, | ||||
|             {1, &IAudioOut::Start, "Start"}, | ||||
|             {2, &IAudioOut::Stop, "Stop"}, | ||||
|             {3, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBuffer"}, | ||||
|             {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"}, | ||||
|             {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffers"}, | ||||
|             {5, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffers"}, | ||||
|             {6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"}, | ||||
|             {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"}, | ||||
|             {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"}, | ||||
|             {7, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBufferAuto"}, | ||||
|             {8, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffersAuto"}, | ||||
|             {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"}, | ||||
|             {10, &IAudioOut::GetAudioOutPlayedSampleCount, "GetAudioOutPlayedSampleCount"}, | ||||
|             {11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"}, | ||||
| @@ -64,241 +51,263 @@ public: | ||||
|         // clang-format on | ||||
|         RegisterHandlers(functions); | ||||
|  | ||||
|         // This is the event handle used to check if the audio buffer was released | ||||
|         buffer_event = service_context.CreateEvent("IAudioOutBufferReleased"); | ||||
|  | ||||
|         stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, | ||||
|                                        audio_params.channel_count, std::move(unique_name), [this] { | ||||
|                                            const auto guard = LockService(); | ||||
|                                            buffer_event->GetWritableEvent().Signal(); | ||||
|                                        }); | ||||
|         if (impl->GetSystem() | ||||
|                 .Initialize(device_name, in_params, handle, applet_resource_user_id) | ||||
|                 .IsError()) { | ||||
|             LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ~IAudioOut() override { | ||||
|         service_context.CloseEvent(buffer_event); | ||||
|         impl->Free(); | ||||
|         service_context.CloseEvent(event); | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() { | ||||
|         return impl; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     struct AudioBuffer { | ||||
|         u64_le next; | ||||
|         u64_le buffer; | ||||
|         u64_le buffer_capacity; | ||||
|         u64_le buffer_size; | ||||
|         u64_le offset; | ||||
|     }; | ||||
|     static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size"); | ||||
|  | ||||
|     void GetAudioOutState(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|         const auto state = static_cast<u32>(impl->GetState()); | ||||
|  | ||||
|         LOG_DEBUG(Service_Audio, "called. State={}", state); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped)); | ||||
|         rb.Push(state); | ||||
|     } | ||||
|  | ||||
|     void StartAudioOut(Kernel::HLERequestContext& ctx) { | ||||
|     void Start(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|         if (stream->IsPlaying()) { | ||||
|             IPC::ResponseBuilder rb{ctx, 2}; | ||||
|             rb.Push(ERR_OPERATION_FAILED); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         audio_core.StartStream(stream); | ||||
|         auto result = impl->StartSystem(); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(result); | ||||
|     } | ||||
|  | ||||
|     void StopAudioOut(Kernel::HLERequestContext& ctx) { | ||||
|     void Stop(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|         if (stream->IsPlaying()) { | ||||
|             audio_core.StopStream(stream); | ||||
|         } | ||||
|         auto result = impl->StopSystem(); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(result); | ||||
|     } | ||||
|  | ||||
|     void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) { | ||||
|         IPC::RequestParser rp{ctx}; | ||||
|         u64 tag = rp.PopRaw<u64>(); | ||||
|  | ||||
|         const auto in_buffer_size{ctx.GetReadBufferSize()}; | ||||
|         if (in_buffer_size < sizeof(AudioOutBuffer)) { | ||||
|             LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!"); | ||||
|         } | ||||
|  | ||||
|         const auto& in_buffer = ctx.ReadBuffer(); | ||||
|         AudioOutBuffer buffer{}; | ||||
|         std::memcpy(&buffer, in_buffer.data(), sizeof(AudioOutBuffer)); | ||||
|  | ||||
|         [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; | ||||
|         LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag); | ||||
|  | ||||
|         auto result = impl->AppendBuffer(buffer, tag); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(result); | ||||
|     } | ||||
|  | ||||
|     void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|         auto& buffer_event = impl->GetBufferEvent(); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2, 1}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.PushCopyObjects(buffer_event->GetReadableEvent()); | ||||
|         rb.PushCopyObjects(buffer_event); | ||||
|     } | ||||
|  | ||||
|     void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description()); | ||||
|         IPC::RequestParser rp{ctx}; | ||||
|     void GetReleasedAudioOutBuffers(Kernel::HLERequestContext& ctx) { | ||||
|         auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64); | ||||
|         std::vector<u64> released_buffers(write_buffer_size, 0); | ||||
|  | ||||
|         const auto& input_buffer{ctx.ReadBuffer()}; | ||||
|         ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer), | ||||
|                    "AudioBuffer input is an invalid size!"); | ||||
|         AudioBuffer audio_buffer{}; | ||||
|         std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer)); | ||||
|         const u64 tag{rp.Pop<u64>()}; | ||||
|         auto count = impl->GetReleasedBuffers(released_buffers); | ||||
|  | ||||
|         std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16)); | ||||
|         main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size); | ||||
|  | ||||
|         if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) { | ||||
|             IPC::ResponseBuilder rb{ctx, 2}; | ||||
|             rb.Push(ERR_BUFFER_COUNT_EXCEEDED); | ||||
|             return; | ||||
|         [[maybe_unused]] std::string tags{}; | ||||
|         for (u32 i = 0; i < count; i++) { | ||||
|             tags += fmt::format("{:08X}, ", released_buffers[i]); | ||||
|         } | ||||
|         [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; | ||||
|         LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count, | ||||
|                   tags); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(ResultSuccess); | ||||
|     } | ||||
|  | ||||
|     void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called {}", ctx.Description()); | ||||
|  | ||||
|         const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)}; | ||||
|         const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)}; | ||||
|  | ||||
|         std::vector<u64> tags{released_buffers}; | ||||
|         tags.resize(max_count); | ||||
|         ctx.WriteBuffer(tags); | ||||
|  | ||||
|         ctx.WriteBuffer(released_buffers); | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push<u32>(static_cast<u32>(released_buffers.size())); | ||||
|         rb.Push(count); | ||||
|     } | ||||
|  | ||||
|     void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|         IPC::RequestParser rp{ctx}; | ||||
|  | ||||
|         const u64 tag{rp.Pop<u64>()}; | ||||
|         const auto buffer_queued{impl->ContainsAudioBuffer(tag)}; | ||||
|  | ||||
|         LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(stream->ContainsBuffer(tag)); | ||||
|         rb.Push(buffer_queued); | ||||
|     } | ||||
|  | ||||
|     void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|         const auto buffer_count = impl->GetBufferCount(); | ||||
|  | ||||
|         LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|  | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(static_cast<u32>(stream->GetQueueSize())); | ||||
|         rb.Push(buffer_count); | ||||
|     } | ||||
|  | ||||
|     void GetAudioOutPlayedSampleCount(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|         const auto samples_played = impl->GetPlayedSampleCount(); | ||||
|  | ||||
|         LOG_DEBUG(Service_Audio, "called. Played samples={}", samples_played); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 4}; | ||||
|  | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(stream->GetPlayedSampleCount()); | ||||
|         rb.Push(samples_played); | ||||
|     } | ||||
|  | ||||
|     void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|         bool flushed{impl->FlushAudioOutBuffers()}; | ||||
|  | ||||
|         LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(stream->Flush()); | ||||
|         rb.Push(flushed); | ||||
|     } | ||||
|  | ||||
|     void SetAudioOutVolume(Kernel::HLERequestContext& ctx) { | ||||
|         IPC::RequestParser rp{ctx}; | ||||
|         const float volume = rp.Pop<float>(); | ||||
|         LOG_DEBUG(Service_Audio, "called, volume={}", volume); | ||||
|         const auto volume = rp.Pop<f32>(); | ||||
|  | ||||
|         stream->SetVolume(volume); | ||||
|         LOG_DEBUG(Service_Audio, "called. Volume={}", volume); | ||||
|  | ||||
|         impl->SetVolume(volume); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(ResultSuccess); | ||||
|     } | ||||
|  | ||||
|     void GetAudioOutVolume(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|         const auto volume = impl->GetVolume(); | ||||
|  | ||||
|         LOG_DEBUG(Service_Audio, "called. Volume={}", volume); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(stream->GetVolume()); | ||||
|         rb.Push(volume); | ||||
|     } | ||||
|  | ||||
|     AudioCore::AudioOut& audio_core; | ||||
|     AudioCore::StreamPtr stream; | ||||
|     std::string device_name; | ||||
|  | ||||
|     [[maybe_unused]] AudoutParams audio_params{}; | ||||
|  | ||||
|     Core::Memory::Memory& main_memory; | ||||
|  | ||||
|     KernelHelpers::ServiceContext service_context; | ||||
|  | ||||
|     /// This is the event handle used to check if the audio buffer was released | ||||
|     Kernel::KEvent* buffer_event; | ||||
|     Kernel::KEvent* event; | ||||
|     std::shared_ptr<AudioCore::AudioOut::Out> impl; | ||||
| }; | ||||
|  | ||||
| AudOutU::AudOutU(Core::System& system_) : ServiceFramework{system_, "audout:u"} { | ||||
| AudOutU::AudOutU(Core::System& system_) | ||||
|     : ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew}, | ||||
|       service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>( | ||||
|                                                system_)} { | ||||
|     // clang-format off | ||||
|     static const FunctionInfo functions[] = { | ||||
|         {0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"}, | ||||
|         {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"}, | ||||
|         {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"}, | ||||
|         {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}, | ||||
|         {0, &AudOutU::ListAudioOuts, "ListAudioOuts"}, | ||||
|         {1, &AudOutU::OpenAudioOut, "OpenAudioOut"}, | ||||
|         {2, &AudOutU::ListAudioOuts, "ListAudioOutsAuto"}, | ||||
|         {3, &AudOutU::OpenAudioOut, "OpenAudioOutAuto"}, | ||||
|     }; | ||||
|     // clang-format on | ||||
|  | ||||
|     RegisterHandlers(functions); | ||||
|     audio_core = std::make_unique<AudioCore::AudioOut>(); | ||||
| } | ||||
|  | ||||
| AudOutU::~AudOutU() = default; | ||||
|  | ||||
| void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_DEBUG(Service_Audio, "called"); | ||||
| void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { | ||||
|     using namespace AudioCore::AudioRenderer; | ||||
|  | ||||
|     ctx.WriteBuffer(DefaultDevice); | ||||
|     std::scoped_lock l{impl->mutex}; | ||||
|  | ||||
|     const auto write_count = | ||||
|         static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); | ||||
|     std::vector<AudioDevice::AudioDeviceName> device_names{}; | ||||
|     std::string print_names{}; | ||||
|     if (write_count > 0) { | ||||
|         device_names.push_back(AudioDevice::AudioDeviceName("DeviceOut")); | ||||
|         LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut"); | ||||
|     } else { | ||||
|         LOG_DEBUG(Service_Audio, "called. Empty buffer passed in."); | ||||
|     } | ||||
|  | ||||
|     ctx.WriteBuffer(device_names); | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 3}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push<u32>(1); // Amount of audio devices | ||||
|     rb.Push<u32>(static_cast<u32>(device_names.size())); | ||||
| } | ||||
|  | ||||
| void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|     const auto device_name_data{ctx.ReadBuffer()}; | ||||
|     std::string device_name; | ||||
|     if (device_name_data[0] != '\0') { | ||||
|         device_name.assign(device_name_data.begin(), device_name_data.end()); | ||||
|     } else { | ||||
|         device_name.assign(DefaultDevice.begin(), DefaultDevice.end()); | ||||
|     } | ||||
|     ctx.WriteBuffer(device_name); | ||||
|  | ||||
| void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     auto params{rp.PopRaw<AudoutParams>()}; | ||||
|     if (params.channel_count <= 2) { | ||||
|         // Mono does not exist for audout | ||||
|         params.channel_count = 2; | ||||
|     } else { | ||||
|         params.channel_count = 6; | ||||
|     } | ||||
|     if (!params.sample_rate) { | ||||
|         params.sample_rate = DefaultSampleRate; | ||||
|     auto in_params{rp.PopRaw<AudioOutParameter>()}; | ||||
|     auto applet_resource_user_id{rp.PopRaw<u64>()}; | ||||
|     const auto device_name_data{ctx.ReadBuffer()}; | ||||
|     auto device_name = Common::StringFromBuffer(device_name_data); | ||||
|     auto handle{ctx.GetCopyHandle(0)}; | ||||
|  | ||||
|     auto link{impl->LinkToManager()}; | ||||
|     if (link.IsError()) { | ||||
|         LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager"); | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(link); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())}; | ||||
|     auto audio_out_interface = std::make_shared<IAudioOut>( | ||||
|         system, params, *audio_core, std::move(device_name), std::move(unique_name)); | ||||
|     size_t new_session_id{}; | ||||
|     auto result{impl->AcquireSessionId(new_session_id)}; | ||||
|     if (result.IsError()) { | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(result); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id, | ||||
|               impl->num_free_sessions); | ||||
|  | ||||
|     auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name, | ||||
|                                                  in_params, handle, applet_resource_user_id); | ||||
|  | ||||
|     impl->sessions[new_session_id] = audio_out->GetImpl(); | ||||
|     impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; | ||||
|  | ||||
|     auto& out_system = impl->sessions[new_session_id]->GetSystem(); | ||||
|     AudioOutParameterInternal out_params{.sample_rate = out_system.GetSampleRate(), | ||||
|                                          .channel_count = out_system.GetChannelCount(), | ||||
|                                          .sample_format = | ||||
|                                              static_cast<u32>(out_system.GetSampleFormat()), | ||||
|                                          .state = static_cast<u32>(out_system.GetState())}; | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 6, 0, 1}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push<u32>(DefaultSampleRate); | ||||
|     rb.Push<u32>(params.channel_count); | ||||
|     rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16)); | ||||
|     rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); | ||||
|     rb.PushIpcInterface<IAudioOut>(audio_out_interface); | ||||
|  | ||||
|     audio_out_interfaces.push_back(std::move(audio_out_interface)); | ||||
|     ctx.WriteBuffer(out_system.GetName()); | ||||
|  | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.PushRaw<AudioOutParameterInternal>(out_params); | ||||
|     rb.PushIpcInterface<IAudioOut>(audio_out); | ||||
| } | ||||
|  | ||||
| } // namespace Service::Audio | ||||
|   | ||||
| @@ -3,13 +3,11 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <vector> | ||||
| #include "audio_core/audio_out_manager.h" | ||||
| #include "audio_core/out/audio_out.h" | ||||
| #include "core/hle/service/kernel_helpers.h" | ||||
| #include "core/hle/service/service.h" | ||||
|  | ||||
| namespace AudioCore { | ||||
| class AudioOut; | ||||
| } | ||||
|  | ||||
| namespace Core { | ||||
| class System; | ||||
| } | ||||
| @@ -18,6 +16,11 @@ namespace Kernel { | ||||
| class HLERequestContext; | ||||
| } | ||||
|  | ||||
| namespace AudioCore::AudioOut { | ||||
| class Manager; | ||||
| class Out; | ||||
| } // namespace AudioCore::AudioOut | ||||
|  | ||||
| namespace Service::Audio { | ||||
|  | ||||
| class IAudioOut; | ||||
| @@ -28,11 +31,11 @@ public: | ||||
|     ~AudOutU() override; | ||||
|  | ||||
| private: | ||||
|     void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); | ||||
|     void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); | ||||
|     void ListAudioOuts(Kernel::HLERequestContext& ctx); | ||||
|     void OpenAudioOut(Kernel::HLERequestContext& ctx); | ||||
|  | ||||
|     std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces; | ||||
|     std::unique_ptr<AudioCore::AudioOut> audio_core; | ||||
|     KernelHelpers::ServiceContext service_context; | ||||
|     std::unique_ptr<AudioCore::AudioOut::Manager> impl; | ||||
| }; | ||||
|  | ||||
| } // namespace Service::Audio | ||||
|   | ||||
| @@ -4,7 +4,12 @@ | ||||
| #include <array> | ||||
| #include <memory> | ||||
|  | ||||
| #include "audio_core/audio_renderer.h" | ||||
| #include "audio_core/audio_core.h" | ||||
| #include "audio_core/common/audio_renderer_parameter.h" | ||||
| #include "audio_core/common/feature_support.h" | ||||
| #include "audio_core/renderer/audio_device.h" | ||||
| #include "audio_core/renderer/audio_renderer.h" | ||||
| #include "audio_core/renderer/voice/voice_info.h" | ||||
| #include "common/alignment.h" | ||||
| #include "common/bit_util.h" | ||||
| #include "common/common_funcs.h" | ||||
| @@ -13,91 +18,112 @@ | ||||
| #include "core/core.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/kernel/k_event.h" | ||||
| #include "core/hle/kernel/k_process.h" | ||||
| #include "core/hle/kernel/k_transfer_memory.h" | ||||
| #include "core/hle/service/audio/audren_u.h" | ||||
| #include "core/hle/service/audio/errors.h" | ||||
| #include "core/memory.h" | ||||
|  | ||||
| using namespace AudioCore::AudioRenderer; | ||||
|  | ||||
| namespace Service::Audio { | ||||
|  | ||||
| class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { | ||||
| public: | ||||
|     explicit IAudioRenderer(Core::System& system_, | ||||
|                             const AudioCommon::AudioRendererParameter& audren_params, | ||||
|                             const std::size_t instance_number) | ||||
|     explicit IAudioRenderer(Core::System& system_, Manager& manager_, | ||||
|                             AudioCore::AudioRendererParameterInternal& params, | ||||
|                             Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, | ||||
|                             u32 process_handle, u64 applet_resource_user_id, s32 session_id) | ||||
|         : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew}, | ||||
|           service_context{system_, "IAudioRenderer"} { | ||||
|           service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent( | ||||
|                                                           "IAudioRendererEvent")}, | ||||
|           manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} { | ||||
|         // clang-format off | ||||
|         static const FunctionInfo functions[] = { | ||||
|             {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, | ||||
|             {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"}, | ||||
|             {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"}, | ||||
|             {3, &IAudioRenderer::GetState, "GetState"}, | ||||
|             {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"}, | ||||
|             {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"}, | ||||
|             {5, &IAudioRenderer::Start, "Start"}, | ||||
|             {6, &IAudioRenderer::Stop, "Stop"}, | ||||
|             {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, | ||||
|             {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"}, | ||||
|             {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, | ||||
|             {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"}, | ||||
|             {11, &IAudioRenderer::ExecuteAudioRendererRendering, "ExecuteAudioRendererRendering"}, | ||||
|             {10, nullptr, "RequestUpdateAuto"}, | ||||
|             {11, nullptr, "ExecuteAudioRendererRendering"}, | ||||
|         }; | ||||
|         // clang-format on | ||||
|         RegisterHandlers(functions); | ||||
|  | ||||
|         system_event = service_context.CreateEvent("IAudioRenderer:SystemEvent"); | ||||
|         renderer = std::make_unique<AudioCore::AudioRenderer>( | ||||
|             system.CoreTiming(), system.Memory(), audren_params, | ||||
|             [this]() { | ||||
|                 const auto guard = LockService(); | ||||
|                 system_event->GetWritableEvent().Signal(); | ||||
|             }, | ||||
|             instance_number); | ||||
|         impl->Initialize(params, transfer_memory, transfer_memory_size, process_handle, | ||||
|                          applet_resource_user_id, session_id); | ||||
|     } | ||||
|  | ||||
|     ~IAudioRenderer() override { | ||||
|         service_context.CloseEvent(system_event); | ||||
|         impl->Finalize(); | ||||
|         service_context.CloseEvent(rendered_event); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     void GetSampleRate(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|         const auto sample_rate{impl->GetSystem().GetSampleRate()}; | ||||
|  | ||||
|         LOG_DEBUG(Service_Audio, "called. Sample rate {}", sample_rate); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push<u32>(renderer->GetSampleRate()); | ||||
|         rb.Push(sample_rate); | ||||
|     } | ||||
|  | ||||
|     void GetSampleCount(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|         const auto sample_count{impl->GetSystem().GetSampleCount()}; | ||||
|  | ||||
|         LOG_DEBUG(Service_Audio, "called. Sample count {}", sample_count); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push<u32>(renderer->GetSampleCount()); | ||||
|         rb.Push(sample_count); | ||||
|     } | ||||
|  | ||||
|     void GetState(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|         const u32 state{!impl->GetSystem().IsActive()}; | ||||
|  | ||||
|         LOG_DEBUG(Service_Audio, "called, state {}", state); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push<u32>(static_cast<u32>(renderer->GetStreamState())); | ||||
|         rb.Push(state); | ||||
|     } | ||||
|  | ||||
|     void GetMixBufferCount(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|         const auto buffer_count{impl->GetSystem().GetMixBufferCount()}; | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push<u32>(renderer->GetMixBufferCount()); | ||||
|         rb.Push(buffer_count); | ||||
|     } | ||||
|  | ||||
|     void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "(STUBBED) called"); | ||||
|     void RequestUpdate(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_TRACE(Service_Audio, "called"); | ||||
|  | ||||
|         std::vector<u8> output_params(ctx.GetWriteBufferSize(), 0); | ||||
|         auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params); | ||||
|         std::vector<u8> input{ctx.ReadBuffer(0)}; | ||||
|  | ||||
|         // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for | ||||
|         // checking size 0. Performance size is 0 for most games. | ||||
|         const auto buffers{ctx.BufferDescriptorB()}; | ||||
|         std::vector<u8> output(buffers[0].Size(), 0); | ||||
|         std::vector<u8> performance(buffers[1].Size(), 0); | ||||
|  | ||||
|         auto result = impl->RequestUpdate(input, performance, output); | ||||
|  | ||||
|         if (result.IsSuccess()) { | ||||
|             ctx.WriteBuffer(output_params); | ||||
|             ctx.WriteBufferB(output.data(), output.size(), 0); | ||||
|             ctx.WriteBufferB(performance.data(), performance.size(), 1); | ||||
|         } else { | ||||
|             LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); | ||||
|         } | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
| @@ -105,38 +131,45 @@ private: | ||||
|     } | ||||
|  | ||||
|     void Start(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_WARNING(Service_Audio, "(STUBBED) called"); | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|         const auto result = renderer->Start(); | ||||
|         impl->Start(); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(result); | ||||
|         rb.Push(ResultSuccess); | ||||
|     } | ||||
|  | ||||
|     void Stop(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_WARNING(Service_Audio, "(STUBBED) called"); | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|         const auto result = renderer->Stop(); | ||||
|         impl->Stop(); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(result); | ||||
|         rb.Push(ResultSuccess); | ||||
|     } | ||||
|  | ||||
|     void QuerySystemEvent(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_WARNING(Service_Audio, "(STUBBED) called"); | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|         if (impl->GetSystem().GetExecutionMode() == AudioCore::ExecutionMode::Manual) { | ||||
|             IPC::ResponseBuilder rb{ctx, 2}; | ||||
|             rb.Push(ERR_NOT_SUPPORTED); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2, 1}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.PushCopyObjects(system_event->GetReadableEvent()); | ||||
|         rb.PushCopyObjects(rendered_event->GetReadableEvent()); | ||||
|     } | ||||
|  | ||||
|     void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { | ||||
|         IPC::RequestParser rp{ctx}; | ||||
|         rendering_time_limit_percent = rp.Pop<u32>(); | ||||
|         LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}", | ||||
|                   rendering_time_limit_percent); | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|         ASSERT(rendering_time_limit_percent <= 100); | ||||
|         IPC::RequestParser rp{ctx}; | ||||
|         auto limit = rp.PopRaw<u32>(); | ||||
|  | ||||
|         auto& system_ = impl->GetSystem(); | ||||
|         system_.SetRenderingTimeLimit(limit); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(ResultSuccess); | ||||
| @@ -145,34 +178,34 @@ private: | ||||
|     void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|         auto& system_ = impl->GetSystem(); | ||||
|         auto time = system_.GetRenderingTimeLimit(); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(rendering_time_limit_percent); | ||||
|         rb.Push(time); | ||||
|     } | ||||
|  | ||||
|     void ExecuteAudioRendererRendering(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|         // This service command currently only reports an unsupported operation | ||||
|         // error code, or aborts. Given that, we just always return an error | ||||
|         // code in this case. | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(ERR_NOT_SUPPORTED); | ||||
|     } | ||||
|  | ||||
|     KernelHelpers::ServiceContext service_context; | ||||
|  | ||||
|     Kernel::KEvent* system_event; | ||||
|     std::unique_ptr<AudioCore::AudioRenderer> renderer; | ||||
|     u32 rendering_time_limit_percent = 100; | ||||
|     Kernel::KEvent* rendered_event; | ||||
|     Manager& manager; | ||||
|     std::unique_ptr<Renderer> impl; | ||||
| }; | ||||
|  | ||||
| class IAudioDevice final : public ServiceFramework<IAudioDevice> { | ||||
|  | ||||
| public: | ||||
|     explicit IAudioDevice(Core::System& system_, Kernel::KEvent* buffer_event_, u32_le revision_) | ||||
|         : ServiceFramework{system_, "IAudioDevice"}, buffer_event{buffer_event_}, revision{ | ||||
|                                                                                       revision_} { | ||||
|     explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision, | ||||
|                           u32 device_num) | ||||
|         : ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew}, | ||||
|           service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>( | ||||
|                                                         system_, applet_resource_user_id, | ||||
|                                                         revision)}, | ||||
|           event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} { | ||||
|         static const FunctionInfo functions[] = { | ||||
|             {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, | ||||
|             {1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"}, | ||||
| @@ -186,54 +219,45 @@ public: | ||||
|             {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"}, | ||||
|             {11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"}, | ||||
|             {12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"}, | ||||
|             {13, nullptr, "GetActiveAudioOutputDeviceName"}, | ||||
|             {14, nullptr, "ListAudioOutputDeviceName"}, | ||||
|             {13, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioOutputDeviceName"}, | ||||
|             {14, &IAudioDevice::ListAudioOutputDeviceName, "ListAudioOutputDeviceName"}, | ||||
|         }; | ||||
|         RegisterHandlers(functions); | ||||
|  | ||||
|         event->GetWritableEvent().Signal(); | ||||
|     } | ||||
|  | ||||
|     ~IAudioDevice() override { | ||||
|         service_context.CloseEvent(event); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     using AudioDeviceName = std::array<char, 256>; | ||||
|     static constexpr std::array<std::string_view, 4> audio_device_names{{ | ||||
|         "AudioStereoJackOutput", | ||||
|         "AudioBuiltInSpeakerOutput", | ||||
|         "AudioTvOutput", | ||||
|         "AudioUsbDeviceOutput", | ||||
|     }}; | ||||
|     enum class DeviceType { | ||||
|         AHUBHeadphones, | ||||
|         AHUBSpeakers, | ||||
|         HDA, | ||||
|         USBOutput, | ||||
|     }; | ||||
|  | ||||
|     void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|         const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName); | ||||
|  | ||||
|         const bool usb_output_supported = | ||||
|             IsFeatureSupported(AudioFeatures::AudioUSBDeviceOutput, revision); | ||||
|         const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioDeviceName); | ||||
|         std::vector<AudioDevice::AudioDeviceName> out_names{}; | ||||
|  | ||||
|         std::vector<AudioDeviceName> name_buffer; | ||||
|         name_buffer.reserve(audio_device_names.size()); | ||||
|         u32 out_count = impl->ListAudioDeviceName(out_names, in_count); | ||||
|  | ||||
|         for (std::size_t i = 0; i < count && i < audio_device_names.size(); i++) { | ||||
|             const auto type = static_cast<DeviceType>(i); | ||||
|  | ||||
|             if (!usb_output_supported && type == DeviceType::USBOutput) { | ||||
|                 continue; | ||||
|         std::string out{}; | ||||
|         for (u32 i = 0; i < out_count; i++) { | ||||
|             std::string a{}; | ||||
|             u32 j = 0; | ||||
|             while (out_names[i].name[j] != '\0') { | ||||
|                 a += out_names[i].name[j]; | ||||
|                 j++; | ||||
|             } | ||||
|  | ||||
|             const auto& device_name = audio_device_names[i]; | ||||
|             auto& entry = name_buffer.emplace_back(); | ||||
|             device_name.copy(entry.data(), device_name.size()); | ||||
|             out += "\n\t" + a; | ||||
|         } | ||||
|  | ||||
|         ctx.WriteBuffer(name_buffer); | ||||
|         LOG_DEBUG(Service_Audio, "called.\nNames={}", out); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|  | ||||
|         ctx.WriteBuffer(out_names); | ||||
|  | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(static_cast<u32>(name_buffer.size())); | ||||
|         rb.Push(out_count); | ||||
|     } | ||||
|  | ||||
|     void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { | ||||
| @@ -243,7 +267,11 @@ private: | ||||
|         const auto device_name_buffer = ctx.ReadBuffer(); | ||||
|         const std::string name = Common::StringFromBuffer(device_name_buffer); | ||||
|  | ||||
|         LOG_WARNING(Service_Audio, "(STUBBED) called. name={}, volume={}", name, volume); | ||||
|         LOG_DEBUG(Service_Audio, "called. name={}, volume={}", name, volume); | ||||
|  | ||||
|         if (name == "AudioTvOutput") { | ||||
|             impl->SetDeviceVolumes(volume); | ||||
|         } | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(ResultSuccess); | ||||
| @@ -253,53 +281,60 @@ private: | ||||
|         const auto device_name_buffer = ctx.ReadBuffer(); | ||||
|         const std::string name = Common::StringFromBuffer(device_name_buffer); | ||||
|  | ||||
|         LOG_WARNING(Service_Audio, "(STUBBED) called. name={}", name); | ||||
|         LOG_DEBUG(Service_Audio, "called. Name={}", name); | ||||
|  | ||||
|         f32 volume{1.0f}; | ||||
|         if (name == "AudioTvOutput") { | ||||
|             volume = impl->GetDeviceVolume(name); | ||||
|         } | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(1.0f); | ||||
|         rb.Push(volume); | ||||
|     } | ||||
|  | ||||
|     void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_WARNING(Service_Audio, "(STUBBED) called"); | ||||
|         const auto write_size = ctx.GetWriteBufferSize() / sizeof(char); | ||||
|         std::string out_name{"AudioTvOutput"}; | ||||
|  | ||||
|         // Currently set to always be TV audio output. | ||||
|         const auto& device_name = audio_device_names[2]; | ||||
|         LOG_DEBUG(Service_Audio, "(STUBBED) called. Name={}", out_name); | ||||
|  | ||||
|         AudioDeviceName out_device_name{}; | ||||
|         device_name.copy(out_device_name.data(), device_name.size()); | ||||
|         out_name.resize(write_size); | ||||
|  | ||||
|         ctx.WriteBuffer(out_device_name); | ||||
|         ctx.WriteBuffer(out_name); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(ResultSuccess); | ||||
|     } | ||||
|  | ||||
|     void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_WARNING(Service_Audio, "(STUBBED) called"); | ||||
|         LOG_DEBUG(Service_Audio, "(STUBBED) called"); | ||||
|  | ||||
|         buffer_event->GetWritableEvent().Signal(); | ||||
|         event->GetWritableEvent().Signal(); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2, 1}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.PushCopyObjects(buffer_event->GetReadableEvent()); | ||||
|         rb.PushCopyObjects(event->GetReadableEvent()); | ||||
|     } | ||||
|  | ||||
|     void GetActiveChannelCount(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_WARNING(Service_Audio, "(STUBBED) called"); | ||||
|         const auto& sink{system.AudioCore().GetOutputSink()}; | ||||
|         u32 channel_count{sink.GetDeviceChannels()}; | ||||
|  | ||||
|         LOG_DEBUG(Service_Audio, "(STUBBED) called. Channels={}", channel_count); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|  | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push<u32>(2); | ||||
|         rb.Push<u32>(channel_count); | ||||
|     } | ||||
|  | ||||
|     // Should be similar to QueryAudioDeviceOutputEvent | ||||
|     void QueryAudioDeviceInputEvent(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_WARNING(Service_Audio, "(STUBBED) called"); | ||||
|         LOG_DEBUG(Service_Audio, "(STUBBED) called"); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2, 1}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.PushCopyObjects(buffer_event->GetReadableEvent()); | ||||
|         rb.PushCopyObjects(event->GetReadableEvent()); | ||||
|     } | ||||
|  | ||||
|     void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) { | ||||
| @@ -307,402 +342,167 @@ private: | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2, 1}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.PushCopyObjects(buffer_event->GetReadableEvent()); | ||||
|         rb.PushCopyObjects(event->GetReadableEvent()); | ||||
|     } | ||||
|  | ||||
|     Kernel::KEvent* buffer_event; | ||||
|     u32_le revision = 0; | ||||
|     void ListAudioOutputDeviceName(Kernel::HLERequestContext& ctx) { | ||||
|         const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName); | ||||
|  | ||||
|         std::vector<AudioDevice::AudioDeviceName> out_names{}; | ||||
|  | ||||
|         u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count); | ||||
|  | ||||
|         std::string out{}; | ||||
|         for (u32 i = 0; i < out_count; i++) { | ||||
|             std::string a{}; | ||||
|             u32 j = 0; | ||||
|             while (out_names[i].name[j] != '\0') { | ||||
|                 a += out_names[i].name[j]; | ||||
|                 j++; | ||||
|             } | ||||
|             out += "\n\t" + a; | ||||
|         } | ||||
|  | ||||
|         LOG_DEBUG(Service_Audio, "called.\nNames={}", out); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|  | ||||
|         ctx.WriteBuffer(out_names); | ||||
|  | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(out_count); | ||||
|     } | ||||
|  | ||||
|     KernelHelpers::ServiceContext service_context; | ||||
|     std::unique_ptr<AudioDevice> impl; | ||||
|     Kernel::KEvent* event; | ||||
| }; | ||||
|  | ||||
| AudRenU::AudRenU(Core::System& system_) | ||||
|     : ServiceFramework{system_, "audren:u"}, service_context{system_, "audren:u"} { | ||||
|     : ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew}, | ||||
|       service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} { | ||||
|     // clang-format off | ||||
|     static const FunctionInfo functions[] = { | ||||
|         {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"}, | ||||
|         {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetWorkBufferSize"}, | ||||
|         {1, &AudRenU::GetWorkBufferSize, "GetWorkBufferSize"}, | ||||
|         {2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"}, | ||||
|         {3, &AudRenU::OpenAudioRendererForManualExecution, "OpenAudioRendererForManualExecution"}, | ||||
|         {3, nullptr, "OpenAudioRendererForManualExecution"}, | ||||
|         {4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"}, | ||||
|     }; | ||||
|     // clang-format on | ||||
|  | ||||
|     RegisterHandlers(functions); | ||||
|  | ||||
|     buffer_event = service_context.CreateEvent("IAudioOutBufferReleasedEvent"); | ||||
| } | ||||
|  | ||||
| AudRenU::~AudRenU() { | ||||
|     service_context.CloseEvent(buffer_event); | ||||
| } | ||||
| AudRenU::~AudRenU() = default; | ||||
|  | ||||
| void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_DEBUG(Service_Audio, "called"); | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|  | ||||
|     OpenAudioRendererImpl(ctx); | ||||
|     AudioCore::AudioRendererParameterInternal params; | ||||
|     rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params); | ||||
|     auto transfer_memory_handle = ctx.GetCopyHandle(0); | ||||
|     auto process_handle = ctx.GetCopyHandle(1); | ||||
|     auto transfer_memory_size = rp.Pop<u64>(); | ||||
|     auto applet_resource_user_id = rp.Pop<u64>(); | ||||
|  | ||||
|     if (impl->GetSessionCount() + 1 > AudioCore::MaxRendererSessions) { | ||||
|         LOG_ERROR(Service_Audio, "Too many AudioRenderer sessions open!"); | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(ERR_MAXIMUM_SESSIONS_REACHED); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const auto& handle_table{system.CurrentProcess()->GetHandleTable()}; | ||||
|     auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)}; | ||||
|     auto transfer_memory{ | ||||
|         process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)}; | ||||
|  | ||||
|     const auto session_id{impl->GetSessionId()}; | ||||
|     if (session_id == -1) { | ||||
|         LOG_ERROR(Service_Audio, "Tried to open a session that's already in use!"); | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(ERR_MAXIMUM_SESSIONS_REACHED); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     LOG_DEBUG(Service_Audio, "Opened new AudioRenderer session {} sessions open {}", session_id, | ||||
|               impl->GetSessionCount()); | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.PushIpcInterface<IAudioRenderer>(system, *impl, params, transfer_memory.GetPointerUnsafe(), | ||||
|                                         transfer_memory_size, process_handle, | ||||
|                                         applet_resource_user_id, session_id); | ||||
| } | ||||
|  | ||||
| static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) { | ||||
|     // +1 represents the final mix. | ||||
|     return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count + | ||||
|            1; | ||||
| } | ||||
|  | ||||
| void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|     // Several calculations below align the sizes being calculated | ||||
|     // onto a 64 byte boundary. | ||||
|     static constexpr u64 buffer_alignment_size = 64; | ||||
|  | ||||
|     // Some calculations that calculate portions of the buffer | ||||
|     // that will contain information, on the other hand, align | ||||
|     // the result of some of their calcularions on a 16 byte boundary. | ||||
|     static constexpr u64 info_field_alignment_size = 16; | ||||
|  | ||||
|     // Maximum detail entries that may exist at one time for performance | ||||
|     // frame statistics. | ||||
|     static constexpr u64 max_perf_detail_entries = 100; | ||||
|  | ||||
|     // Size of the data structure representing the bulk of the voice-related state. | ||||
|     static constexpr u64 voice_state_size_bytes = 0x100; | ||||
|  | ||||
|     // Size of the upsampler manager data structure | ||||
|     constexpr u64 upsampler_manager_size = 0x48; | ||||
|  | ||||
|     // Calculates the part of the size that relates to mix buffers. | ||||
|     const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) { | ||||
|         // As of 8.0.0 this is the maximum on voice channels. | ||||
|         constexpr u64 max_voice_channels = 6; | ||||
|  | ||||
|         // The service expects the sample_count member of the parameters to either be | ||||
|         // a value of 160 or 240, so the maximum sample count is assumed in order | ||||
|         // to adequately handle all values at runtime. | ||||
|         constexpr u64 default_max_sample_count = 240; | ||||
|  | ||||
|         const u64 total_mix_buffers = params.mix_buffer_count + max_voice_channels; | ||||
|  | ||||
|         u64 size = 0; | ||||
|         size += total_mix_buffers * (sizeof(s32) * params.sample_count); | ||||
|         size += total_mix_buffers * (sizeof(s32) * default_max_sample_count); | ||||
|         size += u64{params.submix_count} + params.sink_count; | ||||
|         size = Common::AlignUp(size, buffer_alignment_size); | ||||
|         size += Common::AlignUp(params.unknown_30, buffer_alignment_size); | ||||
|         size += Common::AlignUp(sizeof(s32) * params.mix_buffer_count, buffer_alignment_size); | ||||
|         return size; | ||||
|     }; | ||||
|  | ||||
|     // Calculates the portion of the size related to the mix data (and the sorting thereof). | ||||
|     const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) { | ||||
|         // The size of the mixing info data structure. | ||||
|         constexpr u64 mix_info_size = 0x940; | ||||
|  | ||||
|         // Consists of total submixes with the final mix included. | ||||
|         const u64 total_mix_count = u64{params.submix_count} + 1; | ||||
|  | ||||
|         // The total number of effects that may be available to the audio renderer at any time. | ||||
|         constexpr u64 max_effects = 256; | ||||
|  | ||||
|         // Calculates the part of the size related to the audio node state. | ||||
|         // This will only be used if the audio revision supports the splitter. | ||||
|         const auto calculate_node_state_size = [](std::size_t num_nodes) { | ||||
|             // Internally within a nodestate, it appears to use a data structure | ||||
|             // similar to a std::bitset<64> twice. | ||||
|             constexpr u64 bit_size = Common::BitSize<u64>(); | ||||
|             constexpr u64 num_bitsets = 2; | ||||
|  | ||||
|             // Node state instances have three states internally for performing | ||||
|             // depth-first searches of nodes. Initialized, Found, and Done Sorting. | ||||
|             constexpr u64 num_states = 3; | ||||
|  | ||||
|             u64 size = 0; | ||||
|             size += (num_nodes * num_nodes) * sizeof(s32); | ||||
|             size += num_states * (num_nodes * sizeof(s32)); | ||||
|             size += num_bitsets * (Common::AlignUp(num_nodes, bit_size) / Common::BitSize<u8>()); | ||||
|             return size; | ||||
|         }; | ||||
|  | ||||
|         // Calculates the part of the size related to the adjacency (aka edge) matrix. | ||||
|         const auto calculate_edge_matrix_size = [](std::size_t num_nodes) { | ||||
|             return (num_nodes * num_nodes) * sizeof(s32); | ||||
|         }; | ||||
|  | ||||
|         u64 size = 0; | ||||
|         size += Common::AlignUp(sizeof(void*) * total_mix_count, info_field_alignment_size); | ||||
|         size += Common::AlignUp(mix_info_size * total_mix_count, info_field_alignment_size); | ||||
|         size += Common::AlignUp(sizeof(s32) * max_effects * params.submix_count, | ||||
|                                 info_field_alignment_size); | ||||
|  | ||||
|         if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { | ||||
|             size += Common::AlignUp(calculate_node_state_size(total_mix_count) + | ||||
|                                         calculate_edge_matrix_size(total_mix_count), | ||||
|                                     info_field_alignment_size); | ||||
|         } | ||||
|  | ||||
|         return size; | ||||
|     }; | ||||
|  | ||||
|     // Calculates the part of the size related to voice channel info. | ||||
|     const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) { | ||||
|         constexpr u64 voice_info_size = 0x220; | ||||
|         constexpr u64 voice_resource_size = 0xD0; | ||||
|  | ||||
|         u64 size = 0; | ||||
|         size += Common::AlignUp(sizeof(void*) * params.voice_count, info_field_alignment_size); | ||||
|         size += Common::AlignUp(voice_info_size * params.voice_count, info_field_alignment_size); | ||||
|         size += | ||||
|             Common::AlignUp(voice_resource_size * params.voice_count, info_field_alignment_size); | ||||
|         size += | ||||
|             Common::AlignUp(voice_state_size_bytes * params.voice_count, info_field_alignment_size); | ||||
|         return size; | ||||
|     }; | ||||
|  | ||||
|     // Calculates the part of the size related to memory pools. | ||||
|     const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) { | ||||
|         const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count); | ||||
|         const u64 memory_pool_info_size = 0x20; | ||||
|         return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size); | ||||
|     }; | ||||
|  | ||||
|     // Calculates the part of the size related to the splitter context. | ||||
|     const auto calculate_splitter_context_size = | ||||
|         [](const AudioCommon::AudioRendererParameter& params) -> u64 { | ||||
|         if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         constexpr u64 splitter_info_size = 0x20; | ||||
|         constexpr u64 splitter_destination_data_size = 0xE0; | ||||
|  | ||||
|         u64 size = 0; | ||||
|         size += params.num_splitter_send_channels; | ||||
|         size += | ||||
|             Common::AlignUp(splitter_info_size * params.splitter_count, info_field_alignment_size); | ||||
|         size += Common::AlignUp(splitter_destination_data_size * params.num_splitter_send_channels, | ||||
|                                 info_field_alignment_size); | ||||
|  | ||||
|         return size; | ||||
|     }; | ||||
|  | ||||
|     // Calculates the part of the size related to the upsampler info. | ||||
|     const auto calculate_upsampler_info_size = | ||||
|         [](const AudioCommon::AudioRendererParameter& params) { | ||||
|             constexpr u64 upsampler_info_size = 0x280; | ||||
|             // Yes, using the buffer size over info alignment size is intentional here. | ||||
|             return Common::AlignUp(upsampler_info_size * | ||||
|                                        (u64{params.submix_count} + params.sink_count), | ||||
|                                    buffer_alignment_size); | ||||
|         }; | ||||
|  | ||||
|     // Calculates the part of the size related to effect info. | ||||
|     const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) { | ||||
|         constexpr u64 effect_info_size = 0x2B0; | ||||
|         return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size); | ||||
|     }; | ||||
|  | ||||
|     // Calculates the part of the size related to audio sink info. | ||||
|     const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) { | ||||
|         const u64 sink_info_size = 0x170; | ||||
|         return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size); | ||||
|     }; | ||||
|  | ||||
|     // Calculates the part of the size related to voice state info. | ||||
|     const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) { | ||||
|         const u64 voice_state_size = 0x100; | ||||
|         const u64 additional_size = buffer_alignment_size - 1; | ||||
|         return Common::AlignUp(voice_state_size * params.voice_count + additional_size, | ||||
|                                info_field_alignment_size); | ||||
|     }; | ||||
|  | ||||
|     // Calculates the part of the size related to performance statistics. | ||||
|     const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) { | ||||
|         // Extra size value appended to the end of the calculation. | ||||
|         constexpr u64 appended = 128; | ||||
|  | ||||
|         // Whether or not we assume the newer version of performance metrics data structures. | ||||
|         const bool is_v2 = | ||||
|             IsFeatureSupported(AudioFeatures::PerformanceMetricsVersion2, params.revision); | ||||
|  | ||||
|         // Data structure sizes | ||||
|         constexpr u64 perf_statistics_size = 0x0C; | ||||
|         const u64 header_size = is_v2 ? 0x30 : 0x18; | ||||
|         const u64 entry_size = is_v2 ? 0x18 : 0x10; | ||||
|         const u64 detail_size = is_v2 ? 0x18 : 0x10; | ||||
|  | ||||
|         const u64 entry_count = CalculateNumPerformanceEntries(params); | ||||
|         const u64 size_per_frame = | ||||
|             header_size + (entry_size * entry_count) + (detail_size * max_perf_detail_entries); | ||||
|  | ||||
|         u64 size = 0; | ||||
|         size += Common::AlignUp(size_per_frame * params.performance_frame_count + 1, | ||||
|                                 buffer_alignment_size); | ||||
|         size += Common::AlignUp(perf_statistics_size, buffer_alignment_size); | ||||
|         size += appended; | ||||
|         return size; | ||||
|     }; | ||||
|  | ||||
|     // Calculates the part of the size that relates to the audio command buffer. | ||||
|     const auto calculate_command_buffer_size = | ||||
|         [](const AudioCommon::AudioRendererParameter& params) { | ||||
|             constexpr u64 alignment = (buffer_alignment_size - 1) * 2; | ||||
|  | ||||
|             if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { | ||||
|                 constexpr u64 command_buffer_size = 0x18000; | ||||
|  | ||||
|                 return command_buffer_size + alignment; | ||||
|             } | ||||
|  | ||||
|             // When the variadic command buffer is supported, this means | ||||
|             // the command generator for the audio renderer can issue commands | ||||
|             // that are (as one would expect), variable in size. So what we need to do | ||||
|             // is determine the maximum possible size for a few command data structures | ||||
|             // then multiply them by the amount of present commands indicated by the given | ||||
|             // respective audio parameters. | ||||
|  | ||||
|             constexpr u64 max_biquad_filters = 2; | ||||
|             constexpr u64 max_mix_buffers = 24; | ||||
|  | ||||
|             constexpr u64 biquad_filter_command_size = 0x2C; | ||||
|  | ||||
|             constexpr u64 depop_mix_command_size = 0x24; | ||||
|             constexpr u64 depop_setup_command_size = 0x50; | ||||
|  | ||||
|             constexpr u64 effect_command_max_size = 0x540; | ||||
|  | ||||
|             constexpr u64 mix_command_size = 0x1C; | ||||
|             constexpr u64 mix_ramp_command_size = 0x24; | ||||
|             constexpr u64 mix_ramp_grouped_command_size = 0x13C; | ||||
|  | ||||
|             constexpr u64 perf_command_size = 0x28; | ||||
|  | ||||
|             constexpr u64 sink_command_size = 0x130; | ||||
|  | ||||
|             constexpr u64 submix_command_max_size = | ||||
|                 depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; | ||||
|  | ||||
|             constexpr u64 volume_command_size = 0x1C; | ||||
|             constexpr u64 volume_ramp_command_size = 0x20; | ||||
|  | ||||
|             constexpr u64 voice_biquad_filter_command_size = | ||||
|                 biquad_filter_command_size * max_biquad_filters; | ||||
|             constexpr u64 voice_data_command_size = 0x9C; | ||||
|             const u64 voice_command_max_size = | ||||
|                 (params.splitter_count * depop_setup_command_size) + | ||||
|                 (voice_data_command_size + voice_biquad_filter_command_size + | ||||
|                  volume_ramp_command_size + mix_ramp_grouped_command_size); | ||||
|  | ||||
|             // Now calculate the individual elements that comprise the size and add them together. | ||||
|             const u64 effect_commands_size = params.effect_count * effect_command_max_size; | ||||
|  | ||||
|             const u64 final_mix_commands_size = | ||||
|                 depop_mix_command_size + volume_command_size * max_mix_buffers; | ||||
|  | ||||
|             const u64 perf_commands_size = | ||||
|                 perf_command_size * | ||||
|                 (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); | ||||
|  | ||||
|             const u64 sink_commands_size = params.sink_count * sink_command_size; | ||||
|  | ||||
|             const u64 splitter_commands_size = | ||||
|                 params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; | ||||
|  | ||||
|             const u64 submix_commands_size = params.submix_count * submix_command_max_size; | ||||
|  | ||||
|             const u64 voice_commands_size = params.voice_count * voice_command_max_size; | ||||
|  | ||||
|             return effect_commands_size + final_mix_commands_size + perf_commands_size + | ||||
|                    sink_commands_size + splitter_commands_size + submix_commands_size + | ||||
|                    voice_commands_size + alignment; | ||||
|         }; | ||||
| void AudRenU::GetWorkBufferSize(Kernel::HLERequestContext& ctx) { | ||||
|     AudioCore::AudioRendererParameterInternal params; | ||||
|  | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>(); | ||||
|     rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params); | ||||
|  | ||||
|     u64 size = 0; | ||||
|     size += calculate_mix_buffer_sizes(params); | ||||
|     size += calculate_mix_info_size(params); | ||||
|     size += calculate_voice_info_size(params); | ||||
|     size += upsampler_manager_size; | ||||
|     size += calculate_memory_pools_size(params); | ||||
|     size += calculate_splitter_context_size(params); | ||||
|     u64 size{0}; | ||||
|     auto result = impl->GetWorkBufferSize(params, size); | ||||
|  | ||||
|     size = Common::AlignUp(size, buffer_alignment_size); | ||||
|     std::string output_info{}; | ||||
|     output_info += fmt::format("\tRevision {}", AudioCore::GetRevisionNum(params.revision)); | ||||
|     output_info += | ||||
|         fmt::format("\n\tSample Rate {}, Sample Count {}", params.sample_rate, params.sample_count); | ||||
|     output_info += fmt::format("\n\tExecution Mode {}, Voice Drop Enabled {}", | ||||
|                                static_cast<u32>(params.execution_mode), params.voice_drop_enabled); | ||||
|     output_info += fmt::format( | ||||
|         "\n\tSizes: Effects {:04X}, Mixes {:04X}, Sinks {:04X}, Submixes {:04X}, Splitter Infos " | ||||
|         "{:04X}, Splitter Destinations {:04X}, Voices {:04X}, Performance Frames {:04X} External " | ||||
|         "Context {:04X}", | ||||
|         params.effects, params.mixes, params.sinks, params.sub_mixes, params.splitter_infos, | ||||
|         params.splitter_destinations, params.voices, params.perf_frames, | ||||
|         params.external_context_size); | ||||
|  | ||||
|     size += calculate_upsampler_info_size(params); | ||||
|     size += calculate_effect_info_size(params); | ||||
|     size += calculate_sink_info_size(params); | ||||
|     size += calculate_voice_state_size(params); | ||||
|     size += calculate_perf_size(params); | ||||
|     size += calculate_command_buffer_size(params); | ||||
|  | ||||
|     // finally, 4KB page align the size, and we're done. | ||||
|     size = Common::AlignUp(size, 4096); | ||||
|     LOG_DEBUG(Service_Audio, "called.\nInput params:\n{}\nOutput params:\n\tWorkbuffer size {:08X}", | ||||
|               output_info, size); | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 4}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(result); | ||||
|     rb.Push<u64>(size); | ||||
|  | ||||
|     LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", size); | ||||
| } | ||||
|  | ||||
| void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     const u64 aruid = rp.Pop<u64>(); | ||||
|  | ||||
|     LOG_DEBUG(Service_Audio, "called. aruid={:016X}", aruid); | ||||
|     const auto applet_resource_user_id = rp.Pop<u64>(); | ||||
|  | ||||
|     LOG_DEBUG(Service_Audio, "called. Applet resource id {}", applet_resource_user_id); | ||||
|  | ||||
|     // Revisionless variant of GetAudioDeviceServiceWithRevisionInfo that | ||||
|     // always assumes the initial release revision (REV1). | ||||
|     IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||||
|  | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.PushIpcInterface<IAudioDevice>(system, buffer_event, Common::MakeMagic('R', 'E', 'V', '1')); | ||||
|     rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id, | ||||
|                                       ::Common::MakeMagic('R', 'E', 'V', '1'), num_audio_devices++); | ||||
| } | ||||
|  | ||||
| void AudRenU::OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_DEBUG(Service_Audio, "called"); | ||||
|  | ||||
|     OpenAudioRendererImpl(ctx); | ||||
| } | ||||
|  | ||||
| void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) { | ||||
|     struct Parameters { | ||||
|         u32 revision; | ||||
|         u64 aruid; | ||||
|         u64 applet_resource_user_id; | ||||
|     }; | ||||
|  | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     const auto [revision, aruid] = rp.PopRaw<Parameters>(); | ||||
|  | ||||
|     LOG_DEBUG(Service_Audio, "called. revision={:08X}, aruid={:016X}", revision, aruid); | ||||
|     const auto [revision, applet_resource_user_id] = rp.PopRaw<Parameters>(); | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.PushIpcInterface<IAudioDevice>(system, buffer_event, revision); | ||||
| } | ||||
|     LOG_DEBUG(Service_Audio, "called. Revision {} Applet resource id {}", | ||||
|               AudioCore::GetRevisionNum(revision), applet_resource_user_id); | ||||
|  | ||||
| void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>(); | ||||
|     IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||||
|  | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.PushIpcInterface<IAudioRenderer>(system, params, audren_instance_count++); | ||||
| } | ||||
|  | ||||
| bool IsFeatureSupported(AudioFeatures feature, u32_le revision) { | ||||
|     // Byte swap | ||||
|     const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0'); | ||||
|  | ||||
|     switch (feature) { | ||||
|     case AudioFeatures::AudioUSBDeviceOutput: | ||||
|         return version_num >= 4U; | ||||
|     case AudioFeatures::Splitter: | ||||
|         return version_num >= 2U; | ||||
|     case AudioFeatures::PerformanceMetricsVersion2: | ||||
|     case AudioFeatures::VariadicCommandBuffer: | ||||
|         return version_num >= 5U; | ||||
|     default: | ||||
|         return false; | ||||
|     } | ||||
|     rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id, revision, | ||||
|                                       num_audio_devices++); | ||||
| } | ||||
|  | ||||
| } // namespace Service::Audio | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "audio_core/audio_render_manager.h" | ||||
| #include "core/hle/service/kernel_helpers.h" | ||||
| #include "core/hle/service/service.h" | ||||
|  | ||||
| @@ -15,6 +16,7 @@ class HLERequestContext; | ||||
| } | ||||
|  | ||||
| namespace Service::Audio { | ||||
| class IAudioRenderer; | ||||
|  | ||||
| class AudRenU final : public ServiceFramework<AudRenU> { | ||||
| public: | ||||
| @@ -23,28 +25,14 @@ public: | ||||
|  | ||||
| private: | ||||
|     void OpenAudioRenderer(Kernel::HLERequestContext& ctx); | ||||
|     void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx); | ||||
|     void GetWorkBufferSize(Kernel::HLERequestContext& ctx); | ||||
|     void GetAudioDeviceService(Kernel::HLERequestContext& ctx); | ||||
|     void OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx); | ||||
|     void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx); | ||||
|  | ||||
|     void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx); | ||||
|  | ||||
|     KernelHelpers::ServiceContext service_context; | ||||
|  | ||||
|     std::size_t audren_instance_count = 0; | ||||
|     Kernel::KEvent* buffer_event; | ||||
|     std::unique_ptr<AudioCore::AudioRenderer::Manager> impl; | ||||
|     u32 num_audio_devices{0}; | ||||
| }; | ||||
|  | ||||
| // Describes a particular audio feature that may be supported in a particular revision. | ||||
| enum class AudioFeatures : u32 { | ||||
|     AudioUSBDeviceOutput, | ||||
|     Splitter, | ||||
|     PerformanceMetricsVersion2, | ||||
|     VariadicCommandBuffer, | ||||
| }; | ||||
|  | ||||
| // Tests if a particular audio feature is supported with a given audio revision. | ||||
| bool IsFeatureSupported(AudioFeatures feature, u32_le revision); | ||||
|  | ||||
| } // namespace Service::Audio | ||||
|   | ||||
| @@ -7,8 +7,17 @@ | ||||
|  | ||||
| namespace Service::Audio { | ||||
|  | ||||
| constexpr Result ERR_INVALID_DEVICE_NAME{ErrorModule::Audio, 1}; | ||||
| constexpr Result ERR_OPERATION_FAILED{ErrorModule::Audio, 2}; | ||||
| constexpr Result ERR_INVALID_SAMPLE_RATE{ErrorModule::Audio, 3}; | ||||
| constexpr Result ERR_INSUFFICIENT_BUFFER_SIZE{ErrorModule::Audio, 4}; | ||||
| constexpr Result ERR_MAXIMUM_SESSIONS_REACHED{ErrorModule::Audio, 5}; | ||||
| constexpr Result ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8}; | ||||
| constexpr Result ERR_INVALID_CHANNEL_COUNT{ErrorModule::Audio, 10}; | ||||
| constexpr Result ERR_INVALID_UPDATE_DATA{ErrorModule::Audio, 41}; | ||||
| constexpr Result ERR_POOL_MAPPING_FAILED{ErrorModule::Audio, 42}; | ||||
| constexpr Result ERR_NOT_SUPPORTED{ErrorModule::Audio, 513}; | ||||
| constexpr Result ERR_INVALID_PROCESS_HANDLE{ErrorModule::Audio, 1536}; | ||||
| constexpr Result ERR_INVALID_REVISION{ErrorModule::Audio, 1537}; | ||||
|  | ||||
| } // namespace Service::Audio | ||||
|   | ||||
| @@ -298,7 +298,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx) { | ||||
|     const auto sample_rate = rp.Pop<u32>(); | ||||
|     const auto channel_count = rp.Pop<u32>(); | ||||
|  | ||||
|     LOG_CRITICAL(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); | ||||
|     LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); | ||||
|  | ||||
|     ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || | ||||
|                    sample_rate == 12000 || sample_rate == 8000, | ||||
|   | ||||
| @@ -511,7 +511,7 @@ struct Memory::Impl { | ||||
|  | ||||
|     [[nodiscard]] u8* GetPointerImpl(VAddr vaddr, auto on_unmapped, auto on_rasterizer) const { | ||||
|         // AARCH64 masks the upper 16 bit of all memory accesses | ||||
|         vaddr &= 0xffffffffffffLL; | ||||
|         vaddr &= 0xffffffffffffULL; | ||||
|  | ||||
|         if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { | ||||
|             on_unmapped(); | ||||
| @@ -776,6 +776,10 @@ void Memory::CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr s | ||||
|     impl->CopyBlock(process, dest_addr, src_addr, size); | ||||
| } | ||||
|  | ||||
| void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, const std::size_t size) { | ||||
|     impl->ZeroBlock(process, dest_addr, size); | ||||
| } | ||||
|  | ||||
| void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { | ||||
|     impl->RasterizerMarkRegionCached(vaddr, size, cached); | ||||
| } | ||||
|   | ||||
| @@ -436,6 +436,19 @@ public: | ||||
|     void CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr, | ||||
|                    std::size_t size); | ||||
|  | ||||
|     /** | ||||
|      * Zeros a range of bytes within the current process' address space at the specified | ||||
|      * virtual address. | ||||
|      * | ||||
|      * @param process   The process that will have data zeroed within its address space. | ||||
|      * @param dest_addr The destination virtual address to zero the data from. | ||||
|      * @param size      The size of the range to zero out, in bytes. | ||||
|      * | ||||
|      * @post The range [dest_addr, size) within the process' address space contains the | ||||
|      *       value 0. | ||||
|      */ | ||||
|     void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); | ||||
|  | ||||
|     /** | ||||
|      * Marks each page within the specified address range as cached or uncached. | ||||
|      * | ||||
|   | ||||
		Reference in New Issue
	
	Block a user