diff options
Diffstat (limited to 'src/audio_core')
20 files changed, 1465 insertions, 72 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 67dfe0290..400988c5f 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt | |||
| @@ -10,6 +10,13 @@ add_library(audio_core STATIC | |||
| 10 | adsp/apps/audio_renderer/command_buffer.h | 10 | adsp/apps/audio_renderer/command_buffer.h |
| 11 | adsp/apps/audio_renderer/command_list_processor.cpp | 11 | adsp/apps/audio_renderer/command_list_processor.cpp |
| 12 | adsp/apps/audio_renderer/command_list_processor.h | 12 | adsp/apps/audio_renderer/command_list_processor.h |
| 13 | adsp/apps/opus/opus_decoder.cpp | ||
| 14 | adsp/apps/opus/opus_decoder.h | ||
| 15 | adsp/apps/opus/opus_decode_object.cpp | ||
| 16 | adsp/apps/opus/opus_decode_object.h | ||
| 17 | adsp/apps/opus/opus_multistream_decode_object.cpp | ||
| 18 | adsp/apps/opus/opus_multistream_decode_object.h | ||
| 19 | adsp/apps/opus/shared_memory.h | ||
| 13 | audio_core.cpp | 20 | audio_core.cpp |
| 14 | audio_core.h | 21 | audio_core.h |
| 15 | audio_event.h | 22 | audio_event.h |
| @@ -35,6 +42,13 @@ add_library(audio_core STATIC | |||
| 35 | in/audio_in.h | 42 | in/audio_in.h |
| 36 | in/audio_in_system.cpp | 43 | in/audio_in_system.cpp |
| 37 | in/audio_in_system.h | 44 | in/audio_in_system.h |
| 45 | opus/hardware_opus.cpp | ||
| 46 | opus/hardware_opus.h | ||
| 47 | opus/decoder_manager.cpp | ||
| 48 | opus/decoder_manager.h | ||
| 49 | opus/decoder.cpp | ||
| 50 | opus/decoder.h | ||
| 51 | opus/parameters.h | ||
| 38 | out/audio_out.cpp | 52 | out/audio_out.cpp |
| 39 | out/audio_out.h | 53 | out/audio_out.h |
| 40 | out/audio_out_system.cpp | 54 | out/audio_out_system.cpp |
| @@ -214,7 +228,7 @@ else() | |||
| 214 | ) | 228 | ) |
| 215 | endif() | 229 | endif() |
| 216 | 230 | ||
| 217 | target_link_libraries(audio_core PUBLIC common core) | 231 | target_link_libraries(audio_core PUBLIC common core Opus::opus) |
| 218 | if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) | 232 | if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) |
| 219 | target_link_libraries(audio_core PRIVATE dynarmic::dynarmic) | 233 | target_link_libraries(audio_core PRIVATE dynarmic::dynarmic) |
| 220 | endif() | 234 | endif() |
diff --git a/src/audio_core/adsp/adsp.cpp b/src/audio_core/adsp/adsp.cpp index 0580990f5..6c53c98fd 100644 --- a/src/audio_core/adsp/adsp.cpp +++ b/src/audio_core/adsp/adsp.cpp | |||
| @@ -7,12 +7,21 @@ | |||
| 7 | namespace AudioCore::ADSP { | 7 | namespace AudioCore::ADSP { |
| 8 | 8 | ||
| 9 | ADSP::ADSP(Core::System& system, Sink::Sink& sink) { | 9 | ADSP::ADSP(Core::System& system, Sink::Sink& sink) { |
| 10 | audio_renderer = | 10 | audio_renderer = std::make_unique<AudioRenderer::AudioRenderer>(system, sink); |
| 11 | std::make_unique<AudioRenderer::AudioRenderer>(system, system.ApplicationMemory(), sink); | 11 | opus_decoder = std::make_unique<OpusDecoder::OpusDecoder>(system); |
| 12 | opus_decoder->Send(Direction::DSP, OpusDecoder::Message::Start); | ||
| 13 | if (opus_decoder->Receive(Direction::Host) != OpusDecoder::Message::StartOK) { | ||
| 14 | LOG_ERROR(Service_Audio, "OpusDeocder failed to initialize."); | ||
| 15 | return; | ||
| 16 | } | ||
| 12 | } | 17 | } |
| 13 | 18 | ||
| 14 | AudioRenderer::AudioRenderer& ADSP::AudioRenderer() { | 19 | AudioRenderer::AudioRenderer& ADSP::AudioRenderer() { |
| 15 | return *audio_renderer.get(); | 20 | return *audio_renderer.get(); |
| 16 | } | 21 | } |
| 17 | 22 | ||
| 23 | OpusDecoder::OpusDecoder& ADSP::OpusDecoder() { | ||
| 24 | return *opus_decoder.get(); | ||
| 25 | } | ||
| 26 | |||
| 18 | } // namespace AudioCore::ADSP | 27 | } // namespace AudioCore::ADSP |
diff --git a/src/audio_core/adsp/adsp.h b/src/audio_core/adsp/adsp.h index bd5bcc63b..a0c24a16a 100644 --- a/src/audio_core/adsp/adsp.h +++ b/src/audio_core/adsp/adsp.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "audio_core/adsp/apps/audio_renderer/audio_renderer.h" | 6 | #include "audio_core/adsp/apps/audio_renderer/audio_renderer.h" |
| 7 | #include "audio_core/adsp/apps/opus/opus_decoder.h" | ||
| 7 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 8 | 9 | ||
| 9 | namespace Core { | 10 | namespace Core { |
| @@ -40,10 +41,12 @@ public: | |||
| 40 | ~ADSP() = default; | 41 | ~ADSP() = default; |
| 41 | 42 | ||
| 42 | AudioRenderer::AudioRenderer& AudioRenderer(); | 43 | AudioRenderer::AudioRenderer& AudioRenderer(); |
| 44 | OpusDecoder::OpusDecoder& OpusDecoder(); | ||
| 43 | 45 | ||
| 44 | private: | 46 | private: |
| 45 | /// AudioRenderer app | 47 | /// AudioRenderer app |
| 46 | std::unique_ptr<AudioRenderer::AudioRenderer> audio_renderer{}; | 48 | std::unique_ptr<AudioRenderer::AudioRenderer> audio_renderer{}; |
| 49 | std::unique_ptr<OpusDecoder::OpusDecoder> opus_decoder{}; | ||
| 47 | }; | 50 | }; |
| 48 | 51 | ||
| 49 | } // namespace ADSP | 52 | } // namespace ADSP |
diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp index 2e549bc6f..972d5e45b 100644 --- a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp | |||
| @@ -14,13 +14,12 @@ | |||
| 14 | #include "core/core.h" | 14 | #include "core/core.h" |
| 15 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| 16 | 16 | ||
| 17 | MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97)); | 17 | MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP_AudioRenderer", MP_RGB(60, 19, 97)); |
| 18 | 18 | ||
| 19 | namespace AudioCore::ADSP::AudioRenderer { | 19 | namespace AudioCore::ADSP::AudioRenderer { |
| 20 | 20 | ||
| 21 | AudioRenderer::AudioRenderer(Core::System& system_, Core::Memory::Memory& memory_, | 21 | AudioRenderer::AudioRenderer(Core::System& system_, Sink::Sink& sink_) |
| 22 | Sink::Sink& sink_) | 22 | : system{system_}, sink{sink_} {} |
| 23 | : system{system_}, memory{memory_}, sink{sink_} {} | ||
| 24 | 23 | ||
| 25 | AudioRenderer::~AudioRenderer() { | 24 | AudioRenderer::~AudioRenderer() { |
| 26 | Stop(); | 25 | Stop(); |
| @@ -33,8 +32,8 @@ void AudioRenderer::Start() { | |||
| 33 | 32 | ||
| 34 | main_thread = std::jthread([this](std::stop_token stop_token) { Main(stop_token); }); | 33 | main_thread = std::jthread([this](std::stop_token stop_token) { Main(stop_token); }); |
| 35 | 34 | ||
| 36 | mailbox.Send(Direction::DSP, {Message::InitializeOK, {}}); | 35 | mailbox.Send(Direction::DSP, Message::InitializeOK); |
| 37 | if (mailbox.Receive(Direction::Host).msg != Message::InitializeOK) { | 36 | if (mailbox.Receive(Direction::Host) != Message::InitializeOK) { |
| 38 | LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " | 37 | LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " |
| 39 | "message response from ADSP!"); | 38 | "message response from ADSP!"); |
| 40 | return; | 39 | return; |
| @@ -47,8 +46,8 @@ void AudioRenderer::Stop() { | |||
| 47 | return; | 46 | return; |
| 48 | } | 47 | } |
| 49 | 48 | ||
| 50 | mailbox.Send(Direction::DSP, {Message::Shutdown, {}}); | 49 | mailbox.Send(Direction::DSP, Message::Shutdown); |
| 51 | if (mailbox.Receive(Direction::Host).msg != Message::Shutdown) { | 50 | if (mailbox.Receive(Direction::Host) != Message::Shutdown) { |
| 52 | LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " | 51 | LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " |
| 53 | "message response from ADSP!"); | 52 | "message response from ADSP!"); |
| 54 | } | 53 | } |
| @@ -67,25 +66,25 @@ void AudioRenderer::Stop() { | |||
| 67 | 66 | ||
| 68 | void AudioRenderer::Signal() { | 67 | void AudioRenderer::Signal() { |
| 69 | signalled_tick = system.CoreTiming().GetGlobalTimeNs().count(); | 68 | signalled_tick = system.CoreTiming().GetGlobalTimeNs().count(); |
| 70 | Send(Direction::DSP, {Message::Render, {}}); | 69 | Send(Direction::DSP, Message::Render); |
| 71 | } | 70 | } |
| 72 | 71 | ||
| 73 | void AudioRenderer::Wait() { | 72 | void AudioRenderer::Wait() { |
| 74 | auto received = Receive(Direction::Host); | 73 | auto msg = Receive(Direction::Host); |
| 75 | if (received.msg != Message::RenderResponse) { | 74 | if (msg != Message::RenderResponse) { |
| 76 | LOG_ERROR(Service_Audio, | 75 | LOG_ERROR(Service_Audio, |
| 77 | "Did not receive the expected render response from the AudioRenderer! Expected " | 76 | "Did not receive the expected render response from the AudioRenderer! Expected " |
| 78 | "{}, got {}", | 77 | "{}, got {}", |
| 79 | Message::RenderResponse, received.msg); | 78 | Message::RenderResponse, msg); |
| 80 | } | 79 | } |
| 81 | } | 80 | } |
| 82 | 81 | ||
| 83 | void AudioRenderer::Send(Direction dir, MailboxMessage message) { | 82 | void AudioRenderer::Send(Direction dir, u32 message) { |
| 84 | mailbox.Send(dir, std::move(message)); | 83 | mailbox.Send(dir, std::move(message)); |
| 85 | } | 84 | } |
| 86 | 85 | ||
| 87 | MailboxMessage AudioRenderer::Receive(Direction dir, bool block) { | 86 | u32 AudioRenderer::Receive(Direction dir) { |
| 88 | return mailbox.Receive(dir, block); | 87 | return mailbox.Receive(dir); |
| 89 | } | 88 | } |
| 90 | 89 | ||
| 91 | void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit, | 90 | void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit, |
| @@ -120,7 +119,7 @@ void AudioRenderer::CreateSinkStreams() { | |||
| 120 | } | 119 | } |
| 121 | 120 | ||
| 122 | void AudioRenderer::Main(std::stop_token stop_token) { | 121 | void AudioRenderer::Main(std::stop_token stop_token) { |
| 123 | static constexpr char name[]{"AudioRenderer"}; | 122 | static constexpr char name[]{"DSP_AudioRenderer_Main"}; |
| 124 | MicroProfileOnThreadCreate(name); | 123 | MicroProfileOnThreadCreate(name); |
| 125 | Common::SetCurrentThreadName(name); | 124 | Common::SetCurrentThreadName(name); |
| 126 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); | 125 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); |
| @@ -128,28 +127,28 @@ void AudioRenderer::Main(std::stop_token stop_token) { | |||
| 128 | // TODO: Create buffer map/unmap thread + mailbox | 127 | // TODO: Create buffer map/unmap thread + mailbox |
| 129 | // TODO: Create gMix devices, initialize them here | 128 | // TODO: Create gMix devices, initialize them here |
| 130 | 129 | ||
| 131 | if (mailbox.Receive(Direction::DSP).msg != Message::InitializeOK) { | 130 | if (mailbox.Receive(Direction::DSP) != Message::InitializeOK) { |
| 132 | LOG_ERROR(Service_Audio, | 131 | LOG_ERROR(Service_Audio, |
| 133 | "ADSP Audio Renderer -- Failed to receive initialize message from host!"); | 132 | "ADSP Audio Renderer -- Failed to receive initialize message from host!"); |
| 134 | return; | 133 | return; |
| 135 | } | 134 | } |
| 136 | 135 | ||
| 137 | mailbox.Send(Direction::Host, {Message::InitializeOK, {}}); | 136 | mailbox.Send(Direction::Host, Message::InitializeOK); |
| 138 | 137 | ||
| 139 | // 0.12 seconds (2,304,000 / 19,200,000) | 138 | // 0.12 seconds (2,304,000 / 19,200,000) |
| 140 | constexpr u64 max_process_time{2'304'000ULL}; | 139 | constexpr u64 max_process_time{2'304'000ULL}; |
| 141 | 140 | ||
| 142 | while (!stop_token.stop_requested()) { | 141 | while (!stop_token.stop_requested()) { |
| 143 | auto received{mailbox.Receive(Direction::DSP)}; | 142 | auto msg{mailbox.Receive(Direction::DSP)}; |
| 144 | switch (received.msg) { | 143 | switch (msg) { |
| 145 | case Message::Shutdown: | 144 | case Message::Shutdown: |
| 146 | mailbox.Send(Direction::Host, {Message::Shutdown, {}}); | 145 | mailbox.Send(Direction::Host, Message::Shutdown); |
| 147 | return; | 146 | return; |
| 148 | 147 | ||
| 149 | case Message::Render: { | 148 | case Message::Render: { |
| 150 | if (system.IsShuttingDown()) [[unlikely]] { | 149 | if (system.IsShuttingDown()) [[unlikely]] { |
| 151 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); | 150 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); |
| 152 | mailbox.Send(Direction::Host, {Message::RenderResponse, {}}); | 151 | mailbox.Send(Direction::Host, Message::RenderResponse); |
| 153 | continue; | 152 | continue; |
| 154 | } | 153 | } |
| 155 | std::array<bool, MaxRendererSessions> buffers_reset{}; | 154 | std::array<bool, MaxRendererSessions> buffers_reset{}; |
| @@ -205,13 +204,12 @@ void AudioRenderer::Main(std::stop_token stop_token) { | |||
| 205 | } | 204 | } |
| 206 | } | 205 | } |
| 207 | 206 | ||
| 208 | mailbox.Send(Direction::Host, {Message::RenderResponse, {}}); | 207 | mailbox.Send(Direction::Host, Message::RenderResponse); |
| 209 | } break; | 208 | } break; |
| 210 | 209 | ||
| 211 | default: | 210 | default: |
| 212 | LOG_WARNING(Service_Audio, | 211 | LOG_WARNING(Service_Audio, |
| 213 | "ADSP AudioRenderer received an invalid message, msg={:02X}!", | 212 | "ADSP AudioRenderer received an invalid message, msg={:02X}!", msg); |
| 214 | received.msg); | ||
| 215 | break; | 213 | break; |
| 216 | } | 214 | } |
| 217 | } | 215 | } |
diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h index 3f5b7dca2..85874d88a 100644 --- a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h | |||
| @@ -17,13 +17,6 @@ | |||
| 17 | 17 | ||
| 18 | namespace Core { | 18 | namespace Core { |
| 19 | class System; | 19 | class System; |
| 20 | namespace Timing { | ||
| 21 | struct EventType; | ||
| 22 | } | ||
| 23 | namespace Memory { | ||
| 24 | class Memory; | ||
| 25 | } | ||
| 26 | class System; | ||
| 27 | } // namespace Core | 20 | } // namespace Core |
| 28 | 21 | ||
| 29 | namespace AudioCore { | 22 | namespace AudioCore { |
| @@ -34,19 +27,19 @@ class Sink; | |||
| 34 | namespace ADSP::AudioRenderer { | 27 | namespace ADSP::AudioRenderer { |
| 35 | 28 | ||
| 36 | enum Message : u32 { | 29 | enum Message : u32 { |
| 37 | Invalid = 0x00, | 30 | Invalid = 0, |
| 38 | MapUnmap_Map = 0x01, | 31 | MapUnmap_Map = 1, |
| 39 | MapUnmap_MapResponse = 0x02, | 32 | MapUnmap_MapResponse = 2, |
| 40 | MapUnmap_Unmap = 0x03, | 33 | MapUnmap_Unmap = 3, |
| 41 | MapUnmap_UnmapResponse = 0x04, | 34 | MapUnmap_UnmapResponse = 4, |
| 42 | MapUnmap_InvalidateCache = 0x05, | 35 | MapUnmap_InvalidateCache = 5, |
| 43 | MapUnmap_InvalidateCacheResponse = 0x06, | 36 | MapUnmap_InvalidateCacheResponse = 6, |
| 44 | MapUnmap_Shutdown = 0x07, | 37 | MapUnmap_Shutdown = 7, |
| 45 | MapUnmap_ShutdownResponse = 0x08, | 38 | MapUnmap_ShutdownResponse = 8, |
| 46 | InitializeOK = 0x16, | 39 | InitializeOK = 22, |
| 47 | RenderResponse = 0x20, | 40 | RenderResponse = 32, |
| 48 | Render = 0x2A, | 41 | Render = 42, |
| 49 | Shutdown = 0x34, | 42 | Shutdown = 52, |
| 50 | }; | 43 | }; |
| 51 | 44 | ||
| 52 | /** | 45 | /** |
| @@ -54,7 +47,7 @@ enum Message : u32 { | |||
| 54 | */ | 47 | */ |
| 55 | class AudioRenderer { | 48 | class AudioRenderer { |
| 56 | public: | 49 | public: |
| 57 | explicit AudioRenderer(Core::System& system, Core::Memory::Memory& memory, Sink::Sink& sink); | 50 | explicit AudioRenderer(Core::System& system, Sink::Sink& sink); |
| 58 | ~AudioRenderer(); | 51 | ~AudioRenderer(); |
| 59 | 52 | ||
| 60 | /** | 53 | /** |
| @@ -72,8 +65,8 @@ public: | |||
| 72 | void Signal(); | 65 | void Signal(); |
| 73 | void Wait(); | 66 | void Wait(); |
| 74 | 67 | ||
| 75 | void Send(Direction dir, MailboxMessage message); | 68 | void Send(Direction dir, u32 message); |
| 76 | MailboxMessage Receive(Direction dir, bool block = true); | 69 | u32 Receive(Direction dir); |
| 77 | 70 | ||
| 78 | void SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit, | 71 | void SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit, |
| 79 | u64 applet_resource_user_id, bool reset) noexcept; | 72 | u64 applet_resource_user_id, bool reset) noexcept; |
| @@ -94,9 +87,7 @@ private: | |||
| 94 | 87 | ||
| 95 | /// Core system | 88 | /// Core system |
| 96 | Core::System& system; | 89 | Core::System& system; |
| 97 | /// Memory | 90 | /// The output sink the AudioRenderer will send samples to |
| 98 | Core::Memory::Memory& memory; | ||
| 99 | /// The output sink the AudioRenderer will use | ||
| 100 | Sink::Sink& sink; | 91 | Sink::Sink& sink; |
| 101 | /// The active mailbox | 92 | /// The active mailbox |
| 102 | Mailbox mailbox; | 93 | Mailbox mailbox; |
| @@ -104,11 +95,13 @@ private: | |||
| 104 | std::jthread main_thread{}; | 95 | std::jthread main_thread{}; |
| 105 | /// The current state | 96 | /// The current state |
| 106 | std::atomic<bool> running{}; | 97 | std::atomic<bool> running{}; |
| 98 | /// Shared memory of input command buffers, set by host, read by DSP | ||
| 107 | std::array<CommandBuffer, MaxRendererSessions> command_buffers{}; | 99 | std::array<CommandBuffer, MaxRendererSessions> command_buffers{}; |
| 108 | /// The command lists to process | 100 | /// The command lists to process |
| 109 | std::array<CommandListProcessor, MaxRendererSessions> command_list_processors{}; | 101 | std::array<CommandListProcessor, MaxRendererSessions> command_list_processors{}; |
| 110 | /// The streams which will receive the processed samples | 102 | /// The streams which will receive the processed samples |
| 111 | std::array<Sink::SinkStream*, MaxRendererSessions> streams{}; | 103 | std::array<Sink::SinkStream*, MaxRendererSessions> streams{}; |
| 104 | /// CPU Tick when the DSP was signalled to process, uses time rather than tick | ||
| 112 | u64 signalled_tick{0}; | 105 | u64 signalled_tick{0}; |
| 113 | }; | 106 | }; |
| 114 | 107 | ||
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 000000000..2c16d3769 --- /dev/null +++ b/src/audio_core/adsp/apps/opus/opus_decode_object.cpp | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/adsp/apps/opus/opus_decode_object.h" | ||
| 5 | #include "common/assert.h" | ||
| 6 | |||
| 7 | namespace AudioCore::ADSP::OpusDecoder { | ||
| 8 | namespace { | ||
| 9 | bool IsValidChannelCount(u32 channel_count) { | ||
| 10 | return channel_count == 1 || channel_count == 2; | ||
| 11 | } | ||
| 12 | } // namespace | ||
| 13 | |||
| 14 | u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) { | ||
| 15 | if (!IsValidChannelCount(channel_count)) { | ||
| 16 | return 0; | ||
| 17 | } | ||
| 18 | return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count); | ||
| 19 | } | ||
| 20 | |||
| 21 | OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) { | ||
| 22 | auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer); | ||
| 23 | auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2); | ||
| 24 | |||
| 25 | if (new_decoder->magic == DecodeObjectMagic) { | ||
| 26 | if (!new_decoder->initialized || | ||
| 27 | (new_decoder->initialized && new_decoder->self == comparison)) { | ||
| 28 | new_decoder->state_valid = true; | ||
| 29 | } | ||
| 30 | } else { | ||
| 31 | new_decoder->initialized = false; | ||
| 32 | new_decoder->state_valid = true; | ||
| 33 | } | ||
| 34 | return *new_decoder; | ||
| 35 | } | ||
| 36 | |||
| 37 | s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) { | ||
| 38 | if (!state_valid) { | ||
| 39 | return OPUS_INVALID_STATE; | ||
| 40 | } | ||
| 41 | |||
| 42 | if (initialized) { | ||
| 43 | return OPUS_OK; | ||
| 44 | } | ||
| 45 | |||
| 46 | // Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include | ||
| 47 | // it in this class. Nintendo does not allocate memory, which is why we have a workbuffer | ||
| 48 | // provided. | ||
| 49 | // We could use _create and have libopus allocate it for us, but then we have to separately | ||
| 50 | // track which decoder is being used between this and multistream in order to call the correct | ||
| 51 | // destroy from the host side. | ||
| 52 | // This is a bit cringe, but is safe as these objects are only ever initialized inside the given | ||
| 53 | // workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow. | ||
| 54 | decoder = (LibOpusDecoder*)(this + 1); | ||
| 55 | s32 ret = opus_decoder_init(decoder, sample_rate, channel_count); | ||
| 56 | if (ret == OPUS_OK) { | ||
| 57 | magic = DecodeObjectMagic; | ||
| 58 | initialized = true; | ||
| 59 | state_valid = true; | ||
| 60 | self = this; | ||
| 61 | final_range = 0; | ||
| 62 | } | ||
| 63 | return ret; | ||
| 64 | } | ||
| 65 | |||
| 66 | s32 OpusDecodeObject::Shutdown() { | ||
| 67 | if (!state_valid) { | ||
| 68 | return OPUS_INVALID_STATE; | ||
| 69 | } | ||
| 70 | |||
| 71 | if (initialized) { | ||
| 72 | magic = 0x0; | ||
| 73 | initialized = false; | ||
| 74 | state_valid = false; | ||
| 75 | self = nullptr; | ||
| 76 | final_range = 0; | ||
| 77 | decoder = nullptr; | ||
| 78 | } | ||
| 79 | return OPUS_OK; | ||
| 80 | } | ||
| 81 | |||
| 82 | s32 OpusDecodeObject::ResetDecoder() { | ||
| 83 | return opus_decoder_ctl(decoder, OPUS_RESET_STATE); | ||
| 84 | } | ||
| 85 | |||
| 86 | s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, | ||
| 87 | u64 input_data, u64 input_data_size) { | ||
| 88 | ASSERT(initialized); | ||
| 89 | out_sample_count = 0; | ||
| 90 | |||
| 91 | if (!state_valid) { | ||
| 92 | return OPUS_INVALID_STATE; | ||
| 93 | } | ||
| 94 | |||
| 95 | auto ret_code_or_samples = opus_decode( | ||
| 96 | decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size), | ||
| 97 | reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0); | ||
| 98 | |||
| 99 | if (ret_code_or_samples < OPUS_OK) { | ||
| 100 | return ret_code_or_samples; | ||
| 101 | } | ||
| 102 | |||
| 103 | out_sample_count = ret_code_or_samples; | ||
| 104 | return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); | ||
| 105 | } | ||
| 106 | |||
| 107 | } // 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 000000000..6425f987c --- /dev/null +++ b/src/audio_core/adsp/apps/opus/opus_decode_object.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <opus.h> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | |||
| 10 | namespace AudioCore::ADSP::OpusDecoder { | ||
| 11 | using LibOpusDecoder = ::OpusDecoder; | ||
| 12 | static constexpr u32 DecodeObjectMagic = 0xDEADBEEF; | ||
| 13 | |||
| 14 | class OpusDecodeObject { | ||
| 15 | public: | ||
| 16 | static u32 GetWorkBufferSize(u32 channel_count); | ||
| 17 | static OpusDecodeObject& Initialize(u64 buffer, u64 buffer2); | ||
| 18 | |||
| 19 | s32 InitializeDecoder(u32 sample_rate, u32 channel_count); | ||
| 20 | s32 Shutdown(); | ||
| 21 | s32 ResetDecoder(); | ||
| 22 | s32 Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data, | ||
| 23 | u64 input_data_size); | ||
| 24 | u32 GetFinalRange() const noexcept { | ||
| 25 | return final_range; | ||
| 26 | } | ||
| 27 | |||
| 28 | private: | ||
| 29 | u32 magic; | ||
| 30 | bool initialized; | ||
| 31 | bool state_valid; | ||
| 32 | OpusDecodeObject* self; | ||
| 33 | u32 final_range; | ||
| 34 | LibOpusDecoder* decoder; | ||
| 35 | }; | ||
| 36 | static_assert(std::is_trivially_constructible_v<OpusDecodeObject>); | ||
| 37 | |||
| 38 | } // 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 new file mode 100644 index 000000000..2084de128 --- /dev/null +++ b/src/audio_core/adsp/apps/opus/opus_decoder.cpp | |||
| @@ -0,0 +1,269 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <array> | ||
| 5 | #include <chrono> | ||
| 6 | |||
| 7 | #include "audio_core/adsp/apps/opus/opus_decode_object.h" | ||
| 8 | #include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h" | ||
| 9 | #include "audio_core/adsp/apps/opus/shared_memory.h" | ||
| 10 | #include "audio_core/audio_core.h" | ||
| 11 | #include "audio_core/common/common.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/microprofile.h" | ||
| 14 | #include "common/thread.h" | ||
| 15 | #include "core/core.h" | ||
| 16 | #include "core/core_timing.h" | ||
| 17 | |||
| 18 | MICROPROFILE_DEFINE(OpusDecoder, "Audio", "DSP_OpusDecoder", MP_RGB(60, 19, 97)); | ||
| 19 | |||
| 20 | namespace AudioCore::ADSP::OpusDecoder { | ||
| 21 | |||
| 22 | namespace { | ||
| 23 | constexpr size_t OpusStreamCountMax = 255; | ||
| 24 | |||
| 25 | bool IsValidChannelCount(u32 channel_count) { | ||
| 26 | return channel_count == 1 || channel_count == 2; | ||
| 27 | } | ||
| 28 | |||
| 29 | bool IsValidMultiStreamChannelCount(u32 channel_count) { | ||
| 30 | return channel_count <= OpusStreamCountMax; | ||
| 31 | } | ||
| 32 | |||
| 33 | bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 sterero_stream_count) { | ||
| 34 | return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 && | ||
| 35 | sterero_stream_count > 0 && sterero_stream_count <= total_stream_count; | ||
| 36 | } | ||
| 37 | } // namespace | ||
| 38 | |||
| 39 | OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} { | ||
| 40 | init_thread = std::jthread([this](std::stop_token stop_token) { Init(stop_token); }); | ||
| 41 | } | ||
| 42 | |||
| 43 | OpusDecoder::~OpusDecoder() { | ||
| 44 | if (!running) { | ||
| 45 | init_thread.request_stop(); | ||
| 46 | return; | ||
| 47 | } | ||
| 48 | |||
| 49 | // Shutdown the thread | ||
| 50 | Send(Direction::DSP, Message::Shutdown); | ||
| 51 | auto msg = Receive(Direction::Host); | ||
| 52 | ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}", | ||
| 53 | Message::ShutdownOK, msg); | ||
| 54 | main_thread.request_stop(); | ||
| 55 | main_thread.join(); | ||
| 56 | running = false; | ||
| 57 | } | ||
| 58 | |||
| 59 | void OpusDecoder::Send(Direction dir, u32 message) { | ||
| 60 | mailbox.Send(dir, std::move(message)); | ||
| 61 | } | ||
| 62 | |||
| 63 | u32 OpusDecoder::Receive(Direction dir, std::stop_token stop_token) { | ||
| 64 | return mailbox.Receive(dir, stop_token); | ||
| 65 | } | ||
| 66 | |||
| 67 | void OpusDecoder::Init(std::stop_token stop_token) { | ||
| 68 | Common::SetCurrentThreadName("DSP_OpusDecoder_Init"); | ||
| 69 | |||
| 70 | if (Receive(Direction::DSP, stop_token) != Message::Start) { | ||
| 71 | LOG_ERROR(Service_Audio, | ||
| 72 | "DSP OpusDecoder failed to receive Start message. Opus initialization failed."); | ||
| 73 | return; | ||
| 74 | } | ||
| 75 | main_thread = std::jthread([this](std::stop_token st) { Main(st); }); | ||
| 76 | running = true; | ||
| 77 | Send(Direction::Host, Message::StartOK); | ||
| 78 | } | ||
| 79 | |||
| 80 | void OpusDecoder::Main(std::stop_token stop_token) { | ||
| 81 | Common::SetCurrentThreadName("DSP_OpusDecoder_Main"); | ||
| 82 | |||
| 83 | while (!stop_token.stop_requested()) { | ||
| 84 | auto msg = Receive(Direction::DSP, stop_token); | ||
| 85 | switch (msg) { | ||
| 86 | case Shutdown: | ||
| 87 | Send(Direction::Host, Message::ShutdownOK); | ||
| 88 | return; | ||
| 89 | |||
| 90 | case GetWorkBufferSize: { | ||
| 91 | auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]); | ||
| 92 | |||
| 93 | ASSERT(IsValidChannelCount(channel_count)); | ||
| 94 | |||
| 95 | shared_memory->dsp_return_data[0] = OpusDecodeObject::GetWorkBufferSize(channel_count); | ||
| 96 | Send(Direction::Host, Message::GetWorkBufferSizeOK); | ||
| 97 | } break; | ||
| 98 | |||
| 99 | case InitializeDecodeObject: { | ||
| 100 | auto buffer = shared_memory->host_send_data[0]; | ||
| 101 | auto buffer_size = shared_memory->host_send_data[1]; | ||
| 102 | auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]); | ||
| 103 | auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]); | ||
| 104 | |||
| 105 | ASSERT(sample_rate >= 0); | ||
| 106 | ASSERT(IsValidChannelCount(channel_count)); | ||
| 107 | ASSERT(buffer_size >= OpusDecodeObject::GetWorkBufferSize(channel_count)); | ||
| 108 | |||
| 109 | auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer); | ||
| 110 | shared_memory->dsp_return_data[0] = | ||
| 111 | decoder_object.InitializeDecoder(sample_rate, channel_count); | ||
| 112 | |||
| 113 | Send(Direction::Host, Message::InitializeDecodeObjectOK); | ||
| 114 | } break; | ||
| 115 | |||
| 116 | case ShutdownDecodeObject: { | ||
| 117 | auto buffer = shared_memory->host_send_data[0]; | ||
| 118 | [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; | ||
| 119 | |||
| 120 | auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer); | ||
| 121 | shared_memory->dsp_return_data[0] = decoder_object.Shutdown(); | ||
| 122 | |||
| 123 | Send(Direction::Host, Message::ShutdownDecodeObjectOK); | ||
| 124 | } break; | ||
| 125 | |||
| 126 | case DecodeInterleaved: { | ||
| 127 | auto start_time = system.CoreTiming().GetGlobalTimeUs(); | ||
| 128 | |||
| 129 | auto buffer = shared_memory->host_send_data[0]; | ||
| 130 | auto input_data = shared_memory->host_send_data[1]; | ||
| 131 | auto input_data_size = shared_memory->host_send_data[2]; | ||
| 132 | auto output_data = shared_memory->host_send_data[3]; | ||
| 133 | auto output_data_size = shared_memory->host_send_data[4]; | ||
| 134 | auto final_range = static_cast<u32>(shared_memory->host_send_data[5]); | ||
| 135 | auto reset_requested = shared_memory->host_send_data[6]; | ||
| 136 | |||
| 137 | u32 decoded_samples{0}; | ||
| 138 | |||
| 139 | auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer); | ||
| 140 | s32 error_code{OPUS_OK}; | ||
| 141 | if (reset_requested) { | ||
| 142 | error_code = decoder_object.ResetDecoder(); | ||
| 143 | } | ||
| 144 | |||
| 145 | if (error_code == OPUS_OK) { | ||
| 146 | error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size, | ||
| 147 | input_data, input_data_size); | ||
| 148 | } | ||
| 149 | |||
| 150 | if (error_code == OPUS_OK) { | ||
| 151 | if (final_range && decoder_object.GetFinalRange() != final_range) { | ||
| 152 | error_code = OPUS_INVALID_PACKET; | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | auto end_time = system.CoreTiming().GetGlobalTimeUs(); | ||
| 157 | shared_memory->dsp_return_data[0] = error_code; | ||
| 158 | shared_memory->dsp_return_data[1] = decoded_samples; | ||
| 159 | shared_memory->dsp_return_data[2] = (end_time - start_time).count(); | ||
| 160 | |||
| 161 | Send(Direction::Host, Message::DecodeInterleavedOK); | ||
| 162 | } break; | ||
| 163 | |||
| 164 | case MapMemory: { | ||
| 165 | [[maybe_unused]] auto buffer = shared_memory->host_send_data[0]; | ||
| 166 | [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; | ||
| 167 | Send(Direction::Host, Message::MapMemoryOK); | ||
| 168 | } break; | ||
| 169 | |||
| 170 | case UnmapMemory: { | ||
| 171 | [[maybe_unused]] auto buffer = shared_memory->host_send_data[0]; | ||
| 172 | [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; | ||
| 173 | Send(Direction::Host, Message::UnmapMemoryOK); | ||
| 174 | } break; | ||
| 175 | |||
| 176 | case GetWorkBufferSizeForMultiStream: { | ||
| 177 | auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]); | ||
| 178 | auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]); | ||
| 179 | |||
| 180 | ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count)); | ||
| 181 | |||
| 182 | shared_memory->dsp_return_data[0] = OpusMultiStreamDecodeObject::GetWorkBufferSize( | ||
| 183 | total_stream_count, stereo_stream_count); | ||
| 184 | Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK); | ||
| 185 | } break; | ||
| 186 | |||
| 187 | case InitializeMultiStreamDecodeObject: { | ||
| 188 | auto buffer = shared_memory->host_send_data[0]; | ||
| 189 | auto buffer_size = shared_memory->host_send_data[1]; | ||
| 190 | auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]); | ||
| 191 | auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]); | ||
| 192 | auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[4]); | ||
| 193 | auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[5]); | ||
| 194 | // Nintendo seem to have a bug here, they try to use &host_send_data[6] for the channel | ||
| 195 | // mappings, but [6] is never set, and there is not enough room in the argument data for | ||
| 196 | // more than 40 channels, when 255 are possible. | ||
| 197 | // It also means the mapping values are undefined, though likely always 0, | ||
| 198 | // and the mappings given by the game are ignored. The mappings are copied to this | ||
| 199 | // dedicated buffer host side, so let's do as intended. | ||
| 200 | auto mappings = shared_memory->channel_mapping.data(); | ||
| 201 | |||
| 202 | ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count)); | ||
| 203 | ASSERT(sample_rate >= 0); | ||
| 204 | ASSERT(buffer_size >= OpusMultiStreamDecodeObject::GetWorkBufferSize( | ||
| 205 | total_stream_count, stereo_stream_count)); | ||
| 206 | |||
| 207 | auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer); | ||
| 208 | shared_memory->dsp_return_data[0] = decoder_object.InitializeDecoder( | ||
| 209 | sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings); | ||
| 210 | |||
| 211 | Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK); | ||
| 212 | } break; | ||
| 213 | |||
| 214 | case ShutdownMultiStreamDecodeObject: { | ||
| 215 | auto buffer = shared_memory->host_send_data[0]; | ||
| 216 | [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; | ||
| 217 | |||
| 218 | auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer); | ||
| 219 | shared_memory->dsp_return_data[0] = decoder_object.Shutdown(); | ||
| 220 | |||
| 221 | Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK); | ||
| 222 | } break; | ||
| 223 | |||
| 224 | case DecodeInterleavedForMultiStream: { | ||
| 225 | auto start_time = system.CoreTiming().GetGlobalTimeUs(); | ||
| 226 | |||
| 227 | auto buffer = shared_memory->host_send_data[0]; | ||
| 228 | auto input_data = shared_memory->host_send_data[1]; | ||
| 229 | auto input_data_size = shared_memory->host_send_data[2]; | ||
| 230 | auto output_data = shared_memory->host_send_data[3]; | ||
| 231 | auto output_data_size = shared_memory->host_send_data[4]; | ||
| 232 | auto final_range = static_cast<u32>(shared_memory->host_send_data[5]); | ||
| 233 | auto reset_requested = shared_memory->host_send_data[6]; | ||
| 234 | |||
| 235 | u32 decoded_samples{0}; | ||
| 236 | |||
| 237 | auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer); | ||
| 238 | s32 error_code{OPUS_OK}; | ||
| 239 | if (reset_requested) { | ||
| 240 | error_code = decoder_object.ResetDecoder(); | ||
| 241 | } | ||
| 242 | |||
| 243 | if (error_code == OPUS_OK) { | ||
| 244 | error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size, | ||
| 245 | input_data, input_data_size); | ||
| 246 | } | ||
| 247 | |||
| 248 | if (error_code == OPUS_OK) { | ||
| 249 | if (final_range && decoder_object.GetFinalRange() != final_range) { | ||
| 250 | error_code = OPUS_INVALID_PACKET; | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | auto end_time = system.CoreTiming().GetGlobalTimeUs(); | ||
| 255 | shared_memory->dsp_return_data[0] = error_code; | ||
| 256 | shared_memory->dsp_return_data[1] = decoded_samples; | ||
| 257 | shared_memory->dsp_return_data[2] = (end_time - start_time).count(); | ||
| 258 | |||
| 259 | Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK); | ||
| 260 | } break; | ||
| 261 | |||
| 262 | default: | ||
| 263 | LOG_ERROR(Service_Audio, "Invalid OpusDecoder command {}", msg); | ||
| 264 | continue; | ||
| 265 | } | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | } // 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 new file mode 100644 index 000000000..fcb89bb40 --- /dev/null +++ b/src/audio_core/adsp/apps/opus/opus_decoder.h | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | #include <thread> | ||
| 8 | |||
| 9 | #include "audio_core/adsp/apps/opus/shared_memory.h" | ||
| 10 | #include "audio_core/adsp/mailbox.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | |||
| 13 | namespace Core { | ||
| 14 | class System; | ||
| 15 | } // namespace Core | ||
| 16 | |||
| 17 | namespace AudioCore::ADSP::OpusDecoder { | ||
| 18 | |||
| 19 | enum Message : u32 { | ||
| 20 | Invalid = 0, | ||
| 21 | Start = 1, | ||
| 22 | Shutdown = 2, | ||
| 23 | StartOK = 11, | ||
| 24 | ShutdownOK = 12, | ||
| 25 | GetWorkBufferSize = 21, | ||
| 26 | InitializeDecodeObject = 22, | ||
| 27 | ShutdownDecodeObject = 23, | ||
| 28 | DecodeInterleaved = 24, | ||
| 29 | MapMemory = 25, | ||
| 30 | UnmapMemory = 26, | ||
| 31 | GetWorkBufferSizeForMultiStream = 27, | ||
| 32 | InitializeMultiStreamDecodeObject = 28, | ||
| 33 | ShutdownMultiStreamDecodeObject = 29, | ||
| 34 | DecodeInterleavedForMultiStream = 30, | ||
| 35 | |||
| 36 | GetWorkBufferSizeOK = 41, | ||
| 37 | InitializeDecodeObjectOK = 42, | ||
| 38 | ShutdownDecodeObjectOK = 43, | ||
| 39 | DecodeInterleavedOK = 44, | ||
| 40 | MapMemoryOK = 45, | ||
| 41 | UnmapMemoryOK = 46, | ||
| 42 | GetWorkBufferSizeForMultiStreamOK = 47, | ||
| 43 | InitializeMultiStreamDecodeObjectOK = 48, | ||
| 44 | ShutdownMultiStreamDecodeObjectOK = 49, | ||
| 45 | DecodeInterleavedForMultiStreamOK = 50, | ||
| 46 | }; | ||
| 47 | |||
| 48 | /** | ||
| 49 | * The AudioRenderer application running on the ADSP. | ||
| 50 | */ | ||
| 51 | class OpusDecoder { | ||
| 52 | public: | ||
| 53 | explicit OpusDecoder(Core::System& system); | ||
| 54 | ~OpusDecoder(); | ||
| 55 | |||
| 56 | bool IsRunning() const noexcept { | ||
| 57 | return running; | ||
| 58 | } | ||
| 59 | |||
| 60 | void Send(Direction dir, u32 message); | ||
| 61 | u32 Receive(Direction dir, std::stop_token stop_token = {}); | ||
| 62 | |||
| 63 | void SetSharedMemory(SharedMemory& shared_memory_) { | ||
| 64 | shared_memory = &shared_memory_; | ||
| 65 | } | ||
| 66 | |||
| 67 | private: | ||
| 68 | /** | ||
| 69 | * Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread. | ||
| 70 | */ | ||
| 71 | void Init(std::stop_token stop_token); | ||
| 72 | /** | ||
| 73 | * Main OpusDecoder thread, responsible for processing the incoming Opus packets. | ||
| 74 | */ | ||
| 75 | void Main(std::stop_token stop_token); | ||
| 76 | |||
| 77 | /// Core system | ||
| 78 | Core::System& system; | ||
| 79 | /// Mailbox to communicate messages with the host, drives the main thread | ||
| 80 | Mailbox mailbox; | ||
| 81 | /// Init thread | ||
| 82 | std::jthread init_thread{}; | ||
| 83 | /// Main thread | ||
| 84 | std::jthread main_thread{}; | ||
| 85 | /// The current state | ||
| 86 | bool running{}; | ||
| 87 | /// Structure shared with the host, input data set by the host before sending a mailbox message, | ||
| 88 | /// and the responses are written back by the OpusDecoder. | ||
| 89 | SharedMemory* shared_memory{}; | ||
| 90 | }; | ||
| 91 | |||
| 92 | } // 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 000000000..f6d362e68 --- /dev/null +++ b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h" | ||
| 5 | #include "common/assert.h" | ||
| 6 | |||
| 7 | namespace AudioCore::ADSP::OpusDecoder { | ||
| 8 | |||
| 9 | namespace { | ||
| 10 | bool IsValidChannelCount(u32 channel_count) { | ||
| 11 | return channel_count == 1 || channel_count == 2; | ||
| 12 | } | ||
| 13 | |||
| 14 | bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) { | ||
| 15 | return total_stream_count > 0 && stereo_stream_count > 0 && | ||
| 16 | stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count); | ||
| 17 | } | ||
| 18 | } // namespace | ||
| 19 | |||
| 20 | u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count, | ||
| 21 | u32 stereo_stream_count) { | ||
| 22 | if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) { | ||
| 23 | return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) + | ||
| 24 | opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count); | ||
| 25 | } | ||
| 26 | return 0; | ||
| 27 | } | ||
| 28 | |||
| 29 | OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) { | ||
| 30 | auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer); | ||
| 31 | auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2); | ||
| 32 | |||
| 33 | if (new_decoder->magic == DecodeMultiStreamObjectMagic) { | ||
| 34 | if (!new_decoder->initialized || | ||
| 35 | (new_decoder->initialized && new_decoder->self == comparison)) { | ||
| 36 | new_decoder->state_valid = true; | ||
| 37 | } | ||
| 38 | } else { | ||
| 39 | new_decoder->initialized = false; | ||
| 40 | new_decoder->state_valid = true; | ||
| 41 | } | ||
| 42 | return *new_decoder; | ||
| 43 | } | ||
| 44 | |||
| 45 | s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count, | ||
| 46 | u32 channel_count, u32 stereo_stream_count, | ||
| 47 | u8* mappings) { | ||
| 48 | if (!state_valid) { | ||
| 49 | return OPUS_INVALID_STATE; | ||
| 50 | } | ||
| 51 | |||
| 52 | if (initialized) { | ||
| 53 | return OPUS_OK; | ||
| 54 | } | ||
| 55 | |||
| 56 | // See OpusDecodeObject::InitializeDecoder for an explanation of this | ||
| 57 | decoder = (LibOpusMSDecoder*)(this + 1); | ||
| 58 | s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count, | ||
| 59 | stereo_stream_count, mappings); | ||
| 60 | if (ret == OPUS_OK) { | ||
| 61 | magic = DecodeMultiStreamObjectMagic; | ||
| 62 | initialized = true; | ||
| 63 | state_valid = true; | ||
| 64 | self = this; | ||
| 65 | final_range = 0; | ||
| 66 | } | ||
| 67 | return ret; | ||
| 68 | } | ||
| 69 | |||
| 70 | s32 OpusMultiStreamDecodeObject::Shutdown() { | ||
| 71 | if (!state_valid) { | ||
| 72 | return OPUS_INVALID_STATE; | ||
| 73 | } | ||
| 74 | |||
| 75 | if (initialized) { | ||
| 76 | magic = 0x0; | ||
| 77 | initialized = false; | ||
| 78 | state_valid = false; | ||
| 79 | self = nullptr; | ||
| 80 | final_range = 0; | ||
| 81 | decoder = nullptr; | ||
| 82 | } | ||
| 83 | return OPUS_OK; | ||
| 84 | } | ||
| 85 | |||
| 86 | s32 OpusMultiStreamDecodeObject::ResetDecoder() { | ||
| 87 | return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE); | ||
| 88 | } | ||
| 89 | |||
| 90 | s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data, | ||
| 91 | u64 output_data_size, u64 input_data, u64 input_data_size) { | ||
| 92 | ASSERT(initialized); | ||
| 93 | out_sample_count = 0; | ||
| 94 | |||
| 95 | if (!state_valid) { | ||
| 96 | return OPUS_INVALID_STATE; | ||
| 97 | } | ||
| 98 | |||
| 99 | auto ret_code_or_samples = opus_multistream_decode( | ||
| 100 | decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size), | ||
| 101 | reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0); | ||
| 102 | |||
| 103 | if (ret_code_or_samples < OPUS_OK) { | ||
| 104 | return ret_code_or_samples; | ||
| 105 | } | ||
| 106 | |||
| 107 | out_sample_count = ret_code_or_samples; | ||
| 108 | return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); | ||
| 109 | } | ||
| 110 | |||
| 111 | } // 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 000000000..93558ded5 --- /dev/null +++ b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <opus_multistream.h> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | |||
| 10 | namespace AudioCore::ADSP::OpusDecoder { | ||
| 11 | using LibOpusMSDecoder = ::OpusMSDecoder; | ||
| 12 | static constexpr u32 DecodeMultiStreamObjectMagic = 0xDEADBEEF; | ||
| 13 | |||
| 14 | class OpusMultiStreamDecodeObject { | ||
| 15 | public: | ||
| 16 | static u32 GetWorkBufferSize(u32 total_stream_count, u32 stereo_stream_count); | ||
| 17 | static OpusMultiStreamDecodeObject& Initialize(u64 buffer, u64 buffer2); | ||
| 18 | |||
| 19 | s32 InitializeDecoder(u32 sample_rate, u32 total_stream_count, u32 channel_count, | ||
| 20 | u32 stereo_stream_count, u8* mappings); | ||
| 21 | s32 Shutdown(); | ||
| 22 | s32 ResetDecoder(); | ||
| 23 | s32 Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data, | ||
| 24 | u64 input_data_size); | ||
| 25 | u32 GetFinalRange() const noexcept { | ||
| 26 | return final_range; | ||
| 27 | } | ||
| 28 | |||
| 29 | private: | ||
| 30 | u32 magic; | ||
| 31 | bool initialized; | ||
| 32 | bool state_valid; | ||
| 33 | OpusMultiStreamDecodeObject* self; | ||
| 34 | u32 final_range; | ||
| 35 | LibOpusMSDecoder* decoder; | ||
| 36 | }; | ||
| 37 | static_assert(std::is_trivially_constructible_v<OpusMultiStreamDecodeObject>); | ||
| 38 | |||
| 39 | } // namespace AudioCore::ADSP::OpusDecoder | ||
diff --git a/src/audio_core/adsp/apps/opus/shared_memory.h b/src/audio_core/adsp/apps/opus/shared_memory.h new file mode 100644 index 000000000..c696731ed --- /dev/null +++ b/src/audio_core/adsp/apps/opus/shared_memory.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_funcs.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace AudioCore::ADSP::OpusDecoder { | ||
| 10 | |||
| 11 | struct SharedMemory { | ||
| 12 | std::array<u8, 0x100> channel_mapping{}; | ||
| 13 | std::array<u64, 16> host_send_data{}; | ||
| 14 | std::array<u64, 16> dsp_return_data{}; | ||
| 15 | }; | ||
| 16 | |||
| 17 | } // namespace AudioCore::ADSP::OpusDecoder | ||
diff --git a/src/audio_core/adsp/mailbox.h b/src/audio_core/adsp/mailbox.h index c31b73717..1dd40ebfa 100644 --- a/src/audio_core/adsp/mailbox.h +++ b/src/audio_core/adsp/mailbox.h | |||
| @@ -3,6 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <span> | ||
| 7 | |||
| 6 | #include "common/bounded_threadsafe_queue.h" | 8 | #include "common/bounded_threadsafe_queue.h" |
| 7 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 8 | 10 | ||
| @@ -19,11 +21,6 @@ enum class Direction : u32 { | |||
| 19 | DSP, | 21 | DSP, |
| 20 | }; | 22 | }; |
| 21 | 23 | ||
| 22 | struct MailboxMessage { | ||
| 23 | u32 msg; | ||
| 24 | std::span<u8> data; | ||
| 25 | }; | ||
| 26 | |||
| 27 | class Mailbox { | 24 | class Mailbox { |
| 28 | public: | 25 | public: |
| 29 | void Initialize(AppMailboxId id_) { | 26 | void Initialize(AppMailboxId id_) { |
| @@ -35,25 +32,19 @@ public: | |||
| 35 | return id; | 32 | return id; |
| 36 | } | 33 | } |
| 37 | 34 | ||
| 38 | void Send(Direction dir, MailboxMessage&& message) { | 35 | void Send(Direction dir, u32 message) { |
| 39 | auto& queue = dir == Direction::Host ? host_queue : adsp_queue; | 36 | auto& queue = dir == Direction::Host ? host_queue : adsp_queue; |
| 40 | queue.EmplaceWait(std::move(message)); | 37 | queue.EmplaceWait(message); |
| 41 | } | 38 | } |
| 42 | 39 | ||
| 43 | MailboxMessage Receive(Direction dir, bool block = true) { | 40 | u32 Receive(Direction dir, std::stop_token stop_token = {}) { |
| 44 | auto& queue = dir == Direction::Host ? host_queue : adsp_queue; | 41 | auto& queue = dir == Direction::Host ? host_queue : adsp_queue; |
| 45 | MailboxMessage t; | 42 | return queue.PopWait(stop_token); |
| 46 | if (block) { | ||
| 47 | queue.PopWait(t); | ||
| 48 | } else { | ||
| 49 | queue.TryPop(t); | ||
| 50 | } | ||
| 51 | return t; | ||
| 52 | } | 43 | } |
| 53 | 44 | ||
| 54 | void Reset() { | 45 | void Reset() { |
| 55 | id = AppMailboxId::Invalid; | 46 | id = AppMailboxId::Invalid; |
| 56 | MailboxMessage t; | 47 | u32 t{}; |
| 57 | while (host_queue.TryPop(t)) { | 48 | while (host_queue.TryPop(t)) { |
| 58 | } | 49 | } |
| 59 | while (adsp_queue.TryPop(t)) { | 50 | while (adsp_queue.TryPop(t)) { |
| @@ -62,8 +53,8 @@ public: | |||
| 62 | 53 | ||
| 63 | private: | 54 | private: |
| 64 | AppMailboxId id{0}; | 55 | AppMailboxId id{0}; |
| 65 | Common::SPSCQueue<MailboxMessage> host_queue; | 56 | Common::SPSCQueue<u32> host_queue; |
| 66 | Common::SPSCQueue<MailboxMessage> adsp_queue; | 57 | Common::SPSCQueue<u32> adsp_queue; |
| 67 | }; | 58 | }; |
| 68 | 59 | ||
| 69 | } // namespace AudioCore::ADSP | 60 | } // namespace AudioCore::ADSP |
diff --git a/src/audio_core/opus/decoder.cpp b/src/audio_core/opus/decoder.cpp new file mode 100644 index 000000000..5b23fce14 --- /dev/null +++ b/src/audio_core/opus/decoder.cpp | |||
| @@ -0,0 +1,179 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/opus/decoder.h" | ||
| 5 | #include "audio_core/opus/hardware_opus.h" | ||
| 6 | #include "audio_core/opus/parameters.h" | ||
| 7 | #include "common/alignment.h" | ||
| 8 | #include "common/swap.h" | ||
| 9 | #include "core/core.h" | ||
| 10 | |||
| 11 | namespace AudioCore::OpusDecoder { | ||
| 12 | using namespace Service::Audio; | ||
| 13 | namespace { | ||
| 14 | OpusPacketHeader ReverseHeader(OpusPacketHeader header) { | ||
| 15 | OpusPacketHeader out; | ||
| 16 | out.size = Common::swap32(header.size); | ||
| 17 | out.final_range = Common::swap32(header.final_range); | ||
| 18 | return out; | ||
| 19 | } | ||
| 20 | } // namespace | ||
| 21 | |||
| 22 | OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_) | ||
| 23 | : system{system_}, hardware_opus{hardware_opus_} {} | ||
| 24 | |||
| 25 | OpusDecoder::~OpusDecoder() { | ||
| 26 | if (decode_object_initialized) { | ||
| 27 | hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, | ||
| 32 | u64 transfer_memory_size) { | ||
| 33 | auto frame_size{params.use_large_frame_size ? 5760 : 1920}; | ||
| 34 | shared_buffer_size = transfer_memory_size; | ||
| 35 | shared_buffer = std::make_unique<u8[]>(shared_buffer_size); | ||
| 36 | shared_memory_mapped = true; | ||
| 37 | |||
| 38 | buffer_size = | ||
| 39 | Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); | ||
| 40 | |||
| 41 | out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; | ||
| 42 | size_t in_data_size{0x600u}; | ||
| 43 | in_data = {out_data.data() - in_data_size, in_data_size}; | ||
| 44 | |||
| 45 | ON_RESULT_FAILURE { | ||
| 46 | if (shared_memory_mapped) { | ||
| 47 | shared_memory_mapped = false; | ||
| 48 | ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); | ||
| 49 | } | ||
| 50 | }; | ||
| 51 | |||
| 52 | R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count, | ||
| 53 | shared_buffer.get(), shared_buffer_size)); | ||
| 54 | |||
| 55 | sample_rate = params.sample_rate; | ||
| 56 | channel_count = params.channel_count; | ||
| 57 | use_large_frame_size = params.use_large_frame_size; | ||
| 58 | decode_object_initialized = true; | ||
| 59 | R_SUCCEED(); | ||
| 60 | } | ||
| 61 | |||
| 62 | Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params, | ||
| 63 | Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) { | ||
| 64 | auto frame_size{params.use_large_frame_size ? 5760 : 1920}; | ||
| 65 | shared_buffer_size = transfer_memory_size; | ||
| 66 | shared_buffer = std::make_unique<u8[]>(shared_buffer_size); | ||
| 67 | shared_memory_mapped = true; | ||
| 68 | |||
| 69 | buffer_size = | ||
| 70 | Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); | ||
| 71 | |||
| 72 | out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; | ||
| 73 | size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)}; | ||
| 74 | in_data = {out_data.data() - in_data_size, in_data_size}; | ||
| 75 | |||
| 76 | ON_RESULT_FAILURE { | ||
| 77 | if (shared_memory_mapped) { | ||
| 78 | shared_memory_mapped = false; | ||
| 79 | ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | |||
| 83 | R_TRY(hardware_opus.InitializeMultiStreamDecodeObject( | ||
| 84 | params.sample_rate, params.channel_count, params.total_stream_count, | ||
| 85 | params.stereo_stream_count, params.mappings.data(), shared_buffer.get(), | ||
| 86 | shared_buffer_size)); | ||
| 87 | |||
| 88 | sample_rate = params.sample_rate; | ||
| 89 | channel_count = params.channel_count; | ||
| 90 | total_stream_count = params.total_stream_count; | ||
| 91 | stereo_stream_count = params.stereo_stream_count; | ||
| 92 | use_large_frame_size = params.use_large_frame_size; | ||
| 93 | decode_object_initialized = true; | ||
| 94 | R_SUCCEED(); | ||
| 95 | } | ||
| 96 | |||
| 97 | Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken, | ||
| 98 | u32* out_sample_count, std::span<const u8> input_data, | ||
| 99 | std::span<u8> output_data, bool reset) { | ||
| 100 | u32 out_samples; | ||
| 101 | u64 time_taken{}; | ||
| 102 | |||
| 103 | R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); | ||
| 104 | |||
| 105 | auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; | ||
| 106 | OpusPacketHeader header{ReverseHeader(*header_p)}; | ||
| 107 | |||
| 108 | R_UNLESS(in_data.size_bytes() >= header.size && | ||
| 109 | header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), | ||
| 110 | ResultBufferTooSmall); | ||
| 111 | |||
| 112 | if (!shared_memory_mapped) { | ||
| 113 | R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); | ||
| 114 | shared_memory_mapped = true; | ||
| 115 | } | ||
| 116 | |||
| 117 | std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); | ||
| 118 | |||
| 119 | R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(), | ||
| 120 | channel_count, in_data.data(), header.size, | ||
| 121 | shared_buffer.get(), time_taken, reset)); | ||
| 122 | |||
| 123 | std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); | ||
| 124 | |||
| 125 | *out_data_size = header.size + sizeof(OpusPacketHeader); | ||
| 126 | *out_sample_count = out_samples; | ||
| 127 | if (out_time_taken) { | ||
| 128 | *out_time_taken = time_taken / 1000; | ||
| 129 | } | ||
| 130 | R_SUCCEED(); | ||
| 131 | } | ||
| 132 | |||
| 133 | Result OpusDecoder::SetContext([[maybe_unused]] std::span<const u8> context) { | ||
| 134 | R_SUCCEED_IF(shared_memory_mapped); | ||
| 135 | shared_memory_mapped = true; | ||
| 136 | R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); | ||
| 137 | } | ||
| 138 | |||
| 139 | Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, | ||
| 140 | u32* out_sample_count, | ||
| 141 | std::span<const u8> input_data, | ||
| 142 | std::span<u8> output_data, bool reset) { | ||
| 143 | u32 out_samples; | ||
| 144 | u64 time_taken{}; | ||
| 145 | |||
| 146 | R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); | ||
| 147 | |||
| 148 | auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; | ||
| 149 | OpusPacketHeader header{ReverseHeader(*header_p)}; | ||
| 150 | |||
| 151 | LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}", | ||
| 152 | header.size, input_data.size_bytes(), in_data.size_bytes()); | ||
| 153 | |||
| 154 | R_UNLESS(in_data.size_bytes() >= header.size && | ||
| 155 | header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), | ||
| 156 | ResultBufferTooSmall); | ||
| 157 | |||
| 158 | if (!shared_memory_mapped) { | ||
| 159 | R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); | ||
| 160 | shared_memory_mapped = true; | ||
| 161 | } | ||
| 162 | |||
| 163 | std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); | ||
| 164 | |||
| 165 | R_TRY(hardware_opus.DecodeInterleavedForMultiStream( | ||
| 166 | out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(), | ||
| 167 | header.size, shared_buffer.get(), time_taken, reset)); | ||
| 168 | |||
| 169 | std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); | ||
| 170 | |||
| 171 | *out_data_size = header.size + sizeof(OpusPacketHeader); | ||
| 172 | *out_sample_count = out_samples; | ||
| 173 | if (out_time_taken) { | ||
| 174 | *out_time_taken = time_taken / 1000; | ||
| 175 | } | ||
| 176 | R_SUCCEED(); | ||
| 177 | } | ||
| 178 | |||
| 179 | } // namespace AudioCore::OpusDecoder | ||
diff --git a/src/audio_core/opus/decoder.h b/src/audio_core/opus/decoder.h new file mode 100644 index 000000000..d08d8a4a4 --- /dev/null +++ b/src/audio_core/opus/decoder.h | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <span> | ||
| 7 | |||
| 8 | #include "audio_core/opus/parameters.h" | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "core/hle/kernel/k_transfer_memory.h" | ||
| 11 | #include "core/hle/service/audio/errors.h" | ||
| 12 | |||
| 13 | namespace Core { | ||
| 14 | class System; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace AudioCore::OpusDecoder { | ||
| 18 | class HardwareOpus; | ||
| 19 | |||
| 20 | class OpusDecoder { | ||
| 21 | public: | ||
| 22 | explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_); | ||
| 23 | ~OpusDecoder(); | ||
| 24 | |||
| 25 | Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, | ||
| 26 | u64 transfer_memory_size); | ||
| 27 | Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, | ||
| 28 | u64 transfer_memory_size); | ||
| 29 | Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count, | ||
| 30 | std::span<const u8> input_data, std::span<u8> output_data, bool reset); | ||
| 31 | Result SetContext([[maybe_unused]] std::span<const u8> context); | ||
| 32 | Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, | ||
| 33 | u32* out_sample_count, std::span<const u8> input_data, | ||
| 34 | std::span<u8> output_data, bool reset); | ||
| 35 | |||
| 36 | private: | ||
| 37 | Core::System& system; | ||
| 38 | HardwareOpus& hardware_opus; | ||
| 39 | std::unique_ptr<u8[]> shared_buffer{}; | ||
| 40 | u64 shared_buffer_size; | ||
| 41 | std::span<u8> in_data{}; | ||
| 42 | std::span<u8> out_data{}; | ||
| 43 | u64 buffer_size{}; | ||
| 44 | s32 sample_rate{}; | ||
| 45 | s32 channel_count{}; | ||
| 46 | bool use_large_frame_size{false}; | ||
| 47 | s32 total_stream_count{}; | ||
| 48 | s32 stereo_stream_count{}; | ||
| 49 | bool shared_memory_mapped{false}; | ||
| 50 | bool decode_object_initialized{false}; | ||
| 51 | }; | ||
| 52 | |||
| 53 | } // namespace AudioCore::OpusDecoder | ||
diff --git a/src/audio_core/opus/decoder_manager.cpp b/src/audio_core/opus/decoder_manager.cpp new file mode 100644 index 000000000..4a5382973 --- /dev/null +++ b/src/audio_core/opus/decoder_manager.cpp | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/adsp/apps/opus/opus_decoder.h" | ||
| 5 | #include "audio_core/opus/decoder_manager.h" | ||
| 6 | #include "common/alignment.h" | ||
| 7 | #include "core/core.h" | ||
| 8 | |||
| 9 | namespace AudioCore::OpusDecoder { | ||
| 10 | using namespace Service::Audio; | ||
| 11 | |||
| 12 | namespace { | ||
| 13 | bool IsValidChannelCount(u32 channel_count) { | ||
| 14 | return channel_count == 1 || channel_count == 2; | ||
| 15 | } | ||
| 16 | |||
| 17 | bool IsValidMultiStreamChannelCount(u32 channel_count) { | ||
| 18 | return channel_count > 0 && channel_count <= OpusStreamCountMax; | ||
| 19 | } | ||
| 20 | |||
| 21 | bool IsValidSampleRate(u32 sample_rate) { | ||
| 22 | return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 || | ||
| 23 | sample_rate == 24'000 || sample_rate == 48'000; | ||
| 24 | } | ||
| 25 | |||
| 26 | bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) { | ||
| 27 | return total_stream_count > 0 && stereo_stream_count > 0 && | ||
| 28 | stereo_stream_count <= total_stream_count && | ||
| 29 | total_stream_count + stereo_stream_count <= channel_count; | ||
| 30 | } | ||
| 31 | |||
| 32 | } // namespace | ||
| 33 | |||
| 34 | OpusDecoderManager::OpusDecoderManager(Core::System& system_) | ||
| 35 | : system{system_}, hardware_opus{system} { | ||
| 36 | for (u32 i = 0; i < MaxChannels; i++) { | ||
| 37 | required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i); | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) { | ||
| 42 | OpusParametersEx ex{ | ||
| 43 | .sample_rate = params.sample_rate, | ||
| 44 | .channel_count = params.channel_count, | ||
| 45 | .use_large_frame_size = false, | ||
| 46 | }; | ||
| 47 | R_RETURN(GetWorkBufferSizeExEx(ex, out_size)); | ||
| 48 | } | ||
| 49 | |||
| 50 | Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) { | ||
| 51 | R_RETURN(GetWorkBufferSizeExEx(params, out_size)); | ||
| 52 | } | ||
| 53 | |||
| 54 | Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) { | ||
| 55 | R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount); | ||
| 56 | R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); | ||
| 57 | |||
| 58 | auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]}; | ||
| 59 | auto frame_size{params.use_large_frame_size ? 5760 : 1920}; | ||
| 60 | work_buffer_size += | ||
| 61 | Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); | ||
| 62 | out_size = work_buffer_size + 0x600; | ||
| 63 | R_SUCCEED(); | ||
| 64 | } | ||
| 65 | |||
| 66 | Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, | ||
| 67 | u64& out_size) { | ||
| 68 | OpusMultiStreamParametersEx ex{ | ||
| 69 | .sample_rate = params.sample_rate, | ||
| 70 | .channel_count = params.channel_count, | ||
| 71 | .total_stream_count = params.total_stream_count, | ||
| 72 | .stereo_stream_count = params.stereo_stream_count, | ||
| 73 | .use_large_frame_size = false, | ||
| 74 | .mappings = {}, | ||
| 75 | }; | ||
| 76 | R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size)); | ||
| 77 | } | ||
| 78 | |||
| 79 | Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, | ||
| 80 | u64& out_size) { | ||
| 81 | R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size)); | ||
| 82 | } | ||
| 83 | |||
| 84 | Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, | ||
| 85 | u64& out_size) { | ||
| 86 | R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount); | ||
| 87 | R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); | ||
| 88 | R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count, | ||
| 89 | params.stereo_stream_count), | ||
| 90 | ResultInvalidOpusSampleRate); | ||
| 91 | |||
| 92 | auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream( | ||
| 93 | params.total_stream_count, params.stereo_stream_count)}; | ||
| 94 | auto frame_size{params.use_large_frame_size ? 5760 : 1920}; | ||
| 95 | work_buffer_size += Common::AlignUp(1500 * params.total_stream_count, 64); | ||
| 96 | work_buffer_size += | ||
| 97 | Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); | ||
| 98 | out_size = work_buffer_size; | ||
| 99 | R_SUCCEED(); | ||
| 100 | } | ||
| 101 | |||
| 102 | } // namespace AudioCore::OpusDecoder | ||
diff --git a/src/audio_core/opus/decoder_manager.h b/src/audio_core/opus/decoder_manager.h new file mode 100644 index 000000000..466e1967b --- /dev/null +++ b/src/audio_core/opus/decoder_manager.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "audio_core/opus/hardware_opus.h" | ||
| 7 | #include "audio_core/opus/parameters.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "core/hle/service/audio/errors.h" | ||
| 10 | |||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace AudioCore::OpusDecoder { | ||
| 16 | |||
| 17 | class OpusDecoderManager { | ||
| 18 | public: | ||
| 19 | OpusDecoderManager(Core::System& system); | ||
| 20 | |||
| 21 | HardwareOpus& GetHardwareOpus() { | ||
| 22 | return hardware_opus; | ||
| 23 | } | ||
| 24 | |||
| 25 | Result GetWorkBufferSize(OpusParameters& params, u64& out_size); | ||
| 26 | Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size); | ||
| 27 | Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size); | ||
| 28 | Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size); | ||
| 29 | Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size); | ||
| 30 | Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size); | ||
| 31 | |||
| 32 | private: | ||
| 33 | Core::System& system; | ||
| 34 | HardwareOpus hardware_opus; | ||
| 35 | std::array<u64, MaxChannels> required_workbuffer_sizes{}; | ||
| 36 | }; | ||
| 37 | |||
| 38 | } // namespace AudioCore::OpusDecoder | ||
diff --git a/src/audio_core/opus/hardware_opus.cpp b/src/audio_core/opus/hardware_opus.cpp new file mode 100644 index 000000000..d6544dcb0 --- /dev/null +++ b/src/audio_core/opus/hardware_opus.cpp | |||
| @@ -0,0 +1,241 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <array> | ||
| 5 | |||
| 6 | #include "audio_core/audio_core.h" | ||
| 7 | #include "audio_core/opus/hardware_opus.h" | ||
| 8 | #include "core/core.h" | ||
| 9 | |||
| 10 | namespace AudioCore::OpusDecoder { | ||
| 11 | namespace { | ||
| 12 | using namespace Service::Audio; | ||
| 13 | |||
| 14 | static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) { | ||
| 15 | s32 error{static_cast<s32>(error_code)}; | ||
| 16 | ASSERT(error <= OPUS_OK); | ||
| 17 | switch (error) { | ||
| 18 | case OPUS_ALLOC_FAIL: | ||
| 19 | R_THROW(ResultLibOpusAllocFail); | ||
| 20 | case OPUS_INVALID_STATE: | ||
| 21 | R_THROW(ResultLibOpusInvalidState); | ||
| 22 | case OPUS_UNIMPLEMENTED: | ||
| 23 | R_THROW(ResultLibOpusUnimplemented); | ||
| 24 | case OPUS_INVALID_PACKET: | ||
| 25 | R_THROW(ResultLibOpusInvalidPacket); | ||
| 26 | case OPUS_INTERNAL_ERROR: | ||
| 27 | R_THROW(ResultLibOpusInternalError); | ||
| 28 | case OPUS_BUFFER_TOO_SMALL: | ||
| 29 | R_THROW(ResultBufferTooSmall); | ||
| 30 | case OPUS_BAD_ARG: | ||
| 31 | R_THROW(ResultLibOpusBadArg); | ||
| 32 | case OPUS_OK: | ||
| 33 | R_RETURN(ResultSuccess); | ||
| 34 | } | ||
| 35 | UNREACHABLE(); | ||
| 36 | } | ||
| 37 | |||
| 38 | } // namespace | ||
| 39 | |||
| 40 | HardwareOpus::HardwareOpus(Core::System& system_) | ||
| 41 | : system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} { | ||
| 42 | opus_decoder.SetSharedMemory(shared_memory); | ||
| 43 | } | ||
| 44 | |||
| 45 | u64 HardwareOpus::GetWorkBufferSize(u32 channel) { | ||
| 46 | if (!opus_decoder.IsRunning()) { | ||
| 47 | return 0; | ||
| 48 | } | ||
| 49 | std::scoped_lock l{mutex}; | ||
| 50 | shared_memory.host_send_data[0] = channel; | ||
| 51 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize); | ||
| 52 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 53 | if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) { | ||
| 54 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | ||
| 55 | ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg); | ||
| 56 | return 0; | ||
| 57 | } | ||
| 58 | return shared_memory.dsp_return_data[0]; | ||
| 59 | } | ||
| 60 | |||
| 61 | u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) { | ||
| 62 | std::scoped_lock l{mutex}; | ||
| 63 | shared_memory.host_send_data[0] = total_stream_count; | ||
| 64 | shared_memory.host_send_data[1] = stereo_stream_count; | ||
| 65 | opus_decoder.Send(ADSP::Direction::DSP, | ||
| 66 | ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream); | ||
| 67 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 68 | if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) { | ||
| 69 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | ||
| 70 | ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg); | ||
| 71 | return 0; | ||
| 72 | } | ||
| 73 | return shared_memory.dsp_return_data[0]; | ||
| 74 | } | ||
| 75 | |||
| 76 | Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, | ||
| 77 | u64 buffer_size) { | ||
| 78 | std::scoped_lock l{mutex}; | ||
| 79 | shared_memory.host_send_data[0] = (u64)buffer; | ||
| 80 | shared_memory.host_send_data[1] = buffer_size; | ||
| 81 | shared_memory.host_send_data[2] = sample_rate; | ||
| 82 | shared_memory.host_send_data[3] = channel_count; | ||
| 83 | |||
| 84 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject); | ||
| 85 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 86 | if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) { | ||
| 87 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | ||
| 88 | ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg); | ||
| 89 | R_THROW(ResultInvalidOpusDSPReturnCode); | ||
| 90 | } | ||
| 91 | |||
| 92 | R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); | ||
| 93 | } | ||
| 94 | |||
| 95 | Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, | ||
| 96 | u32 total_stream_count, | ||
| 97 | u32 stereo_stream_count, void* mappings, | ||
| 98 | void* buffer, u64 buffer_size) { | ||
| 99 | std::scoped_lock l{mutex}; | ||
| 100 | shared_memory.host_send_data[0] = (u64)buffer; | ||
| 101 | shared_memory.host_send_data[1] = buffer_size; | ||
| 102 | shared_memory.host_send_data[2] = sample_rate; | ||
| 103 | shared_memory.host_send_data[3] = channel_count; | ||
| 104 | shared_memory.host_send_data[4] = total_stream_count; | ||
| 105 | shared_memory.host_send_data[5] = stereo_stream_count; | ||
| 106 | |||
| 107 | ASSERT(channel_count <= MaxChannels); | ||
| 108 | std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8)); | ||
| 109 | |||
| 110 | opus_decoder.Send(ADSP::Direction::DSP, | ||
| 111 | ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject); | ||
| 112 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 113 | if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) { | ||
| 114 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | ||
| 115 | ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg); | ||
| 116 | R_THROW(ResultInvalidOpusDSPReturnCode); | ||
| 117 | } | ||
| 118 | |||
| 119 | R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); | ||
| 120 | } | ||
| 121 | |||
| 122 | Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) { | ||
| 123 | std::scoped_lock l{mutex}; | ||
| 124 | shared_memory.host_send_data[0] = (u64)buffer; | ||
| 125 | shared_memory.host_send_data[1] = buffer_size; | ||
| 126 | |||
| 127 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject); | ||
| 128 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 129 | ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, | ||
| 130 | "Expected Opus shutdown code {}, got {}", | ||
| 131 | ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg); | ||
| 132 | |||
| 133 | R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); | ||
| 134 | } | ||
| 135 | |||
| 136 | Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) { | ||
| 137 | std::scoped_lock l{mutex}; | ||
| 138 | shared_memory.host_send_data[0] = (u64)buffer; | ||
| 139 | shared_memory.host_send_data[1] = buffer_size; | ||
| 140 | |||
| 141 | opus_decoder.Send(ADSP::Direction::DSP, | ||
| 142 | ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject); | ||
| 143 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 144 | ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, | ||
| 145 | "Expected Opus shutdown code {}, got {}", | ||
| 146 | ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg); | ||
| 147 | |||
| 148 | R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); | ||
| 149 | } | ||
| 150 | |||
| 151 | Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data, | ||
| 152 | u64 output_data_size, u32 channel_count, void* input_data, | ||
| 153 | u64 input_data_size, void* buffer, u64& out_time_taken, | ||
| 154 | bool reset) { | ||
| 155 | std::scoped_lock l{mutex}; | ||
| 156 | shared_memory.host_send_data[0] = (u64)buffer; | ||
| 157 | shared_memory.host_send_data[1] = (u64)input_data; | ||
| 158 | shared_memory.host_send_data[2] = input_data_size; | ||
| 159 | shared_memory.host_send_data[3] = (u64)output_data; | ||
| 160 | shared_memory.host_send_data[4] = output_data_size; | ||
| 161 | shared_memory.host_send_data[5] = 0; | ||
| 162 | shared_memory.host_send_data[6] = reset; | ||
| 163 | |||
| 164 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved); | ||
| 165 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 166 | if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) { | ||
| 167 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | ||
| 168 | ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg); | ||
| 169 | R_THROW(ResultInvalidOpusDSPReturnCode); | ||
| 170 | } | ||
| 171 | |||
| 172 | auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])}; | ||
| 173 | if (error_code == OPUS_OK) { | ||
| 174 | out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]); | ||
| 175 | out_time_taken = 1000 * shared_memory.dsp_return_data[2]; | ||
| 176 | } | ||
| 177 | R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); | ||
| 178 | } | ||
| 179 | |||
| 180 | Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, | ||
| 181 | u64 output_data_size, u32 channel_count, | ||
| 182 | void* input_data, u64 input_data_size, | ||
| 183 | void* buffer, u64& out_time_taken, | ||
| 184 | bool reset) { | ||
| 185 | std::scoped_lock l{mutex}; | ||
| 186 | shared_memory.host_send_data[0] = (u64)buffer; | ||
| 187 | shared_memory.host_send_data[1] = (u64)input_data; | ||
| 188 | shared_memory.host_send_data[2] = input_data_size; | ||
| 189 | shared_memory.host_send_data[3] = (u64)output_data; | ||
| 190 | shared_memory.host_send_data[4] = output_data_size; | ||
| 191 | shared_memory.host_send_data[5] = 0; | ||
| 192 | shared_memory.host_send_data[6] = reset; | ||
| 193 | |||
| 194 | opus_decoder.Send(ADSP::Direction::DSP, | ||
| 195 | ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream); | ||
| 196 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 197 | if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) { | ||
| 198 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | ||
| 199 | ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg); | ||
| 200 | R_THROW(ResultInvalidOpusDSPReturnCode); | ||
| 201 | } | ||
| 202 | |||
| 203 | auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])}; | ||
| 204 | if (error_code == OPUS_OK) { | ||
| 205 | out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]); | ||
| 206 | out_time_taken = 1000 * shared_memory.dsp_return_data[2]; | ||
| 207 | } | ||
| 208 | R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); | ||
| 209 | } | ||
| 210 | |||
| 211 | Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) { | ||
| 212 | std::scoped_lock l{mutex}; | ||
| 213 | shared_memory.host_send_data[0] = (u64)buffer; | ||
| 214 | shared_memory.host_send_data[1] = buffer_size; | ||
| 215 | |||
| 216 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory); | ||
| 217 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 218 | if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) { | ||
| 219 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | ||
| 220 | ADSP::OpusDecoder::Message::MapMemoryOK, msg); | ||
| 221 | R_THROW(ResultInvalidOpusDSPReturnCode); | ||
| 222 | } | ||
| 223 | R_SUCCEED(); | ||
| 224 | } | ||
| 225 | |||
| 226 | Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) { | ||
| 227 | std::scoped_lock l{mutex}; | ||
| 228 | shared_memory.host_send_data[0] = (u64)buffer; | ||
| 229 | shared_memory.host_send_data[1] = buffer_size; | ||
| 230 | |||
| 231 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory); | ||
| 232 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 233 | if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) { | ||
| 234 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | ||
| 235 | ADSP::OpusDecoder::Message::UnmapMemoryOK, msg); | ||
| 236 | R_THROW(ResultInvalidOpusDSPReturnCode); | ||
| 237 | } | ||
| 238 | R_SUCCEED(); | ||
| 239 | } | ||
| 240 | |||
| 241 | } // namespace AudioCore::OpusDecoder | ||
diff --git a/src/audio_core/opus/hardware_opus.h b/src/audio_core/opus/hardware_opus.h new file mode 100644 index 000000000..7013a6b40 --- /dev/null +++ b/src/audio_core/opus/hardware_opus.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <mutex> | ||
| 7 | #include <opus.h> | ||
| 8 | |||
| 9 | #include "audio_core/adsp/apps/opus/opus_decoder.h" | ||
| 10 | #include "audio_core/adsp/apps/opus/shared_memory.h" | ||
| 11 | #include "audio_core/adsp/mailbox.h" | ||
| 12 | #include "core/hle/service/audio/errors.h" | ||
| 13 | |||
| 14 | namespace AudioCore::OpusDecoder { | ||
| 15 | class HardwareOpus { | ||
| 16 | public: | ||
| 17 | HardwareOpus(Core::System& system); | ||
| 18 | |||
| 19 | u64 GetWorkBufferSize(u32 channel); | ||
| 20 | u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count); | ||
| 21 | |||
| 22 | Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, | ||
| 23 | u64 buffer_size); | ||
| 24 | Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, | ||
| 25 | u32 totaL_stream_count, u32 stereo_stream_count, | ||
| 26 | void* mappings, void* buffer, u64 buffer_size); | ||
| 27 | Result ShutdownDecodeObject(void* buffer, u64 buffer_size); | ||
| 28 | Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size); | ||
| 29 | Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size, | ||
| 30 | u32 channel_count, void* input_data, u64 input_data_size, void* buffer, | ||
| 31 | u64& out_time_taken, bool reset); | ||
| 32 | Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, | ||
| 33 | u64 output_data_size, u32 channel_count, | ||
| 34 | void* input_data, u64 input_data_size, void* buffer, | ||
| 35 | u64& out_time_taken, bool reset); | ||
| 36 | Result MapMemory(void* buffer, u64 buffer_size); | ||
| 37 | Result UnmapMemory(void* buffer, u64 buffer_size); | ||
| 38 | |||
| 39 | private: | ||
| 40 | Core::System& system; | ||
| 41 | std::mutex mutex; | ||
| 42 | ADSP::OpusDecoder::OpusDecoder& opus_decoder; | ||
| 43 | ADSP::OpusDecoder::SharedMemory shared_memory; | ||
| 44 | }; | ||
| 45 | } // namespace AudioCore::OpusDecoder | ||
diff --git a/src/audio_core/opus/parameters.h b/src/audio_core/opus/parameters.h new file mode 100644 index 000000000..4c54b2825 --- /dev/null +++ b/src/audio_core/opus/parameters.h | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_funcs.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace AudioCore::OpusDecoder { | ||
| 10 | constexpr size_t OpusStreamCountMax = 255; | ||
| 11 | constexpr size_t MaxChannels = 2; | ||
| 12 | |||
| 13 | struct OpusParameters { | ||
| 14 | /* 0x00 */ u32 sample_rate; | ||
| 15 | /* 0x04 */ u32 channel_count; | ||
| 16 | }; // size = 0x8 | ||
| 17 | static_assert(sizeof(OpusParameters) == 0x8, "OpusParameters has the wrong size!"); | ||
| 18 | |||
| 19 | struct OpusParametersEx { | ||
| 20 | /* 0x00 */ u32 sample_rate; | ||
| 21 | /* 0x04 */ u32 channel_count; | ||
| 22 | /* 0x08 */ bool use_large_frame_size; | ||
| 23 | /* 0x09 */ INSERT_PADDING_BYTES(7); | ||
| 24 | }; // size = 0x10 | ||
| 25 | static_assert(sizeof(OpusParametersEx) == 0x10, "OpusParametersEx has the wrong size!"); | ||
| 26 | |||
| 27 | struct OpusMultiStreamParameters { | ||
| 28 | /* 0x00 */ u32 sample_rate; | ||
| 29 | /* 0x04 */ u32 channel_count; | ||
| 30 | /* 0x08 */ u32 total_stream_count; | ||
| 31 | /* 0x0C */ u32 stereo_stream_count; | ||
| 32 | /* 0x10 */ std::array<u8, OpusStreamCountMax + 1> mappings; | ||
| 33 | }; // size = 0x110 | ||
| 34 | static_assert(sizeof(OpusMultiStreamParameters) == 0x110, | ||
| 35 | "OpusMultiStreamParameters has the wrong size!"); | ||
| 36 | |||
| 37 | struct OpusMultiStreamParametersEx { | ||
| 38 | /* 0x00 */ u32 sample_rate; | ||
| 39 | /* 0x04 */ u32 channel_count; | ||
| 40 | /* 0x08 */ u32 total_stream_count; | ||
| 41 | /* 0x0C */ u32 stereo_stream_count; | ||
| 42 | /* 0x10 */ bool use_large_frame_size; | ||
| 43 | /* 0x11 */ INSERT_PADDING_BYTES(7); | ||
| 44 | /* 0x18 */ std::array<u8, OpusStreamCountMax + 1> mappings; | ||
| 45 | }; // size = 0x118 | ||
| 46 | static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118, | ||
| 47 | "OpusMultiStreamParametersEx has the wrong size!"); | ||
| 48 | |||
| 49 | struct OpusPacketHeader { | ||
| 50 | /* 0x00 */ u32 size; | ||
| 51 | /* 0x04 */ u32 final_range; | ||
| 52 | }; // size = 0x8 | ||
| 53 | static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusPacketHeader has the wrong size!"); | ||
| 54 | } // namespace AudioCore::OpusDecoder | ||