From f65539504f009bb18409907fef429bf8c70e0508 Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 20 Feb 2024 00:55:02 -0500 Subject: audio: split IHardwarweOpusDecoder, move IHardwareOpusDecoderManager --- src/core/CMakeLists.txt | 6 +- src/core/hle/service/audio/audio.cpp | 5 +- .../hle/service/audio/hardware_opus_decoder.cpp | 265 +++++++++++ src/core/hle/service/audio/hardware_opus_decoder.h | 38 ++ .../audio/hardware_opus_decoder_manager.cpp | 243 ++++++++++ .../service/audio/hardware_opus_decoder_manager.h | 36 ++ src/core/hle/service/audio/hwopus.cpp | 502 --------------------- src/core/hle/service/audio/hwopus.h | 36 -- 8 files changed, 589 insertions(+), 542 deletions(-) create mode 100644 src/core/hle/service/audio/hardware_opus_decoder.cpp create mode 100644 src/core/hle/service/audio/hardware_opus_decoder.h create mode 100644 src/core/hle/service/audio/hardware_opus_decoder_manager.cpp create mode 100644 src/core/hle/service/audio/hardware_opus_decoder_manager.h delete mode 100644 src/core/hle/service/audio/hwopus.cpp delete mode 100644 src/core/hle/service/audio/hwopus.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 5a1fc83bd..c49560789 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -512,8 +512,10 @@ add_library(core STATIC hle/service/audio/final_output_recorder_manager_for_applet.h hle/service/audio/final_output_recorder_manager.cpp hle/service/audio/final_output_recorder_manager.h - hle/service/audio/hwopus.cpp - hle/service/audio/hwopus.h + hle/service/audio/hardware_opus_decoder_manager.cpp + hle/service/audio/hardware_opus_decoder_manager.h + hle/service/audio/hardware_opus_decoder.cpp + hle/service/audio/hardware_opus_decoder.h hle/service/bcat/backend/backend.cpp hle/service/bcat/backend/backend.h hle/service/bcat/bcat.cpp diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp index 82a2ae283..331176bf7 100644 --- a/src/core/hle/service/audio/audio.cpp +++ b/src/core/hle/service/audio/audio.cpp @@ -9,7 +9,7 @@ #include "core/hle/service/audio/audio_renderer_manager.h" #include "core/hle/service/audio/final_output_recorder_manager.h" #include "core/hle/service/audio/final_output_recorder_manager_for_applet.h" -#include "core/hle/service/audio/hwopus.h" +#include "core/hle/service/audio/hardware_opus_decoder_manager.h" #include "core/hle/service/server_manager.h" #include "core/hle/service/service.h" @@ -27,7 +27,8 @@ void LoopProcess(Core::System& system) { std::make_shared(system)); server_manager->RegisterNamedService("audren:u", std::make_shared(system)); - server_manager->RegisterNamedService("hwopus", std::make_shared(system)); + server_manager->RegisterNamedService("hwopus", + std::make_shared(system)); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/audio/hardware_opus_decoder.cpp b/src/core/hle/service/audio/hardware_opus_decoder.cpp new file mode 100644 index 000000000..876bfe334 --- /dev/null +++ b/src/core/hle/service/audio/hardware_opus_decoder.cpp @@ -0,0 +1,265 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/audio/hardware_opus_decoder.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::Audio { + +using namespace AudioCore::OpusDecoder; + +IHardwareOpusDecoder::IHardwareOpusDecoder(Core::System& system_, HardwareOpus& hardware_opus) + : ServiceFramework{system_, "IHardwareOpusDecoder"}, + impl{std::make_unique(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); +} + +IHardwareOpusDecoder::~IHardwareOpusDecoder() = default; + +Result IHardwareOpusDecoder::Initialize(OpusParametersEx& params, + Kernel::KTransferMemory* transfer_memory, + u64 transfer_memory_size) { + return impl->Initialize(params, transfer_memory, transfer_memory_size); +} + +Result IHardwareOpusDecoder::Initialize(OpusMultiStreamParametersEx& params, + Kernel::KTransferMemory* transfer_memory, + u64 transfer_memory_size) { + return impl->Initialize(params, transfer_memory, transfer_memory_size); +} + +void IHardwareOpusDecoder::DecodeInterleavedOld(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->DecodeInterleaved(&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); +} + +void IHardwareOpusDecoder::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 IHardwareOpusDecoder::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); +} + +void IHardwareOpusDecoder::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 IHardwareOpusDecoder::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); + + ctx.WriteBuffer(output_data); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(result); + rb.Push(size); + rb.Push(sample_count); + rb.Push(time_taken); +} + +void IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfOld(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->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); +} + +void IHardwareOpusDecoder::DecodeInterleavedWithPerfAndResetOld(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto reset{rp.Pop()}; + + 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 IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfAndResetOld( + HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto reset{rp.Pop()}; + + 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); +} + +void IHardwareOpusDecoder::DecodeInterleaved(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto reset{rp.Pop()}; + + 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 IHardwareOpusDecoder::DecodeInterleavedForMultiStream(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto reset{rp.Pop()}; + + 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); +} + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/hardware_opus_decoder.h b/src/core/hle/service/audio/hardware_opus_decoder.h new file mode 100644 index 000000000..fe6636ce6 --- /dev/null +++ b/src/core/hle/service/audio/hardware_opus_decoder.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "audio_core/opus/decoder.h" +#include "core/hle/service/service.h" + +namespace Service::Audio { + +class IHardwareOpusDecoder final : public ServiceFramework { +public: + explicit IHardwareOpusDecoder(Core::System& system_, + AudioCore::OpusDecoder::HardwareOpus& hardware_opus); + ~IHardwareOpusDecoder() override; + + Result Initialize(AudioCore::OpusDecoder::OpusParametersEx& params, + Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size); + Result Initialize(AudioCore::OpusDecoder::OpusMultiStreamParametersEx& params, + Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size); + +private: + void DecodeInterleavedOld(HLERequestContext& ctx); + void SetContext(HLERequestContext& ctx); + void DecodeInterleavedForMultiStreamOld(HLERequestContext& ctx); + void SetContextForMultiStream(HLERequestContext& ctx); + void DecodeInterleavedWithPerfOld(HLERequestContext& ctx); + void DecodeInterleavedForMultiStreamWithPerfOld(HLERequestContext& ctx); + void DecodeInterleavedWithPerfAndResetOld(HLERequestContext& ctx); + void DecodeInterleavedForMultiStreamWithPerfAndResetOld(HLERequestContext& ctx); + void DecodeInterleaved(HLERequestContext& ctx); + void DecodeInterleavedForMultiStream(HLERequestContext& ctx); + + std::unique_ptr impl; + Common::ScratchBuffer output_data; +}; + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/hardware_opus_decoder_manager.cpp b/src/core/hle/service/audio/hardware_opus_decoder_manager.cpp new file mode 100644 index 000000000..6d7205cd2 --- /dev/null +++ b/src/core/hle/service/audio/hardware_opus_decoder_manager.cpp @@ -0,0 +1,243 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/audio/hardware_opus_decoder.h" +#include "core/hle/service/audio/hardware_opus_decoder_manager.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::Audio { + +using namespace AudioCore::OpusDecoder; + +void IHardwareOpusDecoderManager::OpenHardwareOpusDecoder(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto params = rp.PopRaw(); + auto transfer_memory_size{rp.Pop()}; + auto transfer_memory_handle{ctx.GetCopyHandle(0)}; + auto transfer_memory{ctx.GetObjectFromHandle(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); + + auto decoder{std::make_shared(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); +} + +void IHardwareOpusDecoderManager::GetWorkBufferSize(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto params = rp.PopRaw(); + + u64 size{}; + auto result = impl.GetWorkBufferSize(params, size); + + LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} -- returned size 0x{:X}", + params.sample_rate, params.channel_count, size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(size); +} + +void IHardwareOpusDecoderManager::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()}; + auto transfer_memory_handle{ctx.GetCopyHandle(0)}; + auto transfer_memory{ctx.GetObjectFromHandle(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(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 IHardwareOpusDecoderManager::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 IHardwareOpusDecoderManager::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto params = rp.PopRaw(); + auto transfer_memory_size{rp.Pop()}; + auto transfer_memory_handle{ctx.GetCopyHandle(0)}; + auto transfer_memory{ctx.GetObjectFromHandle(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); + + auto decoder{std::make_shared(system, impl.GetHardwareOpus())}; + + auto result = + decoder->Initialize(params, transfer_memory.GetPointerUnsafe(), transfer_memory_size); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(result); + rb.PushIpcInterface(decoder); +} + +void IHardwareOpusDecoderManager::GetWorkBufferSizeEx(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto params = rp.PopRaw(); + + 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 IHardwareOpusDecoderManager::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto input{ctx.ReadBuffer()}; + OpusMultiStreamParametersEx params; + std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx)); + + auto transfer_memory_size{rp.Pop()}; + auto transfer_memory_handle{ctx.GetCopyHandle(0)}; + auto transfer_memory{ctx.GetObjectFromHandle(transfer_memory_handle)}; + + 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); + + auto decoder{std::make_shared(system, impl.GetHardwareOpus())}; + + auto result = + decoder->Initialize(params, transfer_memory.GetPointerUnsafe(), transfer_memory_size); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(result); + rb.PushIpcInterface(decoder); +} + +void IHardwareOpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto input{ctx.ReadBuffer()}; + OpusMultiStreamParametersEx params; + std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx)); + + 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 IHardwareOpusDecoderManager::GetWorkBufferSizeExEx(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto params = rp.PopRaw(); + + 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 IHardwareOpusDecoderManager::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); +} + +IHardwareOpusDecoderManager::IHardwareOpusDecoderManager(Core::System& system_) + : ServiceFramework{system_, "hwopus"}, system{system_}, impl{system} { + static const FunctionInfo functions[] = { + {0, &IHardwareOpusDecoderManager::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"}, + {1, &IHardwareOpusDecoderManager::GetWorkBufferSize, "GetWorkBufferSize"}, + {2, &IHardwareOpusDecoderManager::OpenHardwareOpusDecoderForMultiStream, + "OpenOpusDecoderForMultiStream"}, + {3, &IHardwareOpusDecoderManager::GetWorkBufferSizeForMultiStream, + "GetWorkBufferSizeForMultiStream"}, + {4, &IHardwareOpusDecoderManager::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"}, + {5, &IHardwareOpusDecoderManager::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"}, + {6, &IHardwareOpusDecoderManager::OpenHardwareOpusDecoderForMultiStreamEx, + "OpenHardwareOpusDecoderForMultiStreamEx"}, + {7, &IHardwareOpusDecoderManager::GetWorkBufferSizeForMultiStreamEx, + "GetWorkBufferSizeForMultiStreamEx"}, + {8, &IHardwareOpusDecoderManager::GetWorkBufferSizeExEx, "GetWorkBufferSizeExEx"}, + {9, &IHardwareOpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx, + "GetWorkBufferSizeForMultiStreamExEx"}, + }; + RegisterHandlers(functions); +} + +IHardwareOpusDecoderManager::~IHardwareOpusDecoderManager() = default; + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/hardware_opus_decoder_manager.h b/src/core/hle/service/audio/hardware_opus_decoder_manager.h new file mode 100644 index 000000000..29b1f16f5 --- /dev/null +++ b/src/core/hle/service/audio/hardware_opus_decoder_manager.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "audio_core/opus/decoder_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::Audio { + +class IHardwareOpusDecoderManager final : public ServiceFramework { +public: + explicit IHardwareOpusDecoderManager(Core::System& system_); + ~IHardwareOpusDecoderManager() override; + +private: + void OpenHardwareOpusDecoder(HLERequestContext& ctx); + void GetWorkBufferSize(HLERequestContext& ctx); + void OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx); + void GetWorkBufferSizeForMultiStream(HLERequestContext& ctx); + void OpenHardwareOpusDecoderEx(HLERequestContext& ctx); + void GetWorkBufferSizeEx(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 diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp deleted file mode 100644 index 91f33aabd..000000000 --- a/src/core/hle/service/audio/hwopus.cpp +++ /dev/null @@ -1,502 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include - -#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 { -using namespace AudioCore::OpusDecoder; - -class IHardwareOpusDecoder final : public ServiceFramework { -public: - explicit IHardwareOpusDecoder(Core::System& system_, HardwareOpus& hardware_opus) - : ServiceFramework{system_, "IHardwareOpusDecoder"}, - impl{std::make_unique(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); - } - - Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, - u64 transfer_memory_size) { - return impl->Initialize(params, transfer_memory, transfer_memory_size); - } - - Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, - u64 transfer_memory_size) { - return impl->Initialize(params, transfer_memory, transfer_memory_size); - } - -private: - void DecodeInterleavedOld(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->DecodeInterleaved(&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); - } - - 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); - } - - 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 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); - - ctx.WriteBuffer(output_data); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(result); - rb.Push(size); - rb.Push(sample_count); - rb.Push(time_taken); - } - - void DecodeInterleavedForMultiStreamWithPerfOld(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->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); - } - - void DecodeInterleavedWithPerfAndResetOld(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto reset{rp.Pop()}; - - 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 DecodeInterleavedForMultiStreamWithPerfAndResetOld(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto reset{rp.Pop()}; - - 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); - } - - void DecodeInterleaved(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto reset{rp.Pop()}; - - 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) { - IPC::RequestParser rp{ctx}; - - auto reset{rp.Pop()}; - - 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); - } - - std::unique_ptr impl; - Common::ScratchBuffer output_data; -}; - -void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto params = rp.PopRaw(); - auto transfer_memory_size{rp.Pop()}; - auto transfer_memory_handle{ctx.GetCopyHandle(0)}; - auto transfer_memory{ctx.GetObjectFromHandle(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); - - auto decoder{std::make_shared(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); -} - -void HwOpus::GetWorkBufferSize(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw(); - - u64 size{}; - auto result = impl.GetWorkBufferSize(params, size); - - LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} -- returned size 0x{:X}", - params.sample_rate, params.channel_count, size); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result); - rb.Push(size); -} - -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()}; - auto transfer_memory_handle{ctx.GetCopyHandle(0)}; - auto transfer_memory{ctx.GetObjectFromHandle(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(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::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::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto params = rp.PopRaw(); - auto transfer_memory_size{rp.Pop()}; - auto transfer_memory_handle{ctx.GetCopyHandle(0)}; - auto transfer_memory{ctx.GetObjectFromHandle(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); - - auto decoder{std::make_shared(system, impl.GetHardwareOpus())}; - - auto result = - decoder->Initialize(params, transfer_memory.GetPointerUnsafe(), transfer_memory_size); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(result); - rb.PushIpcInterface(decoder); -} - -void HwOpus::GetWorkBufferSizeEx(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw(); - - 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::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto input{ctx.ReadBuffer()}; - OpusMultiStreamParametersEx params; - std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx)); - - auto transfer_memory_size{rp.Pop()}; - auto transfer_memory_handle{ctx.GetCopyHandle(0)}; - auto transfer_memory{ctx.GetObjectFromHandle(transfer_memory_handle)}; - - 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); - - auto decoder{std::make_shared(system, impl.GetHardwareOpus())}; - - auto result = - decoder->Initialize(params, transfer_memory.GetPointerUnsafe(), transfer_memory_size); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(result); - rb.PushIpcInterface(decoder); -} - -void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto input{ctx.ReadBuffer()}; - OpusMultiStreamParametersEx params; - std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx)); - - 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(); - - 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"}, system{system_}, impl{system} { - static const FunctionInfo functions[] = { - {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"}, - {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"}, - {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, &HwOpus::GetWorkBufferSizeForMultiStreamExEx, "GetWorkBufferSizeForMultiStreamExEx"}, - }; - RegisterHandlers(functions); -} - -HwOpus::~HwOpus() = default; - -} // namespace Service::Audio diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h deleted file mode 100644 index d3960065e..000000000 --- a/src/core/hle/service/audio/hwopus.h +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "audio_core/opus/decoder_manager.h" -#include "core/hle/service/service.h" - -namespace Core { -class System; -} - -namespace Service::Audio { - -class HwOpus final : public ServiceFramework { -public: - explicit HwOpus(Core::System& system_); - ~HwOpus() override; - -private: - void OpenHardwareOpusDecoder(HLERequestContext& ctx); - void GetWorkBufferSize(HLERequestContext& ctx); - void OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx); - void GetWorkBufferSizeForMultiStream(HLERequestContext& ctx); - void OpenHardwareOpusDecoderEx(HLERequestContext& ctx); - void GetWorkBufferSizeEx(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 -- cgit v1.2.3