Revert "[audio] Replace libopus with FFmpeg (#4169)" (#4419)

This reverts commit ff8368c606.

- [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 <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
This commit is contained in:
lizzie
2026-09-14 14:34:24 +02:00
committed by crueter
parent d9159fefdd
commit 2b4184dd2b
22 changed files with 913 additions and 438 deletions
+6 -8
View File
@@ -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()
$<$<COMPILE_LANGUAGE:C,CXX>:-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
@@ -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<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
@@ -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 <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
+228 -359
View File
@@ -5,388 +5,55 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <chrono>
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/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;
}
static u32 GetWorkBufferSize(u32 channel_count) {
if (channel_count == 1 || channel_count == 2)
return 48 + 16 * channel_count;
return 0;
}
/// 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;
}
// 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;
}
Result Shutdown() {
avcodec_free_context(&avc);
av_frame_free(&frame);
av_packet_free(&avpkt);
return ResultSuccess;
}
Result ResetDecoder() {
if (avc) {
if (avcodec_is_open(avc)) avcodec_flush_buffers(avc);
return ResultSuccess;
}
return Service::Audio::ResultLibOpusInvalidState;
}
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;
}
AVCodecContext* avc = nullptr;
AVPacket* avpkt = nullptr;
AVFrame* frame = nullptr;
};
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) {
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;
}
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 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;
}
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;
}
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;
}
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();
});
OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} {
init_thread = std::jthread([this](std::stop_token stop_token) { Init(stop_token); });
}
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();
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;
}
void OpusDecoder::Send(Direction dir, u32 message) {
@@ -397,4 +64,206 @@ 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;
}
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;
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<u32>(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();
}
if (error_code == OPUS_OK) {
error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
input_data, input_data_size);
}
if (error_code == OPUS_OK) {
if (final_range && decoder_object.GetFinalRange() != final_range) {
error_code = OPUS_INVALID_PACKET;
}
}
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();
}
if (error_code == OPUS_OK) {
error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
input_data, input_data_size);
}
if (error_code == OPUS_OK) {
if (final_range && decoder_object.GetFinalRange() != final_range) {
error_code = OPUS_INVALID_PACKET;
}
}
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::DecodeInterleavedForMultiStreamOK);
} break;
default:
LOG_ERROR(Service_Audio, "Invalid OpusDecoder command {}", msg);
continue;
}
}
}
} // namespace AudioCore::ADSP::OpusDecoder
+22 -5
View File
@@ -6,13 +6,12 @@
#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;
@@ -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
@@ -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<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
@@ -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 <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
@@ -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);
}
@@ -6,6 +6,7 @@
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace AudioCore::ADSP::OpusDecoder {
+48 -23
View File
@@ -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<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);
}
@@ -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<s32>(shared_memory.dsp_return_data[0])};
if (error_code == OPUS_OK) {
out_sample_count = static_cast<u32>(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<s32>(shared_memory.dsp_return_data[0])};
if (error_code == OPUS_OK) {
out_sample_count = static_cast<u32>(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();
+2
View File
@@ -8,6 +8,8 @@
#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"