aboutsummaryrefslogtreecommitdiff
path: root/src/audio_core
diff options
context:
space:
mode:
authorlizzie <lizzie@eden-emu.dev>2026-09-12 09:59:33 +0200
committercrueter <crueter@eden-emu.dev>2026-09-12 09:59:33 +0200
commitff8368c6066faf3962e2f77af7da4954e2ff6a43 (patch)
tree34addf136793c9f69495c261dd4e0c64ef444ba8 /src/audio_core
parent8a22f1845b8b7ec19fbb518b696513e36d42b317 (diff)
[audio] Replace libopus with FFmpeg (#4169)
FFmpeg offers two opus decoders: - libopus, this one is fed the same data (that affects output) as the previous libopus decoder had as well - native opus decoder: this only takes Float32 samples, we then need to invoke swrescale to translate between s16 (what switch sends) and float32 (what FFmpeg expects), the process is computationally more expensive, but trades off neatly as the native opus decoder is very optimised It also relegates the handling to FFmpeg, which simplifies existing code (more concretely, by using the "fill/drain" scheme for packets/frames) Closes #2897 Signed-off-by: lizzie <lizzie@eden-emu.dev> Co-authored-by: crueter <crueter@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4169 Reviewed-by: CamilleLaVey <camillelavey99@gmail.com> Reviewed-by: Maufeat <sahyno1996@gmail.com>
Diffstat (limited to 'src/audio_core')
-rw-r--r--src/audio_core/CMakeLists.txt14
-rw-r--r--src/audio_core/adsp/apps/opus/opus_decode_object.cpp107
-rw-r--r--src/audio_core/adsp/apps/opus/opus_decode_object.h38
-rw-r--r--src/audio_core/adsp/apps/opus/opus_decoder.cpp569
-rw-r--r--src/audio_core/adsp/apps/opus/opus_decoder.h27
-rw-r--r--src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp113
-rw-r--r--src/audio_core/adsp/apps/opus/opus_multistream_decode_object.h39
-rw-r--r--src/audio_core/adsp/apps/opus/opus_types.h32
-rw-r--r--src/audio_core/adsp/apps/opus/shared_memory.h4
-rw-r--r--src/audio_core/opus/hardware_opus.cpp71
-rw-r--r--src/audio_core/opus/hardware_opus.h2
11 files changed, 421 insertions, 595 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index b244d657a9..71a46a7b1e 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -15,10 +15,6 @@ add_library(audio_core STATIC
adsp/apps/audio_renderer/command_list_processor.h
adsp/apps/opus/opus_decoder.cpp
adsp/apps/opus/opus_decoder.h
- adsp/apps/opus/opus_decode_object.cpp
- adsp/apps/opus/opus_decode_object.h
- adsp/apps/opus/opus_multistream_decode_object.cpp
- adsp/apps/opus/opus_multistream_decode_object.h
adsp/apps/opus/shared_memory.h
audio_core.cpp
audio_core.h
@@ -226,8 +222,14 @@ else()
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>)
endif()
-target_include_directories(audio_core PRIVATE ${OPUS_INCLUDE_DIRS})
-target_link_libraries(audio_core PUBLIC common core Opus::opus)
+if (YUZU_USE_EXTERNAL_FFMPEG)
+ add_dependencies(audio_core ffmpeg-build)
+endif()
+target_include_directories(audio_core PUBLIC ${FFmpeg_INCLUDE_DIR})
+target_link_libraries(audio_core PRIVATE ${FFmpeg_LIBRARIES})
+target_link_options(audio_core PRIVATE ${FFmpeg_LDFLAGS})
+
+target_link_libraries(audio_core PUBLIC common core)
if (ENABLE_CUBEB)
target_sources(audio_core PRIVATE
diff --git a/src/audio_core/adsp/apps/opus/opus_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_decode_object.cpp
deleted file mode 100644
index e2b9eb566c..0000000000
--- a/src/audio_core/adsp/apps/opus/opus_decode_object.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "audio_core/adsp/apps/opus/opus_decode_object.h"
-#include "common/assert.h"
-
-namespace AudioCore::ADSP::OpusDecoder {
-namespace {
-bool IsValidChannelCount(u32 channel_count) {
- return channel_count == 1 || channel_count == 2;
-}
-} // namespace
-
-u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) {
- if (!IsValidChannelCount(channel_count)) {
- return 0;
- }
- return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count);
-}
-
-OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) {
- auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer);
- auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2);
-
- if (new_decoder->magic == DecodeObjectMagic) {
- if (!new_decoder->initialized ||
- (new_decoder->initialized && new_decoder->self == comparison)) {
- new_decoder->state_valid = true;
- }
- } else {
- new_decoder->initialized = false;
- new_decoder->state_valid = true;
- }
- return *new_decoder;
-}
-
-s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) {
- if (!state_valid) {
- return OPUS_INVALID_STATE;
- }
-
- if (initialized) {
- return OPUS_OK;
- }
-
- // Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include
- // it in this class. Nintendo does not allocate memory, which is why we have a workbuffer
- // provided.
- // We could use _create and have libopus allocate it for us, but then we have to separately
- // track which decoder is being used between this and multistream in order to call the correct
- // destroy from the host side.
- // This is a bit cringe, but is safe as these objects are only ever initialized inside the given
- // workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow.
- decoder = (LibOpusDecoder*)(this + 1);
- s32 ret = opus_decoder_init(decoder, sample_rate, channel_count);
- if (ret == OPUS_OK) {
- magic = DecodeObjectMagic;
- initialized = true;
- state_valid = true;
- self = this;
- final_range = 0;
- }
- return ret;
-}
-
-s32 OpusDecodeObject::Shutdown() {
- if (!state_valid) {
- return OPUS_INVALID_STATE;
- }
-
- if (initialized) {
- magic = 0x0;
- initialized = false;
- state_valid = false;
- self = nullptr;
- final_range = 0;
- decoder = nullptr;
- }
- return OPUS_OK;
-}
-
-s32 OpusDecodeObject::ResetDecoder() {
- return opus_decoder_ctl(decoder, OPUS_RESET_STATE);
-}
-
-s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size,
- u64 input_data, u64 input_data_size) {
- ASSERT(initialized);
- out_sample_count = 0;
-
- if (!state_valid) {
- return OPUS_INVALID_STATE;
- }
-
- auto ret_code_or_samples = opus_decode(
- decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
- reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
-
- if (ret_code_or_samples < OPUS_OK) {
- return ret_code_or_samples;
- }
-
- out_sample_count = ret_code_or_samples;
- return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
-}
-
-} // namespace AudioCore::ADSP::OpusDecoder
diff --git a/src/audio_core/adsp/apps/opus/opus_decode_object.h b/src/audio_core/adsp/apps/opus/opus_decode_object.h
deleted file mode 100644
index 6425f987c6..0000000000
--- a/src/audio_core/adsp/apps/opus/opus_decode_object.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <opus.h>
-
-#include "common/common_types.h"
-
-namespace AudioCore::ADSP::OpusDecoder {
-using LibOpusDecoder = ::OpusDecoder;
-static constexpr u32 DecodeObjectMagic = 0xDEADBEEF;
-
-class OpusDecodeObject {
-public:
- static u32 GetWorkBufferSize(u32 channel_count);
- static OpusDecodeObject& Initialize(u64 buffer, u64 buffer2);
-
- s32 InitializeDecoder(u32 sample_rate, u32 channel_count);
- s32 Shutdown();
- s32 ResetDecoder();
- s32 Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data,
- u64 input_data_size);
- u32 GetFinalRange() const noexcept {
- return final_range;
- }
-
-private:
- u32 magic;
- bool initialized;
- bool state_valid;
- OpusDecodeObject* self;
- u32 final_range;
- LibOpusDecoder* decoder;
-};
-static_assert(std::is_trivially_constructible_v<OpusDecodeObject>);
-
-} // namespace AudioCore::ADSP::OpusDecoder
diff --git a/src/audio_core/adsp/apps/opus/opus_decoder.cpp b/src/audio_core/adsp/apps/opus/opus_decoder.cpp
index 8a009c04c7..0be0f774b1 100644
--- a/src/audio_core/adsp/apps/opus/opus_decoder.cpp
+++ b/src/audio_core/adsp/apps/opus/opus_decoder.cpp
@@ -5,265 +5,396 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
-#include <chrono>
-#include "audio_core/adsp/apps/opus/opus_decode_object.h"
-#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h"
+extern "C" {
+#include <libswresample/swresample.h>
+#include <libavcodec/avcodec.h>
+#include <libavcodec/codec.h>
+#include <libavcodec/packet.h>
+#include <libavutil/channel_layout.h>
+#include <libavutil/frame.h>
+#include <libavutil/opt.h>
+#include <libavutil/samplefmt.h>
+}
+
#include "audio_core/adsp/apps/opus/shared_memory.h"
#include "audio_core/audio_core.h"
-#include "audio_core/common/common.h"
#include "common/logging.h"
#include "common/thread.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/hle/service/audio/errors.h"
namespace AudioCore::ADSP::OpusDecoder {
namespace {
-constexpr size_t OpusStreamCountMax = 255;
+constexpr u32 OPUS_STREAM_COUNT_MAX = 255;
+// https://git.ffmpeg.org/gitweb/ffmpeg.git/blob_plain/HEAD:/libavcodec/libopusdec.c
+constexpr u32 OPUS_HEAD_SIZE = 19;
+constexpr u32 OPUS_MAX_CHANNELS = 2;
bool IsValidChannelCount(u32 channel_count) {
- return channel_count == 1 || channel_count == 2;
+ return channel_count >= 1 || channel_count <= OPUS_MAX_CHANNELS;
}
-bool IsValidMultiStreamChannelCount(u32 channel_count) {
- return channel_count <= OpusStreamCountMax;
+bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
+ return total_stream_count > 0 && total_stream_count <= OPUS_STREAM_COUNT_MAX
+ && s32(stereo_stream_count) >= 0 && stereo_stream_count <= total_stream_count;
}
-bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 stereo_stream_count) {
- return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 &&
- stereo_stream_count >= 0 && stereo_stream_count <= total_stream_count;
-}
-} // namespace
-
-OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} {
- init_thread = std::jthread([this](std::stop_token stop_token) { Init(stop_token); });
-}
-
-OpusDecoder::~OpusDecoder() {
- if (!running) {
- init_thread.request_stop();
- return;
+class OpusGenericDecodeObject {
+public:
+ static u32 GetWorkBufferSizeMultistream(u32 total_stream_count, u32 stereo_stream_count) {
+ if (IsValidStreamCounts(total_stream_count, stereo_stream_count))
+ return 48 + 2556 * (total_stream_count * stereo_stream_count);
+ return 0;
}
- // Shutdown the thread
- Send(Direction::DSP, Message::Shutdown);
- auto msg = Receive(Direction::Host);
- ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}",
- Message::ShutdownOK, msg);
- main_thread.request_stop();
- main_thread.join();
- running = false;
-}
-
-void OpusDecoder::Send(Direction dir, u32 message) {
- mailbox.Send(dir, std::move(message));
-}
-
-u32 OpusDecoder::Receive(Direction dir, std::stop_token stop_token) {
- return mailbox.Receive(dir, stop_token);
-}
-
-void OpusDecoder::Init(std::stop_token stop_token) {
- Common::SetCurrentThreadName("DSP_OpusDecoder_Init");
-
- if (Receive(Direction::DSP, stop_token) != Message::Start) {
- LOG_ERROR(Service_Audio,
- "DSP OpusDecoder failed to receive Start message. Opus initialization failed.");
- return;
+ static u32 GetWorkBufferSize(u32 channel_count) {
+ if (channel_count == 1 || channel_count == 2)
+ return 48 + 16 * channel_count;
+ return 0;
}
- main_thread = std::jthread([this](std::stop_token st) { Main(st); });
- running = true;
- Send(Direction::Host, Message::StartOK);
-}
-
-void OpusDecoder::Main(std::stop_token stop_token) {
- Common::SetCurrentThreadName("DSP_OpusDecoder_Main");
-
- while (!stop_token.stop_requested()) {
- auto msg = Receive(Direction::DSP, stop_token);
- switch (msg) {
- case Shutdown:
- Send(Direction::Host, Message::ShutdownOK);
- return;
-
- case GetWorkBufferSize: {
- auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
-
- ASSERT(IsValidChannelCount(channel_count));
-
- shared_memory->dsp_return_data[0] = OpusDecodeObject::GetWorkBufferSize(channel_count);
- Send(Direction::Host, Message::GetWorkBufferSizeOK);
- } break;
-
- case InitializeDecodeObject: {
- auto buffer = shared_memory->host_send_data[0];
- auto buffer_size = shared_memory->host_send_data[1];
- auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
- auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
- ASSERT(sample_rate >= 0);
- ASSERT(IsValidChannelCount(channel_count));
- ASSERT(buffer_size >= OpusDecodeObject::GetWorkBufferSize(channel_count));
-
- auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
- shared_memory->dsp_return_data[0] =
- decoder_object.InitializeDecoder(sample_rate, channel_count);
-
- Send(Direction::Host, Message::InitializeDecodeObjectOK);
- } break;
+ /// idempotency of initialize is guaranteed
+ Result InitializeDecoder(u32 sample_rate, u32 total_stream_count, u32 channel_count, u32 stereo_stream_count, u8 const* mappings) {
+ // prefer libopus, ffmpeg docs say to use libopus **if** available
+ // However, native opus can also work with swrescale:
+ // it uses planarfloat, we can resample to s16
+ AVCodec const* codec = avcodec_find_decoder_by_name("libopus");
+ bool is_libopus = codec != nullptr;
+ if (!codec) {
+ LOG_WARNING(Audio_DSP, "using ffmpeg native opus decoder");
+ codec = avcodec_find_decoder(AV_CODEC_ID_OPUS);
+ }
+ if (codec) {
+ if ((avc = avc ? avc : avcodec_alloc_context3(codec))) {
+ if (is_libopus) {
+ const std::array<u8, 2> mapping_arr{0, 1};
+ mappings = mappings ? mappings : mapping_arr.data();
+
+ // freed by avcodec_context_free()
+ u8 *edata = reinterpret_cast<u8*>(av_mallocz(OPUS_HEAD_SIZE + 2 * OPUS_MAX_CHANNELS + AV_INPUT_BUFFER_PADDING_SIZE));
+ ASSERT(edata);
+ edata[9] = u8(channel_count); //channels
+ edata[10] = u8(0); //opus->pre_skip
+ edata[16] = u8(0); //gain_db
+ edata[18] = u8(0); //channel_map
+ edata[OPUS_HEAD_SIZE + 0] = u8(total_stream_count);
+ edata[OPUS_HEAD_SIZE + 1] = u8(stereo_stream_count);
+ if (channel_count >= 1) edata[OPUS_HEAD_SIZE + 2] = mappings[0];
+ if (channel_count >= 2) edata[OPUS_HEAD_SIZE + 3] = mappings[1];
+ avc->extradata = edata;
+ avc->extradata_size = OPUS_HEAD_SIZE + 2 * channel_count;
+ }
- case ShutdownDecodeObject: {
- auto buffer = shared_memory->host_send_data[0];
- [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
- auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
- shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
+ // FFmpeg hardcodes sample rate
+ avc->sample_rate = sample_rate;
+ avc->request_sample_fmt = AV_SAMPLE_FMT_S16;
+ av_channel_layout_default(&avc->ch_layout, channel_count);
+ if (avcodec_open2(avc, codec, nullptr) >= 0) {
+ avpkt = av_packet_alloc();
+ frame = av_frame_alloc();
+ return ResultSuccess;
+ } else {
+ avcodec_free_context(&avc);
+ }
+ }
+ }
+ return Service::Audio::ResultLibOpusInternalError;
+ }
- Send(Direction::Host, Message::ShutdownDecodeObjectOK);
- } break;
+ Result Shutdown() {
+ avcodec_free_context(&avc);
+ av_frame_free(&frame);
+ av_packet_free(&avpkt);
+ return ResultSuccess;
+ }
- case DecodeInterleaved: {
- auto start_time = system.CoreTiming().GetGlobalTimeUs();
+ Result ResetDecoder() {
+ if (avc) {
+ if (avcodec_is_open(avc)) avcodec_flush_buffers(avc);
+ return ResultSuccess;
+ }
+ return Service::Audio::ResultLibOpusInvalidState;
+ }
- auto buffer = shared_memory->host_send_data[0];
- auto input_data = shared_memory->host_send_data[1];
- auto input_data_size = shared_memory->host_send_data[2];
- auto output_data = shared_memory->host_send_data[3];
- auto output_data_size = shared_memory->host_send_data[4];
- auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
- auto reset_requested = shared_memory->host_send_data[6];
+ Result Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data, u64 input_data_size) {
+ out_sample_count = 0;
+ if (avc) {
+ int rem_output_bytes = int(output_data_size);
+ while (rem_output_bytes > 0) {
+ int r = avcodec_receive_frame(avc, frame);
+ if (r == AVERROR(EAGAIN)) {
+ av_packet_unref(avpkt);
+ av_new_packet(avpkt, int(input_data_size));
+ std::memcpy(avpkt->data, reinterpret_cast<const u8*>(input_data), input_data_size);
+ r = avcodec_send_packet(avc, avpkt);
+ ASSERT(r >= 0);
+ } else if (r == AVERROR_EOF) {
+ break;
+ } else if (r >= 0) {
+ auto const bsize = av_samples_get_buffer_size(nullptr, frame->ch_layout.nb_channels, frame->nb_samples, AV_SAMPLE_FMT_S16, 1);
+ if (frame->format == AV_SAMPLE_FMT_S16) {
+ std::memcpy(reinterpret_cast<s16*>(output_data) + (int(output_data_size) - rem_output_bytes), frame->data[0], size_t(bsize));
+ } else {
+ SwrContext *swr = nullptr;
+ if (swr_alloc_set_opts2(
+ &swr,
+ &avc->ch_layout,
+ AV_SAMPLE_FMT_S16,
+ 48000,
+ &avc->ch_layout,
+ (enum AVSampleFormat)frame->format,
+ 48000,
+ 0,
+ nullptr
+ ) >= 0) {
+ if (swr_init(swr) >= 0) {
+ AVFrame *s16_frame = av_frame_alloc();
+ s16_frame->format = AV_SAMPLE_FMT_S16;
+ s16_frame->sample_rate = frame->sample_rate;
+ av_channel_layout_copy(&s16_frame->ch_layout, &frame->ch_layout);
+ s16_frame->nb_samples = frame->nb_samples;
+ av_frame_get_buffer(s16_frame, 0);
+ swr_convert(swr, s16_frame->data, s16_frame->nb_samples, (const uint8_t **)frame->data, frame->nb_samples);
+ std::memcpy(reinterpret_cast<s16*>(output_data) + (int(output_data_size) - rem_output_bytes), s16_frame->data[0], size_t(bsize));
+ swr_free(&swr);
+ }
+ }
+ }
+ out_sample_count += frame->nb_samples;
+ rem_output_bytes -= bsize;
+ } else {
+ LOG_ERROR(Audio_DSP, "{}", r);
+ break;
+ }
+ }
+ ASSERT(rem_output_bytes == 0 && "remaining bytes!");
+ return ResultSuccess;
+ }
+ return Service::Audio::ResultLibOpusInvalidState;
+ }
- u32 decoded_samples{0};
+ AVCodecContext* avc = nullptr;
+ AVPacket* avpkt = nullptr;
+ AVFrame* frame = nullptr;
+};
+} // namespace
- auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
- s32 error_code{OPUS_OK};
- if (reset_requested) {
- error_code = decoder_object.ResetDecoder();
+OpusDecoder::OpusDecoder(Core::System& system) {
+ dsp_thread = std::jthread([this, &system](std::stop_token stop_token) {
+ Common::SetCurrentThreadName("DSP_OpusDecoder");
+ if (Receive(Direction::DSP, stop_token) != Message::Start) {
+ LOG_ERROR(Service_Audio, "DSP OpusDecoder failed to receive Start message. Opus initialization failed.");
+ return;
+ }
+ Send(Direction::Host, Message::StartOK);
+
+ // Main OpusDecoder thread, responsible for processing the incoming Opus packets.
+ ::Common::unordered_map<u64, OpusGenericDecodeObject> decode_objects;
+ while (!stop_token.stop_requested()) {
+ auto msg = Receive(Direction::DSP, stop_token);
+ switch (msg) {
+ case Shutdown:
+ Send(Direction::Host, Message::ShutdownOK);
+ return;
+ case GetWorkBufferSize: {
+ auto channel_count = s32(shared_memory->host_send_data[0]);
+
+ ASSERT(IsValidChannelCount(channel_count));
+
+ shared_memory->dsp_return_data[0] = OpusGenericDecodeObject::GetWorkBufferSize(channel_count);
+ Send(Direction::Host, Message::GetWorkBufferSizeOK);
+ break;
}
-
- if (error_code == OPUS_OK) {
- error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
- input_data, input_data_size);
+ case InitializeDecodeObject: {
+ auto buffer = shared_memory->host_send_data[0];
+ auto buffer_size = shared_memory->host_send_data[1];
+ auto sample_rate = s32(shared_memory->host_send_data[2]);
+ auto channel_count = s32(shared_memory->host_send_data[3]);
+
+ ASSERT(sample_rate >= 0);
+ ASSERT(IsValidChannelCount(channel_count));
+ ASSERT(buffer_size >= OpusGenericDecodeObject::GetWorkBufferSize(channel_count));
+
+ if (auto const it = decode_objects.find(buffer); it != decode_objects.end()) {
+ it->second.Shutdown();
+ shared_memory->dsp_return_data[0] = it->second.InitializeDecoder(sample_rate, 1, channel_count, channel_count == 2 ? 1 : 0, nullptr).raw;
+ } else {
+ OpusGenericDecodeObject obj{};
+ shared_memory->dsp_return_data[0] = obj.InitializeDecoder(sample_rate, 1, channel_count, channel_count == 2 ? 1 : 0, nullptr).raw;
+ decode_objects.insert_or_assign(buffer, obj);
+ }
+ Send(Direction::Host, Message::InitializeDecodeObjectOK);
+ break;
}
-
- if (error_code == OPUS_OK) {
- if (final_range && decoder_object.GetFinalRange() != final_range) {
- error_code = OPUS_INVALID_PACKET;
+ case ShutdownDecodeObject: {
+ auto buffer = shared_memory->host_send_data[0];
+ //[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
+ if (auto const it = decode_objects.find(buffer); it != decode_objects.end()) {
+ shared_memory->dsp_return_data[0] = it->second.Shutdown().raw;
+ } else {
+ LOG_ERROR(Audio_DSP, "operating unregistered buffer {}", buffer);
+ shared_memory->dsp_return_data[0] = Service::Audio::ResultLibOpusInvalidState.raw;
}
+ Send(Direction::Host, Message::ShutdownDecodeObjectOK);
+ break;
}
-
- auto end_time = system.CoreTiming().GetGlobalTimeUs();
- shared_memory->dsp_return_data[0] = error_code;
- shared_memory->dsp_return_data[1] = decoded_samples;
- shared_memory->dsp_return_data[2] = (end_time - start_time).count();
-
- Send(Direction::Host, Message::DecodeInterleavedOK);
- } break;
-
- case MapMemory: {
- [[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
- [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
- Send(Direction::Host, Message::MapMemoryOK);
- } break;
-
- case UnmapMemory: {
- [[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
- [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
- Send(Direction::Host, Message::UnmapMemoryOK);
- } break;
-
- case GetWorkBufferSizeForMultiStream: {
- auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]);
- auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
-
- ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
-
- shared_memory->dsp_return_data[0] = OpusMultiStreamDecodeObject::GetWorkBufferSize(
- total_stream_count, stereo_stream_count);
- Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
- } break;
-
- case InitializeMultiStreamDecodeObject: {
- auto buffer = shared_memory->host_send_data[0];
- auto buffer_size = shared_memory->host_send_data[1];
- auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
- auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
- auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[4]);
- auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[5]);
- // Nintendo seem to have a bug here, they try to use &host_send_data[6] for the channel
- // mappings, but [6] is never set, and there is not enough room in the argument data for
- // more than 40 channels, when 255 are possible.
- // It also means the mapping values are undefined, though likely always 0,
- // and the mappings given by the game are ignored. The mappings are copied to this
- // dedicated buffer host side, so let's do as intended.
- auto mappings = shared_memory->channel_mapping.data();
-
- ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
- ASSERT(sample_rate >= 0);
- ASSERT(buffer_size >= OpusMultiStreamDecodeObject::GetWorkBufferSize(
- total_stream_count, stereo_stream_count));
-
- auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
- shared_memory->dsp_return_data[0] = decoder_object.InitializeDecoder(
- sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings);
-
- Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
- } break;
-
- case ShutdownMultiStreamDecodeObject: {
- auto buffer = shared_memory->host_send_data[0];
- [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
-
- auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
- shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
-
- Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
- } break;
-
- case DecodeInterleavedForMultiStream: {
- auto start_time = system.CoreTiming().GetGlobalTimeUs();
-
- auto buffer = shared_memory->host_send_data[0];
- auto input_data = shared_memory->host_send_data[1];
- auto input_data_size = shared_memory->host_send_data[2];
- auto output_data = shared_memory->host_send_data[3];
- auto output_data_size = shared_memory->host_send_data[4];
- auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
- auto reset_requested = shared_memory->host_send_data[6];
-
- u32 decoded_samples{0};
-
- auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
- s32 error_code{OPUS_OK};
- if (reset_requested) {
- error_code = decoder_object.ResetDecoder();
+ case DecodeInterleaved: {
+ auto start_time = system.CoreTiming().GetGlobalTimeUs();
+
+ auto buffer = shared_memory->host_send_data[0];
+ auto input_data = shared_memory->host_send_data[1];
+ auto input_data_size = shared_memory->host_send_data[2];
+ auto output_data = shared_memory->host_send_data[3];
+ auto output_data_size = shared_memory->host_send_data[4];
+ //auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
+ auto reset_requested = shared_memory->host_send_data[6];
+
+ u32 decoded_samples{0};
+
+ if (auto const it = decode_objects.find(buffer); it != decode_objects.end()) {
+ auto res = ResultSuccess;
+ if (reset_requested)
+ res = it->second.ResetDecoder();
+ if (res == ResultSuccess)
+ res = it->second.Decode(decoded_samples, output_data, output_data_size, input_data, input_data_size);
+
+ auto end_time = system.CoreTiming().GetGlobalTimeUs();
+ shared_memory->dsp_return_data[0] = res.raw;
+ shared_memory->dsp_return_data[1] = decoded_samples;
+ shared_memory->dsp_return_data[2] = (end_time - start_time).count();
+ } else {
+ LOG_ERROR(Audio_DSP, "operating unregistered buffer {}", buffer);
+ shared_memory->dsp_return_data[0] = Service::Audio::ResultLibOpusInvalidState.raw;
+ }
+ Send(Direction::Host, Message::DecodeInterleavedOK);
+ break;
}
-
- if (error_code == OPUS_OK) {
- error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
- input_data, input_data_size);
+ case MapMemory: {
+ [[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
+ [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
+ Send(Direction::Host, Message::MapMemoryOK);
+ break;
+ }
+ case UnmapMemory: {
+ [[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
+ [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
+ Send(Direction::Host, Message::UnmapMemoryOK);
+ break;
}
+ case GetWorkBufferSizeForMultiStream: {
+ auto total_stream_count = s32(shared_memory->host_send_data[0]);
+ auto stereo_stream_count = s32(shared_memory->host_send_data[1]);
- if (error_code == OPUS_OK) {
- if (final_range && decoder_object.GetFinalRange() != final_range) {
- error_code = OPUS_INVALID_PACKET;
+ ASSERT(IsValidStreamCounts(total_stream_count, stereo_stream_count));
+
+ shared_memory->dsp_return_data[0] = OpusGenericDecodeObject::GetWorkBufferSizeMultistream(total_stream_count, stereo_stream_count);
+ Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
+ break;
+ }
+ case InitializeMultiStreamDecodeObject: {
+ auto buffer = shared_memory->host_send_data[0];
+ auto buffer_size = shared_memory->host_send_data[1];
+ auto sample_rate = s32(shared_memory->host_send_data[2]);
+ auto channel_count = s32(shared_memory->host_send_data[3]);
+ auto total_stream_count = s32(shared_memory->host_send_data[4]);
+ auto stereo_stream_count = s32(shared_memory->host_send_data[5]);
+ // Nintendo seem to have a bug here, they try to use &host_send_data[6] for the channel
+ // mappings, but [6] is never set, and there is not enough room in the argument data for
+ // more than 40 channels, when 255 are possible.
+ // It also means the mapping values are undefined, though likely always 0,
+ // and the mappings given by the game are ignored. The mappings are copied to this
+ // dedicated buffer host side, so let's do as intended.
+ auto mappings = shared_memory->channel_mapping.data();
+
+ ASSERT(IsValidStreamCounts(total_stream_count, stereo_stream_count));
+ ASSERT(sample_rate >= 0);
+ ASSERT(buffer_size >= OpusGenericDecodeObject::GetWorkBufferSizeMultistream(total_stream_count, stereo_stream_count));
+ if (auto const it = decode_objects.find(buffer); it != decode_objects.end()) {
+ it->second.Shutdown();
+ shared_memory->dsp_return_data[0] = it->second.InitializeDecoder(sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings).raw;
+ } else {
+ OpusGenericDecodeObject obj{};
+ shared_memory->dsp_return_data[0] = obj.InitializeDecoder(sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings).raw;
+ decode_objects.insert_or_assign(buffer, obj);
}
+ Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
+ break;
}
+ case ShutdownMultiStreamDecodeObject: {
+ auto buffer = shared_memory->host_send_data[0];
+ //[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
+ if (auto const it = decode_objects.find(buffer); it != decode_objects.end()) {
+ shared_memory->dsp_return_data[0] = it->second.Shutdown().raw;
+ } else {
+ LOG_ERROR(Audio_DSP, "operating unregistered buffer {}", buffer);
+ shared_memory->dsp_return_data[0] = Service::Audio::ResultLibOpusInvalidState.raw;
+ }
+ Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
+ break;
+ }
+ case DecodeInterleavedForMultiStream: {
+ auto start_time = system.CoreTiming().GetGlobalTimeUs();
+
+ auto buffer = shared_memory->host_send_data[0];
+ auto input_data = shared_memory->host_send_data[1];
+ auto input_data_size = shared_memory->host_send_data[2];
+ auto output_data = shared_memory->host_send_data[3];
+ auto output_data_size = shared_memory->host_send_data[4];
+ //auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
+ auto reset_requested = shared_memory->host_send_data[6];
+
+ u32 decoded_samples{0};
+
+ if (auto const it = decode_objects.find(buffer); it != decode_objects.end()) {
+ auto res = ResultSuccess;
+ if (reset_requested)
+ res = it->second.ResetDecoder();
+ if (res == ResultSuccess)
+ res = it->second.Decode(decoded_samples, output_data, output_data_size, input_data, input_data_size);
+
+ auto end_time = system.CoreTiming().GetGlobalTimeUs();
+ shared_memory->dsp_return_data[0] = res.raw;
+ shared_memory->dsp_return_data[1] = decoded_samples;
+ shared_memory->dsp_return_data[2] = (end_time - start_time).count();
+ } else {
+ LOG_ERROR(Audio_DSP, "operating unregistered buffer {}", buffer);
+ shared_memory->dsp_return_data[0] = Service::Audio::ResultLibOpusInvalidState.raw;
+ }
+ Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK);
+ break;
+ }
+ default:
+ LOG_ERROR(Audio_DSP, "Invalid OpusDecoder command {}", msg);
+ continue;
+ }
+ }
+ for (auto e : decode_objects)
+ e.second.Shutdown();
+ });
+}
- auto end_time = system.CoreTiming().GetGlobalTimeUs();
- shared_memory->dsp_return_data[0] = error_code;
- shared_memory->dsp_return_data[1] = decoded_samples;
- shared_memory->dsp_return_data[2] = (end_time - start_time).count();
+OpusDecoder::~OpusDecoder() {
+ if (dsp_thread.joinable()) {
+ // Shutdown the thread
+ auto const stop_token = dsp_thread.get_stop_token();
+ Send(Direction::DSP, Message::Shutdown);
+ auto msg = Receive(Direction::Host, stop_token);
+ ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}", Message::ShutdownOK, msg);
+ dsp_thread.request_stop();
+ dsp_thread.join();
+ }
+}
- Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK);
- } break;
+void OpusDecoder::Send(Direction dir, u32 message) {
+ mailbox.Send(dir, std::move(message));
+}
- default:
- LOG_ERROR(Service_Audio, "Invalid OpusDecoder command {}", msg);
- continue;
- }
- }
+u32 OpusDecoder::Receive(Direction dir, std::stop_token stop_token) {
+ return mailbox.Receive(dir, stop_token);
}
} // namespace AudioCore::ADSP::OpusDecoder
diff --git a/src/audio_core/adsp/apps/opus/opus_decoder.h b/src/audio_core/adsp/apps/opus/opus_decoder.h
index 5b3f22bf4c..dd87b473d1 100644
--- a/src/audio_core/adsp/apps/opus/opus_decoder.h
+++ b/src/audio_core/adsp/apps/opus/opus_decoder.h
@@ -6,12 +6,13 @@
#pragma once
-#include <memory>
#include <thread>
+#include "common/container/unordered_map.h"
#include "audio_core/adsp/apps/opus/shared_memory.h"
#include "audio_core/adsp/mailbox.h"
#include "common/common_types.h"
+#include "core/hle/result.h"
namespace Core {
class System;
@@ -48,16 +49,14 @@ enum Message : u32 {
DecodeInterleavedForMultiStreamOK = 50,
};
-/**
- * The AudioRenderer application running on the ADSP.
- */
+/// @brief The AudioRenderer application running on the ADSP.
class OpusDecoder {
public:
explicit OpusDecoder(Core::System& system);
~OpusDecoder();
bool IsRunning() const noexcept {
- return running;
+ return dsp_thread.joinable();
}
void Send(Direction dir, u32 message);
@@ -68,28 +67,12 @@ public:
}
private:
- /**
- * Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
- */
- void Init(std::stop_token stop_token);
- /**
- * Main OpusDecoder thread, responsible for processing the incoming Opus packets.
- */
- void Main(std::stop_token stop_token);
-
- /// Core system
- Core::System& system;
/// Mailbox to communicate messages with the host, drives the main thread
Mailbox mailbox;
- /// Init thread
- std::jthread init_thread{};
- /// Main thread
- std::jthread main_thread{};
- /// The current state
- bool running{};
/// Structure shared with the host, input data set by the host before sending a mailbox message,
/// and the responses are written back by the OpusDecoder.
SharedMemory* shared_memory{};
+ std::jthread dsp_thread{};
};
} // namespace AudioCore::ADSP::OpusDecoder
diff --git a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp
deleted file mode 100644
index 6fecd63309..0000000000
--- a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h"
-#include "common/assert.h"
-
-namespace AudioCore::ADSP::OpusDecoder {
-
-namespace {
-constexpr u32 OpusStreamCountMax = 255;
-
-bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
- return total_stream_count > 0 && total_stream_count <= OpusStreamCountMax &&
- static_cast<s32>(stereo_stream_count) >= 0 &&
- stereo_stream_count <= total_stream_count;
-}
-} // namespace
-
-u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count,
- u32 stereo_stream_count) {
- if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) {
- return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) +
- opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count);
- }
- return 0;
-}
-
-OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) {
- auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer);
- auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2);
-
- if (new_decoder->magic == DecodeMultiStreamObjectMagic) {
- if (!new_decoder->initialized ||
- (new_decoder->initialized && new_decoder->self == comparison)) {
- new_decoder->state_valid = true;
- }
- } else {
- new_decoder->initialized = false;
- new_decoder->state_valid = true;
- }
- return *new_decoder;
-}
-
-s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count,
- u32 channel_count, u32 stereo_stream_count,
- u8* mappings) {
- if (!state_valid) {
- return OPUS_INVALID_STATE;
- }
-
- if (initialized) {
- return OPUS_OK;
- }
-
- // See OpusDecodeObject::InitializeDecoder for an explanation of this
- decoder = (LibOpusMSDecoder*)(this + 1);
- s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count,
- stereo_stream_count, mappings);
- if (ret == OPUS_OK) {
- magic = DecodeMultiStreamObjectMagic;
- initialized = true;
- state_valid = true;
- self = this;
- final_range = 0;
- }
- return ret;
-}
-
-s32 OpusMultiStreamDecodeObject::Shutdown() {
- if (!state_valid) {
- return OPUS_INVALID_STATE;
- }
-
- if (initialized) {
- magic = 0x0;
- initialized = false;
- state_valid = false;
- self = nullptr;
- final_range = 0;
- decoder = nullptr;
- }
- return OPUS_OK;
-}
-
-s32 OpusMultiStreamDecodeObject::ResetDecoder() {
- return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE);
-}
-
-s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data,
- u64 output_data_size, u64 input_data, u64 input_data_size) {
- ASSERT(initialized);
- out_sample_count = 0;
-
- if (!state_valid) {
- return OPUS_INVALID_STATE;
- }
-
- auto ret_code_or_samples = opus_multistream_decode(
- decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
- reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
-
- if (ret_code_or_samples < OPUS_OK) {
- return ret_code_or_samples;
- }
-
- out_sample_count = ret_code_or_samples;
- return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
-}
-
-} // namespace AudioCore::ADSP::OpusDecoder
diff --git a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.h b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.h
deleted file mode 100644
index 93558ded5d..0000000000
--- a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <opus_multistream.h>
-
-#include "common/common_types.h"
-
-namespace AudioCore::ADSP::OpusDecoder {
-using LibOpusMSDecoder = ::OpusMSDecoder;
-static constexpr u32 DecodeMultiStreamObjectMagic = 0xDEADBEEF;
-
-class OpusMultiStreamDecodeObject {
-public:
- static u32 GetWorkBufferSize(u32 total_stream_count, u32 stereo_stream_count);
- static OpusMultiStreamDecodeObject& Initialize(u64 buffer, u64 buffer2);
-
- s32 InitializeDecoder(u32 sample_rate, u32 total_stream_count, u32 channel_count,
- u32 stereo_stream_count, u8* mappings);
- s32 Shutdown();
- s32 ResetDecoder();
- s32 Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data,
- u64 input_data_size);
- u32 GetFinalRange() const noexcept {
- return final_range;
- }
-
-private:
- u32 magic;
- bool initialized;
- bool state_valid;
- OpusMultiStreamDecodeObject* self;
- u32 final_range;
- LibOpusMSDecoder* decoder;
-};
-static_assert(std::is_trivially_constructible_v<OpusMultiStreamDecodeObject>);
-
-} // namespace AudioCore::ADSP::OpusDecoder
diff --git a/src/audio_core/adsp/apps/opus/opus_types.h b/src/audio_core/adsp/apps/opus/opus_types.h
new file mode 100644
index 0000000000..bacfb30338
--- /dev/null
+++ b/src/audio_core/adsp/apps/opus/opus_types.h
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace AudioCore::ADSP {
+
+static constexpr u32 DECODE_OBJECT_MAGIC = 0xDEADBEEF;
+struct LibOpusDecoder {
+ u32 magic;
+ bool initialized;
+ bool state_valid;
+ LibOpusDecoder* self;
+ u32 final_range;
+ void* decoder;
+};
+static_assert(sizeof(LibOpusDecoder) == 32);
+
+static constexpr u32 DECODE_MULTISTREAM_OBJECT_MAGIC = 0xDEADBEEF;
+struct LibOpusMultistreamDecoder {
+ u32 magic;
+ bool initialized;
+ bool state_valid;
+ LibOpusMultistreamDecoder* self;
+ u32 final_range;
+ void* decoder;
+};
+static_assert(sizeof(LibOpusMultistreamDecoder) == 32);
+
+}
diff --git a/src/audio_core/adsp/apps/opus/shared_memory.h b/src/audio_core/adsp/apps/opus/shared_memory.h
index c696731ed8..45079c6292 100644
--- a/src/audio_core/adsp/apps/opus/shared_memory.h
+++ b/src/audio_core/adsp/apps/opus/shared_memory.h
@@ -1,9 +1,11 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
-#include "common/common_funcs.h"
#include "common/common_types.h"
namespace AudioCore::ADSP::OpusDecoder {
diff --git a/src/audio_core/opus/hardware_opus.cpp b/src/audio_core/opus/hardware_opus.cpp
index 04591c10b6..c07a358168 100644
--- a/src/audio_core/opus/hardware_opus.cpp
+++ b/src/audio_core/opus/hardware_opus.cpp
@@ -10,39 +10,18 @@
#include "audio_core/audio_core.h"
#include "audio_core/opus/hardware_opus.h"
#include "core/core.h"
+#include "core/hle/result.h"
namespace AudioCore::OpusDecoder {
+
namespace {
using namespace Service::Audio;
-
-static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) {
- s32 error{static_cast<s32>(error_code)};
- ASSERT(error <= OPUS_OK);
- switch (error) {
- case OPUS_ALLOC_FAIL:
- R_THROW(ResultLibOpusAllocFail);
- case OPUS_INVALID_STATE:
- R_THROW(ResultLibOpusInvalidState);
- case OPUS_UNIMPLEMENTED:
- R_THROW(ResultLibOpusUnimplemented);
- case OPUS_INVALID_PACKET:
- R_THROW(ResultLibOpusInvalidPacket);
- case OPUS_INTERNAL_ERROR:
- R_THROW(ResultLibOpusInternalError);
- case OPUS_BUFFER_TOO_SMALL:
- R_THROW(ResultBufferTooSmall);
- case OPUS_BAD_ARG:
- R_THROW(ResultLibOpusBadArg);
- case OPUS_OK:
- R_RETURN(ResultSuccess);
- }
- UNREACHABLE();
-}
-
} // namespace
HardwareOpus::HardwareOpus(Core::System& system_)
- : system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} {
+ : system{system_}
+ , opus_decoder{system.AudioCore().ADSP().OpusDecoder()}
+{
opus_decoder.SetSharedMemory(shared_memory);
}
@@ -112,7 +91,7 @@ Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count,
R_THROW(ResultInvalidOpusDSPReturnCode);
}
- R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
+ R_RETURN(Result(u32(shared_memory.dsp_return_data[0])));
}
Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
@@ -140,7 +119,7 @@ Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 chan
R_THROW(ResultInvalidOpusDSPReturnCode);
}
- R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
+ R_RETURN(Result(u32(shared_memory.dsp_return_data[0])));
}
Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) {
@@ -154,7 +133,7 @@ Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) {
"Expected Opus shutdown code {}, got {}",
ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg);
- R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
+ R_RETURN(Result(u32(shared_memory.dsp_return_data[0])));
}
Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) {
@@ -169,7 +148,7 @@ Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_si
"Expected Opus shutdown code {}, got {}",
ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg);
- R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
+ R_RETURN(Result(u32(shared_memory.dsp_return_data[0])));
}
Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data,
@@ -193,12 +172,12 @@ Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data,
R_THROW(ResultInvalidOpusDSPReturnCode);
}
- auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
- if (error_code == OPUS_OK) {
- out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
+ auto error_code = s32(shared_memory.dsp_return_data[0]);
+ if (error_code == ResultSuccess.raw) {
+ out_sample_count = u32(shared_memory.dsp_return_data[1]);
out_time_taken = 1000 * shared_memory.dsp_return_data[2];
}
- R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
+ R_RETURN(Result(u32(error_code)));
}
Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
@@ -207,29 +186,27 @@ Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void
void* buffer, u64& out_time_taken,
bool reset) {
std::scoped_lock l{mutex};
- shared_memory.host_send_data[0] = (u64)buffer;
- shared_memory.host_send_data[1] = (u64)input_data;
+ shared_memory.host_send_data[0] = u64(buffer);
+ shared_memory.host_send_data[1] = u64(input_data);
shared_memory.host_send_data[2] = input_data_size;
- shared_memory.host_send_data[3] = (u64)output_data;
+ shared_memory.host_send_data[3] = u64(output_data);
shared_memory.host_send_data[4] = output_data_size;
shared_memory.host_send_data[5] = 0;
shared_memory.host_send_data[6] = reset;
- opus_decoder.Send(ADSP::Direction::DSP,
- ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream);
+ opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) {
- LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
- ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg);
+ LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
- auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
- if (error_code == OPUS_OK) {
+ auto const error_code = shared_memory.dsp_return_data[0];
+ if (error_code == ResultSuccess.raw) {
out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
out_time_taken = 1000 * shared_memory.dsp_return_data[2];
}
- R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
+ R_RETURN(Result(u32(shared_memory.dsp_return_data[0])));
}
Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) {
@@ -240,8 +217,7 @@ Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) {
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) {
- LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
- ADSP::OpusDecoder::Message::MapMemoryOK, msg);
+ LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", ADSP::OpusDecoder::Message::MapMemoryOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
R_SUCCEED();
@@ -255,8 +231,7 @@ Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) {
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) {
- LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
- ADSP::OpusDecoder::Message::UnmapMemoryOK, msg);
+ LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", ADSP::OpusDecoder::Message::UnmapMemoryOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
R_SUCCEED();
diff --git a/src/audio_core/opus/hardware_opus.h b/src/audio_core/opus/hardware_opus.h
index b6c7fbe6c8..26c96e00c0 100644
--- a/src/audio_core/opus/hardware_opus.h
+++ b/src/audio_core/opus/hardware_opus.h
@@ -8,8 +8,6 @@
#include <array>
#include <mutex>
-#include <opus.h>
-
#include "audio_core/adsp/apps/opus/opus_decoder.h"
#include "audio_core/adsp/apps/opus/shared_memory.h"
#include "audio_core/adsp/mailbox.h"