// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #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/log.h" #include "common/microprofile.h" #include "common/thread.h" #include "core/core.h" #include "core/core_timing.h" MICROPROFILE_DEFINE(OpusDecoder, "Audio", "DSP_OpusDecoder", MP_RGB(60, 19, 97)); namespace AudioCore::ADSP::OpusDecoder { namespace { constexpr size_t OpusStreamCountMax = 255; bool IsValidChannelCount(u32 channel_count) { return channel_count == 1 || channel_count == 2; } bool IsValidMultiStreamChannelCount(u32 channel_count) { return channel_count <= OpusStreamCountMax; } bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 sterero_stream_count) { return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 && sterero_stream_count > 0 && sterero_stream_count <= total_stream_count; } } // namespace OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} { init_thread = std::jthread([this](std::stop_token stop_token) { Init(stop_token); }); } OpusDecoder::~OpusDecoder() { if (!running) { init_thread.request_stop(); return; } // Shutdown the thread Send(Direction::DSP, Message::Shutdown); auto msg = Receive(Direction::Host); ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}", Message::ShutdownOK, msg); main_thread.request_stop(); main_thread.join(); running = false; } void OpusDecoder::Send(Direction dir, u32 message) { mailbox.Send(dir, std::move(message)); } u32 OpusDecoder::Receive(Direction dir, std::stop_token stop_token) { return mailbox.Receive(dir, stop_token); } void OpusDecoder::Init(std::stop_token stop_token) { Common::SetCurrentThreadName("DSP_OpusDecoder_Init"); if (Receive(Direction::DSP, stop_token) != Message::Start) { LOG_ERROR(Service_Audio, "DSP OpusDecoder failed to receive Start message. Opus initialization failed."); return; } 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(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(); } 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(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(); } 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