summaryrefslogtreecommitdiffstats
path: root/src/core/hle
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/result.h2
-rw-r--r--src/core/hle/service/audio/errors.h12
-rw-r--r--src/core/hle/service/audio/hwopus.cpp722
-rw-r--r--src/core/hle/service/audio/hwopus.h25
4 files changed, 427 insertions, 334 deletions
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(&params, 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(&params, 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(&param, 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(&params, 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(&params, 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(&params, 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(&params, 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