From 2b4184dd2bb33da081702263d59c3eadb299e673 Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 14 Sep 2026 14:34:24 +0200 Subject: Revert "[audio] Replace libopus with FFmpeg (#4169)" (#4419) This reverts commit ff8368c6066faf3962e2f77af7da4954e2ff6a43. - [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions). - [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md) - [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability. ------------------- Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4419 Reviewed-by: Maufeat Reviewed-by: MaranBr --- .../yuzu/yuzu_emu/fragments/LicensesFragment.kt | 7 + src/android/app/src/main/res/values/strings.xml | 45 ++ src/audio_core/CMakeLists.txt | 14 +- .../adsp/apps/opus/opus_decode_object.cpp | 110 ++++ src/audio_core/adsp/apps/opus/opus_decode_object.h | 41 ++ src/audio_core/adsp/apps/opus/opus_decoder.cpp | 569 ++++++++------------- src/audio_core/adsp/apps/opus/opus_decoder.h | 27 +- .../apps/opus/opus_multistream_decode_object.cpp | 113 ++++ .../apps/opus/opus_multistream_decode_object.h | 42 ++ src/audio_core/adsp/apps/opus/opus_types.h | 32 -- src/audio_core/adsp/apps/opus/shared_memory.h | 1 + src/audio_core/opus/hardware_opus.cpp | 71 ++- src/audio_core/opus/hardware_opus.h | 2 + src/core/CMakeLists.txt | 1 + src/video_core/CMakeLists.txt | 2 + 15 files changed, 659 insertions(+), 418 deletions(-) create mode 100644 src/audio_core/adsp/apps/opus/opus_decode_object.cpp create mode 100644 src/audio_core/adsp/apps/opus/opus_decode_object.h create mode 100644 src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp create mode 100644 src/audio_core/adsp/apps/opus/opus_multistream_decode_object.h delete mode 100644 src/audio_core/adsp/apps/opus/opus_types.h (limited to 'src') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt index 226edfa00c..32b72fe38f 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt @@ -79,6 +79,13 @@ class LicensesFragment : Fragment() { R.string.license_ffmpeg_copyright, R.string.license_ffmpeg_text ), + License( + R.string.license_opus, + R.string.license_opus_description, + R.string.license_opus_link, + R.string.license_opus_copyright, + R.string.license_opus_text + ), License( R.string.license_sirit, R.string.license_sirit_description, diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index e712ec4338..730f6bbade 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -1784,6 +1784,51 @@ RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + Opus + Modern audio compression for the internet + https://github.com/xiph/opus + Copyright 2001–2011 Xiph.Org, Skype Limited, Octasic, Jean-Marc Valin, Timothy B. Terriberry, CSIRO, Gregory Maxwell, Mark Borgerding, Erik de Castro Lopo + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met:\n\n + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer.\n\n + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution.\n\n + +- Neither the name of Internet Society, IETF or IETF Trust, nor the +names of specific contributors, may be used to endorse or promote +products derived from this software without specific prior written +permission.\n\n + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS\'\' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n + +Opus is subject to the royalty-free patent licenses which are +specified at:\n\n + +Xiph.Org Foundation: +https://datatracker.ietf.org/ipr/1524/ \n\n + +Microsoft Corporation: +https://datatracker.ietf.org/ipr/1914/ \n\n + +Broadcom Corporation: +https://datatracker.ietf.org/ipr/1526/ Sirit A runtime SPIR-V assembler diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 71a46a7b1e..b244d657a9 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -15,6 +15,10 @@ 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 @@ -222,14 +226,8 @@ else() $<$:-Wno-sign-conversion>) endif() -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) +target_include_directories(audio_core PRIVATE ${OPUS_INCLUDE_DIRS}) +target_link_libraries(audio_core PUBLIC common core Opus::opus) 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 new file mode 100644 index 0000000000..a47a7f8527 --- /dev/null +++ b/src/audio_core/adsp/apps/opus/opus_decode_object.cpp @@ -0,0 +1,110 @@ +// 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_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(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count); +} + +OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) { + auto* new_decoder = reinterpret_cast(buffer); + auto* comparison = reinterpret_cast(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(input_data), static_cast(input_data_size), + reinterpret_cast(output_data), static_cast(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 new file mode 100644 index 0000000000..feee9587ce --- /dev/null +++ b/src/audio_core/adsp/apps/opus/opus_decode_object.h @@ -0,0 +1,41 @@ +// 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 + +#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); + +} // 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 0be0f774b1..8a009c04c7 100644 --- a/src/audio_core/adsp/apps/opus/opus_decoder.cpp +++ b/src/audio_core/adsp/apps/opus/opus_decoder.cpp @@ -5,396 +5,265 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include -extern "C" { -#include -#include -#include -#include -#include -#include -#include -#include -} - +#include "audio_core/adsp/apps/opus/opus_decode_object.h" +#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.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 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; +constexpr size_t OpusStreamCountMax = 255; bool IsValidChannelCount(u32 channel_count) { - return channel_count >= 1 || channel_count <= OPUS_MAX_CHANNELS; + return channel_count == 1 || channel_count == 2; } -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 IsValidMultiStreamChannelCount(u32 channel_count) { + return channel_count <= OpusStreamCountMax; } -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; - } +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 - static u32 GetWorkBufferSize(u32 channel_count) { - if (channel_count == 1 || channel_count == 2) - return 48 + 16 * channel_count; - return 0; - } +OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} { + init_thread = std::jthread([this](std::stop_token stop_token) { Init(stop_token); }); +} - /// 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 mapping_arr{0, 1}; - mappings = mappings ? mappings : mapping_arr.data(); - - // freed by avcodec_context_free() - u8 *edata = reinterpret_cast(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; - } +OpusDecoder::~OpusDecoder() { + if (!running) { + init_thread.request_stop(); + return; + } + // 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; +} - // 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; - } +void OpusDecoder::Send(Direction dir, u32 message) { + mailbox.Send(dir, std::move(message)); +} - Result Shutdown() { - avcodec_free_context(&avc); - av_frame_free(&frame); - av_packet_free(&avpkt); - return ResultSuccess; - } +u32 OpusDecoder::Receive(Direction dir, std::stop_token stop_token) { + return mailbox.Receive(dir, stop_token); +} - Result ResetDecoder() { - if (avc) { - if (avcodec_is_open(avc)) avcodec_flush_buffers(avc); - return ResultSuccess; - } - return Service::Audio::ResultLibOpusInvalidState; - } +void OpusDecoder::Init(std::stop_token stop_token) { + Common::SetCurrentThreadName("DSP_OpusDecoder_Init"); - 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(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(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(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; + if (Receive(Direction::DSP, stop_token) != Message::Start) { + LOG_ERROR(Service_Audio, + "DSP OpusDecoder failed to receive Start message. Opus initialization failed."); + return; } + main_thread = std::jthread([this](std::stop_token st) { Main(st); }); + running = true; + Send(Direction::Host, Message::StartOK); +} - AVCodecContext* avc = nullptr; - AVPacket* avpkt = nullptr; - AVFrame* frame = nullptr; -}; -} // namespace +void OpusDecoder::Main(std::stop_token stop_token) { + Common::SetCurrentThreadName("DSP_OpusDecoder_Main"); -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."); + while (!stop_token.stop_requested()) { + auto msg = Receive(Direction::DSP, stop_token); + switch (msg) { + case Shutdown: + Send(Direction::Host, Message::ShutdownOK); return; - } - Send(Direction::Host, Message::StartOK); - - // Main OpusDecoder thread, responsible for processing the incoming Opus packets. - ::Common::unordered_map 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; - } - 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; + + case GetWorkBufferSize: { + auto channel_count = static_cast(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(shared_memory->host_send_data[2]); + auto channel_count = static_cast(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; + + 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(); + + Send(Direction::Host, Message::ShutdownDecodeObjectOK); + } break; + + 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(shared_memory->host_send_data[5]); + auto reset_requested = shared_memory->host_send_data[6]; + + u32 decoded_samples{0}; + + auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer); + s32 error_code{OPUS_OK}; + if (reset_requested) { + error_code = decoder_object.ResetDecoder(); } - 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; + + if (error_code == OPUS_OK) { + error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size, + input_data, input_data_size); } - 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(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; + + if (error_code == OPUS_OK) { + if (final_range && decoder_object.GetFinalRange() != final_range) { + error_code = OPUS_INVALID_PACKET; } - 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 = s32(shared_memory->host_send_data[0]); - auto stereo_stream_count = s32(shared_memory->host_send_data[1]); - 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; + 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(shared_memory->host_send_data[0]); + auto stereo_stream_count = static_cast(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(shared_memory->host_send_data[2]); + auto channel_count = static_cast(shared_memory->host_send_data[3]); + auto total_stream_count = static_cast(shared_memory->host_send_data[4]); + auto stereo_stream_count = static_cast(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(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 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; + + if (error_code == OPUS_OK) { + error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size, + input_data, input_data_size); } - 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(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; + + if (error_code == OPUS_OK) { + if (final_range && decoder_object.GetFinalRange() != final_range) { + error_code = OPUS_INVALID_PACKET; } - Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK); - break; - } - default: - LOG_ERROR(Audio_DSP, "Invalid OpusDecoder command {}", msg); - continue; } - } - for (auto e : decode_objects) - e.second.Shutdown(); - }); -} -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(); - } -} + 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(); -void OpusDecoder::Send(Direction dir, u32 message) { - mailbox.Send(dir, std::move(message)); -} + Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK); + } break; -u32 OpusDecoder::Receive(Direction dir, std::stop_token stop_token) { - return mailbox.Receive(dir, stop_token); + default: + LOG_ERROR(Service_Audio, "Invalid OpusDecoder command {}", msg); + continue; + } + } } } // 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 dd87b473d1..5b3f22bf4c 100644 --- a/src/audio_core/adsp/apps/opus/opus_decoder.h +++ b/src/audio_core/adsp/apps/opus/opus_decoder.h @@ -6,13 +6,12 @@ #pragma once +#include #include -#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; @@ -49,14 +48,16 @@ enum Message : u32 { DecodeInterleavedForMultiStreamOK = 50, }; -/// @brief The AudioRenderer application running on the ADSP. +/** + * The AudioRenderer application running on the ADSP. + */ class OpusDecoder { public: explicit OpusDecoder(Core::System& system); ~OpusDecoder(); bool IsRunning() const noexcept { - return dsp_thread.joinable(); + return running; } void Send(Direction dir, u32 message); @@ -67,12 +68,28 @@ 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 new file mode 100644 index 0000000000..6fecd63309 --- /dev/null +++ b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp @@ -0,0 +1,113 @@ +// 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(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(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(buffer); + auto* comparison = reinterpret_cast(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(input_data), static_cast(input_data_size), + reinterpret_cast(output_data), static_cast(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 new file mode 100644 index 0000000000..baad58385e --- /dev/null +++ b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.h @@ -0,0 +1,42 @@ +// 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 + +#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); + +} // 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 deleted file mode 100644 index bacfb30338..0000000000 --- a/src/audio_core/adsp/apps/opus/opus_types.h +++ /dev/null @@ -1,32 +0,0 @@ -// 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 45079c6292..b15a1837cc 100644 --- a/src/audio_core/adsp/apps/opus/shared_memory.h +++ b/src/audio_core/adsp/apps/opus/shared_memory.h @@ -6,6 +6,7 @@ #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 c07a358168..04591c10b6 100644 --- a/src/audio_core/opus/hardware_opus.cpp +++ b/src/audio_core/opus/hardware_opus.cpp @@ -10,18 +10,39 @@ #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(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); } @@ -91,7 +112,7 @@ Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, R_THROW(ResultInvalidOpusDSPReturnCode); } - R_RETURN(Result(u32(shared_memory.dsp_return_data[0]))); + R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); } Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, @@ -119,7 +140,7 @@ Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 chan R_THROW(ResultInvalidOpusDSPReturnCode); } - R_RETURN(Result(u32(shared_memory.dsp_return_data[0]))); + R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); } Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) { @@ -133,7 +154,7 @@ Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) { "Expected Opus shutdown code {}, got {}", ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg); - R_RETURN(Result(u32(shared_memory.dsp_return_data[0]))); + R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); } Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) { @@ -148,7 +169,7 @@ Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_si "Expected Opus shutdown code {}, got {}", ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg); - R_RETURN(Result(u32(shared_memory.dsp_return_data[0]))); + R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); } Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data, @@ -172,12 +193,12 @@ Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data, R_THROW(ResultInvalidOpusDSPReturnCode); } - 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]); + auto error_code{static_cast(shared_memory.dsp_return_data[0])}; + if (error_code == OPUS_OK) { + out_sample_count = static_cast(shared_memory.dsp_return_data[1]); out_time_taken = 1000 * shared_memory.dsp_return_data[2]; } - R_RETURN(Result(u32(error_code))); + R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); } Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, @@ -186,27 +207,29 @@ 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 const error_code = shared_memory.dsp_return_data[0]; - if (error_code == ResultSuccess.raw) { + auto error_code{static_cast(shared_memory.dsp_return_data[0])}; + if (error_code == OPUS_OK) { out_sample_count = static_cast(shared_memory.dsp_return_data[1]); out_time_taken = 1000 * shared_memory.dsp_return_data[2]; } - R_RETURN(Result(u32(shared_memory.dsp_return_data[0]))); + R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); } Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) { @@ -217,7 +240,8 @@ 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(); @@ -231,7 +255,8 @@ 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 26c96e00c0..b6c7fbe6c8 100644 --- a/src/audio_core/opus/hardware_opus.h +++ b/src/audio_core/opus/hardware_opus.h @@ -8,6 +8,8 @@ #include #include +#include + #include "audio_core/adsp/apps/opus/opus_decoder.h" #include "audio_core/adsp/apps/opus/shared_memory.h" #include "audio_core/adsp/mailbox.h" diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e72fd791b9..508ace3501 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1199,6 +1199,7 @@ else() endif() endif() +target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS}) target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz) if (BOOST_NO_HEADERS) diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 06b77a2a8f..64d14ab2a6 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -414,7 +414,9 @@ endif() if (YUZU_USE_EXTERNAL_FFMPEG) add_dependencies(video_core ffmpeg-build) endif() + target_include_directories(video_core PUBLIC ${FFmpeg_INCLUDE_DIR}) + target_link_libraries(video_core PRIVATE ${FFmpeg_LIBRARIES}) target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS}) -- cgit v1.2.3