diff options
| author | liamwhite <liamwhite@users.noreply.github.com> | 2023-09-17 10:42:44 -0400 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-09-17 10:42:44 -0400 | 
| commit | 474739a37920ff8e8a2f5d6f480a9116fdfba825 (patch) | |
| tree | 8331fac91e1e96ddd379917ad51167cef48868f3 /src/core | |
| parent | 4d28e60694d5f065e81e50657a7d8472f6f3da20 (diff) | |
| parent | 67e2d5c28b8423c4f3f1d5b00f87325684158a6f (diff) | |
Merge pull request #11460 from Kelebek1/hw_opus
Reimplement HardwareOpus
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/hle/result.h | 2 | ||||
| -rw-r--r-- | src/core/hle/service/audio/errors.h | 12 | ||||
| -rw-r--r-- | src/core/hle/service/audio/hwopus.cpp | 722 | ||||
| -rw-r--r-- | src/core/hle/service/audio/hwopus.h | 25 | 
5 files changed, 428 insertions, 335 deletions
| diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 30d2f7df6..b2dc71d4c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -890,7 +890,7 @@ endif()  create_target_directory_groups(core)  target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb) -target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus renderdoc) +target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls renderdoc)  if (MINGW)      target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})  endif() diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 92a1439eb..dd0b27f47 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -62,7 +62,7 @@ enum class ErrorModule : u32 {      XCD = 108,      TMP451 = 109,      NIFM = 110, -    Hwopus = 111, +    HwOpus = 111,      LSM6DS3 = 112,      Bluetooth = 113,      VI = 114, diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h index 3d3d3d97a..c41345f7e 100644 --- a/src/core/hle/service/audio/errors.h +++ b/src/core/hle/service/audio/errors.h @@ -20,4 +20,16 @@ constexpr Result ResultNotSupported{ErrorModule::Audio, 513};  constexpr Result ResultInvalidHandle{ErrorModule::Audio, 1536};  constexpr Result ResultInvalidRevision{ErrorModule::Audio, 1537}; +constexpr Result ResultLibOpusAllocFail{ErrorModule::HwOpus, 7}; +constexpr Result ResultInputDataTooSmall{ErrorModule::HwOpus, 8}; +constexpr Result ResultLibOpusInvalidState{ErrorModule::HwOpus, 6}; +constexpr Result ResultLibOpusUnimplemented{ErrorModule::HwOpus, 5}; +constexpr Result ResultLibOpusInvalidPacket{ErrorModule::HwOpus, 17}; +constexpr Result ResultLibOpusInternalError{ErrorModule::HwOpus, 4}; +constexpr Result ResultBufferTooSmall{ErrorModule::HwOpus, 3}; +constexpr Result ResultLibOpusBadArg{ErrorModule::HwOpus, 2}; +constexpr Result ResultInvalidOpusDSPReturnCode{ErrorModule::HwOpus, 259}; +constexpr Result ResultInvalidOpusSampleRate{ErrorModule::HwOpus, 1001}; +constexpr Result ResultInvalidOpusChannelCount{ErrorModule::HwOpus, 1002}; +  } // namespace Service::Audio diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 1557e6088..6a7bf9416 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -1,420 +1,506 @@  // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project  // SPDX-License-Identifier: GPL-2.0-or-later -#include <chrono> -#include <cstring>  #include <memory>  #include <vector> -#include <opus.h> -#include <opus_multistream.h> - +#include "audio_core/opus/decoder.h" +#include "audio_core/opus/parameters.h"  #include "common/assert.h"  #include "common/logging/log.h"  #include "common/scratch_buffer.h" +#include "core/core.h"  #include "core/hle/service/audio/hwopus.h"  #include "core/hle/service/ipc_helpers.h"  namespace Service::Audio { -namespace { -struct OpusDeleter { -    void operator()(OpusMSDecoder* ptr) const { -        opus_multistream_decoder_destroy(ptr); +using namespace AudioCore::OpusDecoder; + +class IHardwareOpusDecoder final : public ServiceFramework<IHardwareOpusDecoder> { +public: +    explicit IHardwareOpusDecoder(Core::System& system_, HardwareOpus& hardware_opus) +        : ServiceFramework{system_, "IHardwareOpusDecoder"}, +          impl{std::make_unique<AudioCore::OpusDecoder::OpusDecoder>(system_, hardware_opus)} { +        // clang-format off +        static const FunctionInfo functions[] = { +            {0, &IHardwareOpusDecoder::DecodeInterleavedOld, "DecodeInterleavedOld"}, +            {1, &IHardwareOpusDecoder::SetContext, "SetContext"}, +            {2, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamOld, "DecodeInterleavedForMultiStreamOld"}, +            {3, &IHardwareOpusDecoder::SetContextForMultiStream, "SetContextForMultiStream"}, +            {4, &IHardwareOpusDecoder::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"}, +            {5, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfOld, "DecodeInterleavedForMultiStreamWithPerfOld"}, +            {6, &IHardwareOpusDecoder::DecodeInterleavedWithPerfAndResetOld, "DecodeInterleavedWithPerfAndResetOld"}, +            {7, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfAndResetOld, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"}, +            {8, &IHardwareOpusDecoder::DecodeInterleaved, "DecodeInterleaved"}, +            {9, &IHardwareOpusDecoder::DecodeInterleavedForMultiStream, "DecodeInterleavedForMultiStream"}, +        }; +        // clang-format on + +        RegisterHandlers(functions);      } -}; -using OpusDecoderPtr = std::unique_ptr<OpusMSDecoder, OpusDeleter>; +    Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, +                      u64 transfer_memory_size) { +        return impl->Initialize(params, transfer_memory, transfer_memory_size); +    } -struct OpusPacketHeader { -    // Packet size in bytes. -    u32_be size; -    // Indicates the final range of the codec's entropy coder. -    u32_be final_range; -}; -static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size"); +    Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, +                      u64 transfer_memory_size) { +        return impl->Initialize(params, transfer_memory, transfer_memory_size); +    } -class OpusDecoderState { -public: -    /// Describes extra behavior that may be asked of the decoding context. -    enum class ExtraBehavior { -        /// No extra behavior. -        None, +private: +    void DecodeInterleavedOld(HLERequestContext& ctx) { +        IPC::RequestParser rp{ctx}; -        /// Resets the decoder context back to a freshly initialized state. -        ResetContext, -    }; +        auto input_data{ctx.ReadBuffer(0)}; +        output_data.resize_destructive(ctx.GetWriteBufferSize()); -    enum class PerfTime { -        Disabled, -        Enabled, -    }; +        u32 size{}; +        u32 sample_count{}; +        auto result = +            impl->DecodeInterleaved(&size, nullptr, &sample_count, input_data, output_data, false); -    explicit OpusDecoderState(OpusDecoderPtr decoder_, u32 sample_rate_, u32 channel_count_) -        : decoder{std::move(decoder_)}, sample_rate{sample_rate_}, channel_count{channel_count_} {} - -    // Decodes interleaved Opus packets. Optionally allows reporting time taken to -    // perform the decoding, as well as any relevant extra behavior. -    void DecodeInterleaved(HLERequestContext& ctx, PerfTime perf_time, -                           ExtraBehavior extra_behavior) { -        if (perf_time == PerfTime::Disabled) { -            DecodeInterleavedHelper(ctx, nullptr, extra_behavior); -        } else { -            u64 performance = 0; -            DecodeInterleavedHelper(ctx, &performance, extra_behavior); -        } +        LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {}", size, sample_count); + +        ctx.WriteBuffer(output_data); + +        IPC::ResponseBuilder rb{ctx, 4}; +        rb.Push(result); +        rb.Push(size); +        rb.Push(sample_count);      } -private: -    void DecodeInterleavedHelper(HLERequestContext& ctx, u64* performance, -                                 ExtraBehavior extra_behavior) { -        u32 consumed = 0; -        u32 sample_count = 0; -        samples.resize_destructive(ctx.GetWriteBufferNumElements<opus_int16>()); - -        if (extra_behavior == ExtraBehavior::ResetContext) { -            ResetDecoderContext(); -        } - -        if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) { -            LOG_ERROR(Audio, "Failed to decode opus data"); -            IPC::ResponseBuilder rb{ctx, 2}; -            // TODO(ogniK): Use correct error code -            rb.Push(ResultUnknown); -            return; -        } - -        const u32 param_size = performance != nullptr ? 6 : 4; -        IPC::ResponseBuilder rb{ctx, param_size}; -        rb.Push(ResultSuccess); -        rb.Push<u32>(consumed); -        rb.Push<u32>(sample_count); -        if (performance) { -            rb.Push<u64>(*performance); -        } -        ctx.WriteBuffer(samples); +    void SetContext(HLERequestContext& ctx) { +        IPC::RequestParser rp{ctx}; + +        LOG_DEBUG(Service_Audio, "called"); + +        auto input_data{ctx.ReadBuffer(0)}; +        auto result = impl->SetContext(input_data); + +        IPC::ResponseBuilder rb{ctx, 2}; +        rb.Push(result); +    } + +    void DecodeInterleavedForMultiStreamOld(HLERequestContext& ctx) { +        IPC::RequestParser rp{ctx}; + +        auto input_data{ctx.ReadBuffer(0)}; +        output_data.resize_destructive(ctx.GetWriteBufferSize()); + +        u32 size{}; +        u32 sample_count{}; +        auto result = impl->DecodeInterleavedForMultiStream(&size, nullptr, &sample_count, +                                                            input_data, output_data, false); + +        LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {}", size, sample_count); + +        ctx.WriteBuffer(output_data); + +        IPC::ResponseBuilder rb{ctx, 4}; +        rb.Push(result); +        rb.Push(size); +        rb.Push(sample_count);      } -    bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input, -                        std::span<opus_int16> output, u64* out_performance_time) const { -        const auto start_time = std::chrono::steady_clock::now(); -        const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); -        if (sizeof(OpusPacketHeader) > input.size()) { -            LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}", -                      sizeof(OpusPacketHeader), input.size()); -            return false; -        } - -        OpusPacketHeader hdr{}; -        std::memcpy(&hdr, input.data(), sizeof(OpusPacketHeader)); -        if (sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size) > input.size()) { -            LOG_ERROR(Audio, "Input does not fit in the opus header size. data_sz={}, input_sz={}", -                      sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size), input.size()); -            return false; -        } - -        const auto frame = input.data() + sizeof(OpusPacketHeader); -        const auto decoded_sample_count = opus_packet_get_nb_samples( -            frame, static_cast<opus_int32>(input.size() - sizeof(OpusPacketHeader)), -            static_cast<opus_int32>(sample_rate)); -        if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) { -            LOG_ERROR( -                Audio, -                "Decoded data does not fit into the output data, decoded_sz={}, raw_output_sz={}", -                decoded_sample_count * channel_count * sizeof(u16), raw_output_sz); -            return false; -        } - -        const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)); -        const auto out_sample_count = -            opus_multistream_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0); -        if (out_sample_count < 0) { -            LOG_ERROR(Audio, -                      "Incorrect sample count received from opus_decode, " -                      "output_sample_count={}, frame_size={}, data_sz_from_hdr={}", -                      out_sample_count, frame_size, static_cast<u32>(hdr.size)); -            return false; -        } - -        const auto end_time = std::chrono::steady_clock::now() - start_time; -        sample_count = out_sample_count; -        consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size); -        if (out_performance_time != nullptr) { -            *out_performance_time = -                std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count(); -        } - -        return true; +    void SetContextForMultiStream(HLERequestContext& ctx) { +        IPC::RequestParser rp{ctx}; + +        LOG_DEBUG(Service_Audio, "called"); + +        auto input_data{ctx.ReadBuffer(0)}; +        auto result = impl->SetContext(input_data); + +        IPC::ResponseBuilder rb{ctx, 2}; +        rb.Push(result);      } -    void ResetDecoderContext() { -        ASSERT(decoder != nullptr); +    void DecodeInterleavedWithPerfOld(HLERequestContext& ctx) { +        IPC::RequestParser rp{ctx}; + +        auto input_data{ctx.ReadBuffer(0)}; +        output_data.resize_destructive(ctx.GetWriteBufferSize()); + +        u32 size{}; +        u32 sample_count{}; +        u64 time_taken{}; +        auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data, +                                              output_data, false); + +        LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {} time taken {}", size, +                  sample_count, time_taken); -        opus_multistream_decoder_ctl(decoder.get(), OPUS_RESET_STATE); +        ctx.WriteBuffer(output_data); + +        IPC::ResponseBuilder rb{ctx, 6}; +        rb.Push(result); +        rb.Push(size); +        rb.Push(sample_count); +        rb.Push(time_taken);      } -    OpusDecoderPtr decoder; -    u32 sample_rate; -    u32 channel_count; -    Common::ScratchBuffer<opus_int16> samples; -}; +    void DecodeInterleavedForMultiStreamWithPerfOld(HLERequestContext& ctx) { +        IPC::RequestParser rp{ctx}; -class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { -public: -    explicit IHardwareOpusDecoderManager(Core::System& system_, OpusDecoderState decoder_state_) -        : ServiceFramework{system_, "IHardwareOpusDecoderManager"}, decoder_state{ -                                                                        std::move(decoder_state_)} { -        // clang-format off -        static const FunctionInfo functions[] = { -            {0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"}, -            {1, nullptr, "SetContext"}, -            {2, nullptr, "DecodeInterleavedForMultiStreamOld"}, -            {3, nullptr, "SetContextForMultiStream"}, -            {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"}, -            {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"}, -            {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"}, -            {7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"}, -            {8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, -            {9, &IHardwareOpusDecoderManager::DecodeInterleavedForMultiStream, "DecodeInterleavedForMultiStream"}, -        }; -        // clang-format on +        auto input_data{ctx.ReadBuffer(0)}; +        output_data.resize_destructive(ctx.GetWriteBufferSize()); -        RegisterHandlers(functions); +        u32 size{}; +        u32 sample_count{}; +        u64 time_taken{}; +        auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count, +                                                            input_data, output_data, false); + +        LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {} time taken {}", size, +                  sample_count, time_taken); + +        ctx.WriteBuffer(output_data); + +        IPC::ResponseBuilder rb{ctx, 6}; +        rb.Push(result); +        rb.Push(size); +        rb.Push(sample_count); +        rb.Push(time_taken);      } -private: -    void DecodeInterleavedOld(HLERequestContext& ctx) { -        LOG_DEBUG(Audio, "called"); +    void DecodeInterleavedWithPerfAndResetOld(HLERequestContext& ctx) { +        IPC::RequestParser rp{ctx}; + +        auto reset{rp.Pop<bool>()}; + +        auto input_data{ctx.ReadBuffer(0)}; +        output_data.resize_destructive(ctx.GetWriteBufferSize()); + +        u32 size{}; +        u32 sample_count{}; +        u64 time_taken{}; +        auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data, +                                              output_data, reset); + +        LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}", +                  reset, size, sample_count, time_taken); + +        ctx.WriteBuffer(output_data); -        decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Disabled, -                                        OpusDecoderState::ExtraBehavior::None); +        IPC::ResponseBuilder rb{ctx, 6}; +        rb.Push(result); +        rb.Push(size); +        rb.Push(sample_count); +        rb.Push(time_taken);      } -    void DecodeInterleavedWithPerfOld(HLERequestContext& ctx) { -        LOG_DEBUG(Audio, "called"); +    void DecodeInterleavedForMultiStreamWithPerfAndResetOld(HLERequestContext& ctx) { +        IPC::RequestParser rp{ctx}; + +        auto reset{rp.Pop<bool>()}; + +        auto input_data{ctx.ReadBuffer(0)}; +        output_data.resize_destructive(ctx.GetWriteBufferSize()); + +        u32 size{}; +        u32 sample_count{}; +        u64 time_taken{}; +        auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count, +                                                            input_data, output_data, reset); -        decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, -                                        OpusDecoderState::ExtraBehavior::None); +        LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}", +                  reset, size, sample_count, time_taken); + +        ctx.WriteBuffer(output_data); + +        IPC::ResponseBuilder rb{ctx, 6}; +        rb.Push(result); +        rb.Push(size); +        rb.Push(sample_count); +        rb.Push(time_taken);      }      void DecodeInterleaved(HLERequestContext& ctx) { -        LOG_DEBUG(Audio, "called"); -          IPC::RequestParser rp{ctx}; -        const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext -                                                   : OpusDecoderState::ExtraBehavior::None; -        decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior); +        auto reset{rp.Pop<bool>()}; + +        auto input_data{ctx.ReadBuffer(0)}; +        output_data.resize_destructive(ctx.GetWriteBufferSize()); + +        u32 size{}; +        u32 sample_count{}; +        u64 time_taken{}; +        auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data, +                                              output_data, reset); + +        LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}", +                  reset, size, sample_count, time_taken); + +        ctx.WriteBuffer(output_data); + +        IPC::ResponseBuilder rb{ctx, 6}; +        rb.Push(result); +        rb.Push(size); +        rb.Push(sample_count); +        rb.Push(time_taken);      }      void DecodeInterleavedForMultiStream(HLERequestContext& ctx) { -        LOG_DEBUG(Audio, "called"); -          IPC::RequestParser rp{ctx}; -        const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext -                                                   : OpusDecoderState::ExtraBehavior::None; -        decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior); +        auto reset{rp.Pop<bool>()}; + +        auto input_data{ctx.ReadBuffer(0)}; +        output_data.resize_destructive(ctx.GetWriteBufferSize()); + +        u32 size{}; +        u32 sample_count{}; +        u64 time_taken{}; +        auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count, +                                                            input_data, output_data, reset); + +        LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}", +                  reset, size, sample_count, time_taken); + +        ctx.WriteBuffer(output_data); + +        IPC::ResponseBuilder rb{ctx, 6}; +        rb.Push(result); +        rb.Push(size); +        rb.Push(sample_count); +        rb.Push(time_taken);      } -    OpusDecoderState decoder_state; +    std::unique_ptr<AudioCore::OpusDecoder::OpusDecoder> impl; +    Common::ScratchBuffer<u8> output_data;  }; -std::size_t WorkerBufferSize(u32 channel_count) { -    ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); -    constexpr int num_streams = 1; -    const int num_stereo_streams = channel_count == 2 ? 1 : 0; -    return opus_multistream_decoder_get_size(num_streams, num_stereo_streams); -} +void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; -// Creates the mapping table that maps the input channels to the particular -// output channels. In the stereo case, we map the left and right input channels -// to the left and right output channels respectively. -// -// However, in the monophonic case, we only map the one available channel -// to the sole output channel. We specify 255 for the would-be right channel -// as this is a special value defined by Opus to indicate to the decoder to -// ignore that channel. -std::array<u8, 2> CreateMappingTable(u32 channel_count) { -    if (channel_count == 2) { -        return {{0, 1}}; -    } +    auto params = rp.PopRaw<OpusParameters>(); +    auto transfer_memory_size{rp.Pop<u32>()}; +    auto transfer_memory_handle{ctx.GetCopyHandle(0)}; +    auto transfer_memory{ +        system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( +            transfer_memory_handle)}; + +    LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}", +              params.sample_rate, params.channel_count, transfer_memory_size); -    return {{0, 255}}; +    auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())}; + +    OpusParametersEx ex{ +        .sample_rate = params.sample_rate, +        .channel_count = params.channel_count, +        .use_large_frame_size = false, +    }; +    auto result = decoder->Initialize(ex, transfer_memory.GetPointerUnsafe(), transfer_memory_size); + +    IPC::ResponseBuilder rb{ctx, 2, 0, 1}; +    rb.Push(result); +    rb.PushIpcInterface(decoder);  } -} // Anonymous namespace  void HwOpus::GetWorkBufferSize(HLERequestContext& ctx) {      IPC::RequestParser rp{ctx}; -    const auto sample_rate = rp.Pop<u32>(); -    const auto channel_count = rp.Pop<u32>(); +    auto params = rp.PopRaw<OpusParameters>(); -    LOG_DEBUG(Audio, "called with sample_rate={}, channel_count={}", sample_rate, channel_count); +    u64 size{}; +    auto result = impl.GetWorkBufferSize(params, size); -    ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || -                   sample_rate == 12000 || sample_rate == 8000, -               "Invalid sample rate"); -    ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); +    LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} -- returned size 0x{:X}", +              params.sample_rate, params.channel_count, size); -    const u32 worker_buffer_sz = static_cast<u32>(WorkerBufferSize(channel_count)); -    LOG_DEBUG(Audio, "worker_buffer_sz={}", worker_buffer_sz); - -    IPC::ResponseBuilder rb{ctx, 3}; -    rb.Push(ResultSuccess); -    rb.Push<u32>(worker_buffer_sz); +    IPC::ResponseBuilder rb{ctx, 4}; +    rb.Push(result); +    rb.Push(size);  } -void HwOpus::GetWorkBufferSizeEx(HLERequestContext& ctx) { -    GetWorkBufferSize(ctx); +void HwOpus::OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; + +    auto input{ctx.ReadBuffer()}; +    OpusMultiStreamParameters params; +    std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParameters)); + +    auto transfer_memory_size{rp.Pop<u32>()}; +    auto transfer_memory_handle{ctx.GetCopyHandle(0)}; +    auto transfer_memory{ +        system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( +            transfer_memory_handle)}; + +    LOG_DEBUG(Service_Audio, +              "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " +              "transfer_memory_size 0x{:X}", +              params.sample_rate, params.channel_count, params.total_stream_count, +              params.stereo_stream_count, transfer_memory_size); + +    auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())}; + +    OpusMultiStreamParametersEx ex{ +        .sample_rate = params.sample_rate, +        .channel_count = params.channel_count, +        .total_stream_count = params.total_stream_count, +        .stereo_stream_count = params.stereo_stream_count, +        .use_large_frame_size = false, +        .mappings{}, +    }; +    std::memcpy(ex.mappings.data(), params.mappings.data(), sizeof(params.mappings)); +    auto result = decoder->Initialize(ex, transfer_memory.GetPointerUnsafe(), transfer_memory_size); + +    IPC::ResponseBuilder rb{ctx, 2, 0, 1}; +    rb.Push(result); +    rb.PushIpcInterface(decoder);  } -void HwOpus::GetWorkBufferSizeExEx(HLERequestContext& ctx) { -    GetWorkBufferSizeEx(ctx); +void HwOpus::GetWorkBufferSizeForMultiStream(HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; + +    auto input{ctx.ReadBuffer()}; +    OpusMultiStreamParameters params; +    std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParameters)); + +    u64 size{}; +    auto result = impl.GetWorkBufferSizeForMultiStream(params, size); + +    LOG_DEBUG(Service_Audio, "size 0x{:X}", size); + +    IPC::ResponseBuilder rb{ctx, 4}; +    rb.Push(result); +    rb.Push(size);  } -void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) { -    OpusMultiStreamParametersEx param; -    std::memcpy(¶m, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); +void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; -    const auto sample_rate = param.sample_rate; -    const auto channel_count = param.channel_count; -    const auto number_streams = param.number_streams; -    const auto number_stereo_streams = param.number_stereo_streams; +    auto params = rp.PopRaw<OpusParametersEx>(); +    auto transfer_memory_size{rp.Pop<u32>()}; +    auto transfer_memory_handle{ctx.GetCopyHandle(0)}; +    auto transfer_memory{ +        system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( +            transfer_memory_handle)}; -    LOG_DEBUG( -        Audio, -        "called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}", -        sample_rate, channel_count, number_streams, number_stereo_streams); +    LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}", +              params.sample_rate, params.channel_count, transfer_memory_size); -    ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || -                   sample_rate == 12000 || sample_rate == 8000, -               "Invalid sample rate"); +    auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())}; -    const u32 worker_buffer_sz = -        static_cast<u32>(opus_multistream_decoder_get_size(number_streams, number_stereo_streams)); +    auto result = +        decoder->Initialize(params, transfer_memory.GetPointerUnsafe(), transfer_memory_size); -    IPC::ResponseBuilder rb{ctx, 3}; -    rb.Push(ResultSuccess); -    rb.Push<u32>(worker_buffer_sz); +    IPC::ResponseBuilder rb{ctx, 2, 0, 1}; +    rb.Push(result); +    rb.PushIpcInterface(decoder);  } -void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) { +void HwOpus::GetWorkBufferSizeEx(HLERequestContext& ctx) {      IPC::RequestParser rp{ctx}; -    const auto sample_rate = rp.Pop<u32>(); -    const auto channel_count = rp.Pop<u32>(); -    const auto buffer_sz = rp.Pop<u32>(); - -    LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate, -              channel_count, buffer_sz); - -    ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || -                   sample_rate == 12000 || sample_rate == 8000, -               "Invalid sample rate"); -    ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); - -    const std::size_t worker_sz = WorkerBufferSize(channel_count); -    ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large"); - -    const int num_stereo_streams = channel_count == 2 ? 1 : 0; -    const auto mapping_table = CreateMappingTable(channel_count); - -    int error = 0; -    OpusDecoderPtr decoder{ -        opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1, -                                        num_stereo_streams, mapping_table.data(), &error)}; -    if (error != OPUS_OK || decoder == nullptr) { -        LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error); -        IPC::ResponseBuilder rb{ctx, 2}; -        // TODO(ogniK): Use correct error code -        rb.Push(ResultUnknown); -        return; -    } +    auto params = rp.PopRaw<OpusParametersEx>(); -    IPC::ResponseBuilder rb{ctx, 2, 0, 1}; -    rb.Push(ResultSuccess); -    rb.PushIpcInterface<IHardwareOpusDecoderManager>( -        system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); +    u64 size{}; +    auto result = impl.GetWorkBufferSizeEx(params, size); + +    LOG_DEBUG(Service_Audio, "size 0x{:X}", size); + +    IPC::ResponseBuilder rb{ctx, 4}; +    rb.Push(result); +    rb.Push(size);  } -void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) { +void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) {      IPC::RequestParser rp{ctx}; -    const auto sample_rate = rp.Pop<u32>(); -    const auto channel_count = rp.Pop<u32>(); -    LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); +    auto input{ctx.ReadBuffer()}; +    OpusMultiStreamParametersEx params; +    std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx)); + +    auto transfer_memory_size{rp.Pop<u32>()}; +    auto transfer_memory_handle{ctx.GetCopyHandle(0)}; +    auto transfer_memory{ +        system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( +            transfer_memory_handle)}; -    ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || -                   sample_rate == 12000 || sample_rate == 8000, -               "Invalid sample rate"); -    ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); +    LOG_DEBUG(Service_Audio, +              "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " +              "use_large_frame_size {}" +              "transfer_memory_size 0x{:X}", +              params.sample_rate, params.channel_count, params.total_stream_count, +              params.stereo_stream_count, params.use_large_frame_size, transfer_memory_size); -    const int num_stereo_streams = channel_count == 2 ? 1 : 0; -    const auto mapping_table = CreateMappingTable(channel_count); +    auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())}; -    int error = 0; -    OpusDecoderPtr decoder{ -        opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1, -                                        num_stereo_streams, mapping_table.data(), &error)}; -    if (error != OPUS_OK || decoder == nullptr) { -        LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error); -        IPC::ResponseBuilder rb{ctx, 2}; -        // TODO(ogniK): Use correct error code -        rb.Push(ResultUnknown); -        return; -    } +    auto result = +        decoder->Initialize(params, transfer_memory.GetPointerUnsafe(), transfer_memory_size);      IPC::ResponseBuilder rb{ctx, 2, 0, 1}; -    rb.Push(ResultSuccess); -    rb.PushIpcInterface<IHardwareOpusDecoderManager>( -        system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); +    rb.Push(result); +    rb.PushIpcInterface(decoder);  } -void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) { +void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; + +    auto input{ctx.ReadBuffer()};      OpusMultiStreamParametersEx params; -    std::memcpy(¶ms, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); - -    const auto& sample_rate = params.sample_rate; -    const auto& channel_count = params.channel_count; - -    LOG_INFO( -        Audio, -        "called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}", -        sample_rate, channel_count, params.number_streams, params.number_stereo_streams); - -    ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || -                   sample_rate == 12000 || sample_rate == 8000, -               "Invalid sample rate"); - -    int error = 0; -    OpusDecoderPtr decoder{opus_multistream_decoder_create( -        sample_rate, static_cast<int>(channel_count), params.number_streams, -        params.number_stereo_streams, params.channel_mappings.data(), &error)}; -    if (error != OPUS_OK || decoder == nullptr) { -        LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error); -        IPC::ResponseBuilder rb{ctx, 2}; -        // TODO(ogniK): Use correct error code -        rb.Push(ResultUnknown); -        return; -    } +    std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx)); -    IPC::ResponseBuilder rb{ctx, 2, 0, 1}; -    rb.Push(ResultSuccess); -    rb.PushIpcInterface<IHardwareOpusDecoderManager>( -        system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); +    u64 size{}; +    auto result = impl.GetWorkBufferSizeForMultiStreamEx(params, size); + +    LOG_DEBUG(Service_Audio, +              "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " +              "use_large_frame_size {} -- returned size 0x{:X}", +              params.sample_rate, params.channel_count, params.total_stream_count, +              params.stereo_stream_count, params.use_large_frame_size, size); + +    IPC::ResponseBuilder rb{ctx, 4}; +    rb.Push(result); +    rb.Push(size); +} + +void HwOpus::GetWorkBufferSizeExEx(HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    auto params = rp.PopRaw<OpusParametersEx>(); + +    u64 size{}; +    auto result = impl.GetWorkBufferSizeExEx(params, size); + +    LOG_DEBUG(Service_Audio, "size 0x{:X}", size); + +    IPC::ResponseBuilder rb{ctx, 4}; +    rb.Push(result); +    rb.Push(size); +} + +void HwOpus::GetWorkBufferSizeForMultiStreamExEx(HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; + +    auto input{ctx.ReadBuffer()}; +    OpusMultiStreamParametersEx params; +    std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx)); + +    u64 size{}; +    auto result = impl.GetWorkBufferSizeForMultiStreamExEx(params, size); + +    LOG_DEBUG(Service_Audio, "size 0x{:X}", size); + +    IPC::ResponseBuilder rb{ctx, 4}; +    rb.Push(result); +    rb.Push(size);  } -HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { +HwOpus::HwOpus(Core::System& system_) +    : ServiceFramework{system_, "hwopus"}, system{system_}, impl{system} {      static const FunctionInfo functions[] = {          {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"},          {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"}, -        {2, nullptr, "OpenOpusDecoderForMultiStream"}, -        {3, nullptr, "GetWorkBufferSizeForMultiStream"}, +        {2, &HwOpus::OpenHardwareOpusDecoderForMultiStream, "OpenOpusDecoderForMultiStream"}, +        {3, &HwOpus::GetWorkBufferSizeForMultiStream, "GetWorkBufferSizeForMultiStream"},          {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"},          {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"},          {6, &HwOpus::OpenHardwareOpusDecoderForMultiStreamEx,           "OpenHardwareOpusDecoderForMultiStreamEx"},          {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"},          {8, &HwOpus::GetWorkBufferSizeExEx, "GetWorkBufferSizeExEx"}, -        {9, nullptr, "GetWorkBufferSizeForMultiStreamExEx"}, +        {9, &HwOpus::GetWorkBufferSizeForMultiStreamExEx, "GetWorkBufferSizeForMultiStreamExEx"},      };      RegisterHandlers(functions);  } diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h index 90867bf74..d3960065e 100644 --- a/src/core/hle/service/audio/hwopus.h +++ b/src/core/hle/service/audio/hwopus.h @@ -3,6 +3,7 @@  #pragma once +#include "audio_core/opus/decoder_manager.h"  #include "core/hle/service/service.h"  namespace Core { @@ -11,18 +12,6 @@ class System;  namespace Service::Audio { -struct OpusMultiStreamParametersEx { -    u32 sample_rate; -    u32 channel_count; -    u32 number_streams; -    u32 number_stereo_streams; -    u32 use_large_frame_size; -    u32 padding; -    std::array<u8, 0x100> channel_mappings; -}; -static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118, -              "OpusMultiStreamParametersEx has incorrect size"); -  class HwOpus final : public ServiceFramework<HwOpus> {  public:      explicit HwOpus(Core::System& system_); @@ -30,12 +19,18 @@ public:  private:      void OpenHardwareOpusDecoder(HLERequestContext& ctx); -    void OpenHardwareOpusDecoderEx(HLERequestContext& ctx); -    void OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx);      void GetWorkBufferSize(HLERequestContext& ctx); +    void OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx); +    void GetWorkBufferSizeForMultiStream(HLERequestContext& ctx); +    void OpenHardwareOpusDecoderEx(HLERequestContext& ctx);      void GetWorkBufferSizeEx(HLERequestContext& ctx); -    void GetWorkBufferSizeExEx(HLERequestContext& ctx); +    void OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx);      void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx); +    void GetWorkBufferSizeExEx(HLERequestContext& ctx); +    void GetWorkBufferSizeForMultiStreamExEx(HLERequestContext& ctx); + +    Core::System& system; +    AudioCore::OpusDecoder::OpusDecoderManager impl;  };  } // namespace Service::Audio | 
