diff options
Diffstat (limited to 'src/core/hle')
| -rw-r--r-- | src/core/hle/kernel/hle_ipc.cpp | 38 | ||||
| -rw-r--r-- | src/core/hle/kernel/hle_ipc.h | 8 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 34 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.h | 3 | ||||
| -rw-r--r-- | src/core/hle/service/am/am.cpp | 10 | ||||
| -rw-r--r-- | src/core/hle/service/am/am.h | 1 | ||||
| -rw-r--r-- | src/core/hle/service/audio/audin_u.cpp | 384 | ||||
| -rw-r--r-- | src/core/hle/service/audio/audin_u.h | 47 | ||||
| -rw-r--r-- | src/core/hle/service/audio/audout_u.cpp | 327 | ||||
| -rw-r--r-- | src/core/hle/service/audio/audout_u.h | 21 | ||||
| -rw-r--r-- | src/core/hle/service/audio/audren_u.cpp | 692 | ||||
| -rw-r--r-- | src/core/hle/service/audio/audren_u.h | 22 | ||||
| -rw-r--r-- | src/core/hle/service/audio/errors.h | 9 | ||||
| -rw-r--r-- | src/core/hle/service/audio/hwopus.cpp | 2 | 
14 files changed, 838 insertions, 760 deletions
| diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 45135a07f..5b3feec66 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -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()}; diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index d3abeee85..99265ce90 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -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 diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 7307cf262..f23c629dc 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -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;  } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index aa0ebaa02..6c7cf6af2 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -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; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 9116dd77c..118f226e4 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -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>(); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 53144427b..bb75c6281 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -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); diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 18d3ae682..48a9a73a0 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -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(); + +        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>(); -AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} { +        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) { +    using namespace AudioCore::AudioRenderer; +      LOG_DEBUG(Service_Audio, "called"); -    constexpr u32 device_count = 0; -    // Since we don't actually use any other audio input devices, we return 0 devices. Filtered -    // device listing just omits the default input device +    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)); +    rb.Push(out_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; +void AudInU::OpenAudioIn(Kernel::HLERequestContext& 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}; -    rb.Push(ResultSuccess); -    rb.PushRaw<AudInOutParams>(params); -    rb.PushIpcInterface<IAudioIn>(system); -} -void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) { -    LOG_WARNING(Service_Audio, "(STUBBED) called"); -    OpenInOutImpl(ctx); +    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 diff --git a/src/core/hle/service/audio/audin_u.h b/src/core/hle/service/audio/audin_u.h index 2bfa38ecc..b45fda78a 100644 --- a/src/core/hle/service/audio/audin_u.h +++ b/src/core/hle/service/audio/audin_u.h @@ -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,22 +16,12 @@ namespace Kernel {  class HLERequestContext;  } -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; +namespace AudioCore::AudioOut { +class Manager; +class In; +} // namespace AudioCore::AudioOut -    Kernel::KEvent* buffer_event; -}; +namespace Service::Audio {  class AudInU final : public ServiceFramework<AudInU> {  public: @@ -37,33 +29,14 @@ public:      ~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 diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index b0dad6053..a44dd842a 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -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);      } -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"); +    [[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() { +        return impl; +    } +private:      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 RegisterBufferEvent(Kernel::HLERequestContext& ctx) { -        LOG_DEBUG(Service_Audio, "called"); - -        IPC::ResponseBuilder rb{ctx, 2, 1}; -        rb.Push(ResultSuccess); -        rb.PushCopyObjects(buffer_event->GetReadableEvent()); -    } - -    void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { -        LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description()); +    void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) {          IPC::RequestParser rp{ctx}; +        u64 tag = rp.PopRaw<u64>(); -        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>()}; +        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!"); +        } -        std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16)); -        main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size); +        const auto& in_buffer = ctx.ReadBuffer(); +        AudioOutBuffer buffer{}; +        std::memcpy(&buffer, in_buffer.data(), sizeof(AudioOutBuffer)); -        if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) { -            IPC::ResponseBuilder rb{ctx, 2}; -            rb.Push(ERR_BUFFER_COUNT_EXCEEDED); -            return; -        } +        [[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 GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { -        LOG_DEBUG(Service_Audio, "called {}", ctx.Description()); +    void GetReleasedAudioOutBuffers(Kernel::HLERequestContext& ctx) { +        auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64); +        std::vector<u64> released_buffers(write_buffer_size, 0); -        const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)}; -        const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)}; +        auto count = impl->GetReleasedBuffers(released_buffers); -        std::vector<u64> tags{released_buffers}; -        tags.resize(max_count); -        ctx.WriteBuffer(tags); +        [[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<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>(); + +        LOG_DEBUG(Service_Audio, "called. Volume={}", volume); -        stream->SetVolume(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"); - +void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    auto in_params{rp.PopRaw<AudioOutParameter>()}; +    auto applet_resource_user_id{rp.PopRaw<u64>()};      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); +    auto device_name = Common::StringFromBuffer(device_name_data); +    auto handle{ctx.GetCopyHandle(0)}; -    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; +    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;      } -    if (!params.sample_rate) { -        params.sample_rate = DefaultSampleRate; + +    size_t new_session_id{}; +    auto result{impl->AcquireSessionId(new_session_id)}; +    if (result.IsError()) { +        IPC::ResponseBuilder rb{ctx, 2}; +        rb.Push(result); +        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)); +    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 diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index d82004c2e..fdc0ee754 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h @@ -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 diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 2ce63c004..381a66ba5 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -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) { +        LOG_DEBUG(Service_Audio, "called"); +          IPC::RequestParser rp{ctx}; -        rendering_time_limit_percent = rp.Pop<u32>(); -        LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}", -                  rendering_time_limit_percent); +        auto limit = rp.PopRaw<u32>(); -        ASSERT(rendering_time_limit_percent <= 100); +        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();      } -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, -    }; +    ~IAudioDevice() override { +        service_context.CloseEvent(event); +    } +private:      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"); - -    OpenAudioRendererImpl(ctx); -} - -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); +    IPC::RequestParser rp{ctx}; -            // 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; +    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>(); -            const u64 final_mix_commands_size = -                depop_mix_command_size + volume_command_size * max_mix_buffers; +    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 u64 perf_commands_size = -                perf_command_size * -                (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); +    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 u64 sink_commands_size = params.sink_count * sink_command_size; +    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; +    } -            const u64 splitter_commands_size = -                params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; +    LOG_DEBUG(Service_Audio, "Opened new AudioRenderer session {} sessions open {}", session_id, +              impl->GetSessionCount()); -            const u64 submix_commands_size = params.submix_count * submix_command_max_size; +    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); +} -            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>(); - -    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); - -    size = Common::AlignUp(size, buffer_alignment_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); +    rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params); + +    u64 size{0}; +    auto result = impl->GetWorkBufferSize(params, 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); + +    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 diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index 869d39002..4384a9b3c 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -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 diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h index ac6c514af..d706978cb 100644 --- a/src/core/hle/service/audio/errors.h +++ b/src/core/hle/service/audio/errors.h @@ -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 diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 75da659e5..4f2ed2d52 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -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, | 
