diff options
| author | 2023-08-31 15:09:15 +0100 | |
|---|---|---|
| committer | 2023-09-16 11:56:25 -0400 | |
| commit | 67e2d5c28b8423c4f3f1d5b00f87325684158a6f (patch) | |
| tree | e419a2bb6c064ddc69a49046705b6187772fee48 /src | |
| parent | Merge pull request #11519 from german77/system-policy (diff) | |
| download | yuzu-67e2d5c28b8423c4f3f1d5b00f87325684158a6f.tar.gz yuzu-67e2d5c28b8423c4f3f1d5b00f87325684158a6f.tar.xz yuzu-67e2d5c28b8423c4f3f1d5b00f87325684158a6f.zip | |
Reimplement HardwareOpus
Diffstat (limited to 'src')
27 files changed, 1898 insertions, 411 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 | ||
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 34877b461..416203c59 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -26,12 +26,11 @@ add_library(common STATIC | |||
| 26 | assert.h | 26 | assert.h |
| 27 | atomic_helpers.h | 27 | atomic_helpers.h |
| 28 | atomic_ops.h | 28 | atomic_ops.h |
| 29 | detached_tasks.cpp | ||
| 30 | detached_tasks.h | ||
| 31 | bit_cast.h | 29 | bit_cast.h |
| 32 | bit_field.h | 30 | bit_field.h |
| 33 | bit_set.h | 31 | bit_set.h |
| 34 | bit_util.h | 32 | bit_util.h |
| 33 | bounded_threadsafe_queue.h | ||
| 35 | cityhash.cpp | 34 | cityhash.cpp |
| 36 | cityhash.h | 35 | cityhash.h |
| 37 | common_funcs.h | 36 | common_funcs.h |
| @@ -41,6 +40,8 @@ add_library(common STATIC | |||
| 41 | container_hash.h | 40 | container_hash.h |
| 42 | demangle.cpp | 41 | demangle.cpp |
| 43 | demangle.h | 42 | demangle.h |
| 43 | detached_tasks.cpp | ||
| 44 | detached_tasks.h | ||
| 44 | div_ceil.h | 45 | div_ceil.h |
| 45 | dynamic_library.cpp | 46 | dynamic_library.cpp |
| 46 | dynamic_library.h | 47 | dynamic_library.h |
diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h index bd87aa09b..b36fc1de9 100644 --- a/src/common/bounded_threadsafe_queue.h +++ b/src/common/bounded_threadsafe_queue.h | |||
| @@ -45,13 +45,13 @@ public: | |||
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | T PopWait() { | 47 | T PopWait() { |
| 48 | T t; | 48 | T t{}; |
| 49 | Pop<PopMode::Wait>(t); | 49 | Pop<PopMode::Wait>(t); |
| 50 | return t; | 50 | return t; |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | T PopWait(std::stop_token stop_token) { | 53 | T PopWait(std::stop_token stop_token) { |
| 54 | T t; | 54 | T t{}; |
| 55 | Pop<PopMode::WaitWithStopToken>(t, stop_token); | 55 | Pop<PopMode::WaitWithStopToken>(t, stop_token); |
| 56 | return t; | 56 | return t; |
| 57 | } | 57 | } |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 30d2f7df6..b2dc71d4c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -890,7 +890,7 @@ endif() | |||
| 890 | create_target_directory_groups(core) | 890 | create_target_directory_groups(core) |
| 891 | 891 | ||
| 892 | target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb) | 892 | target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb) |
| 893 | target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus renderdoc) | 893 | target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls renderdoc) |
| 894 | if (MINGW) | 894 | if (MINGW) |
| 895 | target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) | 895 | target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) |
| 896 | endif() | 896 | endif() |
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 92a1439eb..dd0b27f47 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -62,7 +62,7 @@ enum class ErrorModule : u32 { | |||
| 62 | XCD = 108, | 62 | XCD = 108, |
| 63 | TMP451 = 109, | 63 | TMP451 = 109, |
| 64 | NIFM = 110, | 64 | NIFM = 110, |
| 65 | Hwopus = 111, | 65 | HwOpus = 111, |
| 66 | LSM6DS3 = 112, | 66 | LSM6DS3 = 112, |
| 67 | Bluetooth = 113, | 67 | Bluetooth = 113, |
| 68 | VI = 114, | 68 | VI = 114, |
diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h index 3d3d3d97a..c41345f7e 100644 --- a/src/core/hle/service/audio/errors.h +++ b/src/core/hle/service/audio/errors.h | |||
| @@ -20,4 +20,16 @@ constexpr Result ResultNotSupported{ErrorModule::Audio, 513}; | |||
| 20 | constexpr Result ResultInvalidHandle{ErrorModule::Audio, 1536}; | 20 | constexpr Result ResultInvalidHandle{ErrorModule::Audio, 1536}; |
| 21 | constexpr Result ResultInvalidRevision{ErrorModule::Audio, 1537}; | 21 | constexpr Result ResultInvalidRevision{ErrorModule::Audio, 1537}; |
| 22 | 22 | ||
| 23 | constexpr Result ResultLibOpusAllocFail{ErrorModule::HwOpus, 7}; | ||
| 24 | constexpr Result ResultInputDataTooSmall{ErrorModule::HwOpus, 8}; | ||
| 25 | constexpr Result ResultLibOpusInvalidState{ErrorModule::HwOpus, 6}; | ||
| 26 | constexpr Result ResultLibOpusUnimplemented{ErrorModule::HwOpus, 5}; | ||
| 27 | constexpr Result ResultLibOpusInvalidPacket{ErrorModule::HwOpus, 17}; | ||
| 28 | constexpr Result ResultLibOpusInternalError{ErrorModule::HwOpus, 4}; | ||
| 29 | constexpr Result ResultBufferTooSmall{ErrorModule::HwOpus, 3}; | ||
| 30 | constexpr Result ResultLibOpusBadArg{ErrorModule::HwOpus, 2}; | ||
| 31 | constexpr Result ResultInvalidOpusDSPReturnCode{ErrorModule::HwOpus, 259}; | ||
| 32 | constexpr Result ResultInvalidOpusSampleRate{ErrorModule::HwOpus, 1001}; | ||
| 33 | constexpr Result ResultInvalidOpusChannelCount{ErrorModule::HwOpus, 1002}; | ||
| 34 | |||
| 23 | } // namespace Service::Audio | 35 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 1557e6088..6a7bf9416 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp | |||
| @@ -1,420 +1,506 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <chrono> | ||
| 5 | #include <cstring> | ||
| 6 | #include <memory> | 4 | #include <memory> |
| 7 | #include <vector> | 5 | #include <vector> |
| 8 | 6 | ||
| 9 | #include <opus.h> | 7 | #include "audio_core/opus/decoder.h" |
| 10 | #include <opus_multistream.h> | 8 | #include "audio_core/opus/parameters.h" |
| 11 | |||
| 12 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 13 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 14 | #include "common/scratch_buffer.h" | 11 | #include "common/scratch_buffer.h" |
| 12 | #include "core/core.h" | ||
| 15 | #include "core/hle/service/audio/hwopus.h" | 13 | #include "core/hle/service/audio/hwopus.h" |
| 16 | #include "core/hle/service/ipc_helpers.h" | 14 | #include "core/hle/service/ipc_helpers.h" |
| 17 | 15 | ||
| 18 | namespace Service::Audio { | 16 | namespace Service::Audio { |
| 19 | namespace { | 17 | using namespace AudioCore::OpusDecoder; |
| 20 | struct OpusDeleter { | 18 | |
| 21 | void operator()(OpusMSDecoder* ptr) const { | 19 | class IHardwareOpusDecoder final : public ServiceFramework<IHardwareOpusDecoder> { |
| 22 | opus_multistream_decoder_destroy(ptr); | 20 | public: |
| 21 | explicit IHardwareOpusDecoder(Core::System& system_, HardwareOpus& hardware_opus) | ||
| 22 | : ServiceFramework{system_, "IHardwareOpusDecoder"}, | ||
| 23 | impl{std::make_unique<AudioCore::OpusDecoder::OpusDecoder>(system_, hardware_opus)} { | ||
| 24 | // clang-format off | ||
| 25 | static const FunctionInfo functions[] = { | ||
| 26 | {0, &IHardwareOpusDecoder::DecodeInterleavedOld, "DecodeInterleavedOld"}, | ||
| 27 | {1, &IHardwareOpusDecoder::SetContext, "SetContext"}, | ||
| 28 | {2, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamOld, "DecodeInterleavedForMultiStreamOld"}, | ||
| 29 | {3, &IHardwareOpusDecoder::SetContextForMultiStream, "SetContextForMultiStream"}, | ||
| 30 | {4, &IHardwareOpusDecoder::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"}, | ||
| 31 | {5, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfOld, "DecodeInterleavedForMultiStreamWithPerfOld"}, | ||
| 32 | {6, &IHardwareOpusDecoder::DecodeInterleavedWithPerfAndResetOld, "DecodeInterleavedWithPerfAndResetOld"}, | ||
| 33 | {7, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfAndResetOld, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"}, | ||
| 34 | {8, &IHardwareOpusDecoder::DecodeInterleaved, "DecodeInterleaved"}, | ||
| 35 | {9, &IHardwareOpusDecoder::DecodeInterleavedForMultiStream, "DecodeInterleavedForMultiStream"}, | ||
| 36 | }; | ||
| 37 | // clang-format on | ||
| 38 | |||
| 39 | RegisterHandlers(functions); | ||
| 23 | } | 40 | } |
| 24 | }; | ||
| 25 | 41 | ||
| 26 | using OpusDecoderPtr = std::unique_ptr<OpusMSDecoder, OpusDeleter>; | 42 | Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, |
| 43 | u64 transfer_memory_size) { | ||
| 44 | return impl->Initialize(params, transfer_memory, transfer_memory_size); | ||
| 45 | } | ||
| 27 | 46 | ||
| 28 | struct OpusPacketHeader { | 47 | Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, |
| 29 | // Packet size in bytes. | 48 | u64 transfer_memory_size) { |
| 30 | u32_be size; | 49 | return impl->Initialize(params, transfer_memory, transfer_memory_size); |
| 31 | // Indicates the final range of the codec's entropy coder. | 50 | } |
| 32 | u32_be final_range; | ||
| 33 | }; | ||
| 34 | static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size"); | ||
| 35 | 51 | ||
| 36 | class OpusDecoderState { | 52 | private: |
| 37 | public: | 53 | void DecodeInterleavedOld(HLERequestContext& ctx) { |
| 38 | /// Describes extra behavior that may be asked of the decoding context. | 54 | IPC::RequestParser rp{ctx}; |
| 39 | enum class ExtraBehavior { | ||
| 40 | /// No extra behavior. | ||
| 41 | None, | ||
| 42 | 55 | ||
| 43 | /// Resets the decoder context back to a freshly initialized state. | 56 | auto input_data{ctx.ReadBuffer(0)}; |
| 44 | ResetContext, | 57 | output_data.resize_destructive(ctx.GetWriteBufferSize()); |
| 45 | }; | ||
| 46 | 58 | ||
| 47 | enum class PerfTime { | 59 | u32 size{}; |
| 48 | Disabled, | 60 | u32 sample_count{}; |
| 49 | Enabled, | 61 | auto result = |
| 50 | }; | 62 | impl->DecodeInterleaved(&size, nullptr, &sample_count, input_data, output_data, false); |
| 51 | 63 | ||
| 52 | explicit OpusDecoderState(OpusDecoderPtr decoder_, u32 sample_rate_, u32 channel_count_) | 64 | LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {}", size, sample_count); |
| 53 | : decoder{std::move(decoder_)}, sample_rate{sample_rate_}, channel_count{channel_count_} {} | 65 | |
| 54 | 66 | ctx.WriteBuffer(output_data); | |
| 55 | // Decodes interleaved Opus packets. Optionally allows reporting time taken to | 67 | |
| 56 | // perform the decoding, as well as any relevant extra behavior. | 68 | IPC::ResponseBuilder rb{ctx, 4}; |
| 57 | void DecodeInterleaved(HLERequestContext& ctx, PerfTime perf_time, | 69 | rb.Push(result); |
| 58 | ExtraBehavior extra_behavior) { | 70 | rb.Push(size); |
| 59 | if (perf_time == PerfTime::Disabled) { | 71 | rb.Push(sample_count); |
| 60 | DecodeInterleavedHelper(ctx, nullptr, extra_behavior); | ||
| 61 | } else { | ||
| 62 | u64 performance = 0; | ||
| 63 | DecodeInterleavedHelper(ctx, &performance, extra_behavior); | ||
| 64 | } | ||
| 65 | } | 72 | } |
| 66 | 73 | ||
| 67 | private: | 74 | void SetContext(HLERequestContext& ctx) { |
| 68 | void DecodeInterleavedHelper(HLERequestContext& ctx, u64* performance, | 75 | IPC::RequestParser rp{ctx}; |
| 69 | ExtraBehavior extra_behavior) { | 76 | |
| 70 | u32 consumed = 0; | 77 | LOG_DEBUG(Service_Audio, "called"); |
| 71 | u32 sample_count = 0; | 78 | |
| 72 | samples.resize_destructive(ctx.GetWriteBufferNumElements<opus_int16>()); | 79 | auto input_data{ctx.ReadBuffer(0)}; |
| 73 | 80 | auto result = impl->SetContext(input_data); | |
| 74 | if (extra_behavior == ExtraBehavior::ResetContext) { | 81 | |
| 75 | ResetDecoderContext(); | 82 | IPC::ResponseBuilder rb{ctx, 2}; |
| 76 | } | 83 | rb.Push(result); |
| 77 | 84 | } | |
| 78 | if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) { | 85 | |
| 79 | LOG_ERROR(Audio, "Failed to decode opus data"); | 86 | void DecodeInterleavedForMultiStreamOld(HLERequestContext& ctx) { |
| 80 | IPC::ResponseBuilder rb{ctx, 2}; | 87 | IPC::RequestParser rp{ctx}; |
| 81 | // TODO(ogniK): Use correct error code | 88 | |
| 82 | rb.Push(ResultUnknown); | 89 | auto input_data{ctx.ReadBuffer(0)}; |
| 83 | return; | 90 | output_data.resize_destructive(ctx.GetWriteBufferSize()); |
| 84 | } | 91 | |
| 85 | 92 | u32 size{}; | |
| 86 | const u32 param_size = performance != nullptr ? 6 : 4; | 93 | u32 sample_count{}; |
| 87 | IPC::ResponseBuilder rb{ctx, param_size}; | 94 | auto result = impl->DecodeInterleavedForMultiStream(&size, nullptr, &sample_count, |
| 88 | rb.Push(ResultSuccess); | 95 | input_data, output_data, false); |
| 89 | rb.Push<u32>(consumed); | 96 | |
| 90 | rb.Push<u32>(sample_count); | 97 | LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {}", size, sample_count); |
| 91 | if (performance) { | 98 | |
| 92 | rb.Push<u64>(*performance); | 99 | ctx.WriteBuffer(output_data); |
| 93 | } | 100 | |
| 94 | ctx.WriteBuffer(samples); | 101 | IPC::ResponseBuilder rb{ctx, 4}; |
| 102 | rb.Push(result); | ||
| 103 | rb.Push(size); | ||
| 104 | rb.Push(sample_count); | ||
| 95 | } | 105 | } |
| 96 | 106 | ||
| 97 | bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input, | 107 | void SetContextForMultiStream(HLERequestContext& ctx) { |
| 98 | std::span<opus_int16> output, u64* out_performance_time) const { | 108 | IPC::RequestParser rp{ctx}; |
| 99 | const auto start_time = std::chrono::steady_clock::now(); | 109 | |
| 100 | const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); | 110 | LOG_DEBUG(Service_Audio, "called"); |
| 101 | if (sizeof(OpusPacketHeader) > input.size()) { | 111 | |
| 102 | LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}", | 112 | auto input_data{ctx.ReadBuffer(0)}; |
| 103 | sizeof(OpusPacketHeader), input.size()); | 113 | auto result = impl->SetContext(input_data); |
| 104 | return false; | 114 | |
| 105 | } | 115 | IPC::ResponseBuilder rb{ctx, 2}; |
| 106 | 116 | rb.Push(result); | |
| 107 | OpusPacketHeader hdr{}; | ||
| 108 | std::memcpy(&hdr, input.data(), sizeof(OpusPacketHeader)); | ||
| 109 | if (sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size) > input.size()) { | ||
| 110 | LOG_ERROR(Audio, "Input does not fit in the opus header size. data_sz={}, input_sz={}", | ||
| 111 | sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size), input.size()); | ||
| 112 | return false; | ||
| 113 | } | ||
| 114 | |||
| 115 | const auto frame = input.data() + sizeof(OpusPacketHeader); | ||
| 116 | const auto decoded_sample_count = opus_packet_get_nb_samples( | ||
| 117 | frame, static_cast<opus_int32>(input.size() - sizeof(OpusPacketHeader)), | ||
| 118 | static_cast<opus_int32>(sample_rate)); | ||
| 119 | if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) { | ||
| 120 | LOG_ERROR( | ||
| 121 | Audio, | ||
| 122 | "Decoded data does not fit into the output data, decoded_sz={}, raw_output_sz={}", | ||
| 123 | decoded_sample_count * channel_count * sizeof(u16), raw_output_sz); | ||
| 124 | return false; | ||
| 125 | } | ||
| 126 | |||
| 127 | const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)); | ||
| 128 | const auto out_sample_count = | ||
| 129 | opus_multistream_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0); | ||
| 130 | if (out_sample_count < 0) { | ||
| 131 | LOG_ERROR(Audio, | ||
| 132 | "Incorrect sample count received from opus_decode, " | ||
| 133 | "output_sample_count={}, frame_size={}, data_sz_from_hdr={}", | ||
| 134 | out_sample_count, frame_size, static_cast<u32>(hdr.size)); | ||
| 135 | return false; | ||
| 136 | } | ||
| 137 | |||
| 138 | const auto end_time = std::chrono::steady_clock::now() - start_time; | ||
| 139 | sample_count = out_sample_count; | ||
| 140 | consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size); | ||
| 141 | if (out_performance_time != nullptr) { | ||
| 142 | *out_performance_time = | ||
| 143 | std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count(); | ||
| 144 | } | ||
| 145 | |||
| 146 | return true; | ||
| 147 | } | 117 | } |
| 148 | 118 | ||
| 149 | void ResetDecoderContext() { | 119 | void DecodeInterleavedWithPerfOld(HLERequestContext& ctx) { |
| 150 | ASSERT(decoder != nullptr); | 120 | IPC::RequestParser rp{ctx}; |
| 121 | |||
| 122 | auto input_data{ctx.ReadBuffer(0)}; | ||
| 123 | output_data.resize_destructive(ctx.GetWriteBufferSize()); | ||
| 124 | |||
| 125 | u32 size{}; | ||
| 126 | u32 sample_count{}; | ||
| 127 | u64 time_taken{}; | ||
| 128 | auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data, | ||
| 129 | output_data, false); | ||
| 130 | |||
| 131 | LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {} time taken {}", size, | ||
| 132 | sample_count, time_taken); | ||
| 151 | 133 | ||
| 152 | opus_multistream_decoder_ctl(decoder.get(), OPUS_RESET_STATE); | 134 | ctx.WriteBuffer(output_data); |
| 135 | |||
| 136 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 137 | rb.Push(result); | ||
| 138 | rb.Push(size); | ||
| 139 | rb.Push(sample_count); | ||
| 140 | rb.Push(time_taken); | ||
| 153 | } | 141 | } |
| 154 | 142 | ||
| 155 | OpusDecoderPtr decoder; | 143 | void DecodeInterleavedForMultiStreamWithPerfOld(HLERequestContext& ctx) { |
| 156 | u32 sample_rate; | 144 | IPC::RequestParser rp{ctx}; |
| 157 | u32 channel_count; | ||
| 158 | Common::ScratchBuffer<opus_int16> samples; | ||
| 159 | }; | ||
| 160 | 145 | ||
| 161 | class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { | 146 | auto input_data{ctx.ReadBuffer(0)}; |
| 162 | public: | 147 | output_data.resize_destructive(ctx.GetWriteBufferSize()); |
| 163 | explicit IHardwareOpusDecoderManager(Core::System& system_, OpusDecoderState decoder_state_) | ||
| 164 | : ServiceFramework{system_, "IHardwareOpusDecoderManager"}, decoder_state{ | ||
| 165 | std::move(decoder_state_)} { | ||
| 166 | // clang-format off | ||
| 167 | static const FunctionInfo functions[] = { | ||
| 168 | {0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"}, | ||
| 169 | {1, nullptr, "SetContext"}, | ||
| 170 | {2, nullptr, "DecodeInterleavedForMultiStreamOld"}, | ||
| 171 | {3, nullptr, "SetContextForMultiStream"}, | ||
| 172 | {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"}, | ||
| 173 | {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"}, | ||
| 174 | {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"}, | ||
| 175 | {7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"}, | ||
| 176 | {8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, | ||
| 177 | {9, &IHardwareOpusDecoderManager::DecodeInterleavedForMultiStream, "DecodeInterleavedForMultiStream"}, | ||
| 178 | }; | ||
| 179 | // clang-format on | ||
| 180 | 148 | ||
| 181 | RegisterHandlers(functions); | 149 | u32 size{}; |
| 150 | u32 sample_count{}; | ||
| 151 | u64 time_taken{}; | ||
| 152 | auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count, | ||
| 153 | input_data, output_data, false); | ||
| 154 | |||
| 155 | LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {} time taken {}", size, | ||
| 156 | sample_count, time_taken); | ||
| 157 | |||
| 158 | ctx.WriteBuffer(output_data); | ||
| 159 | |||
| 160 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 161 | rb.Push(result); | ||
| 162 | rb.Push(size); | ||
| 163 | rb.Push(sample_count); | ||
| 164 | rb.Push(time_taken); | ||
| 182 | } | 165 | } |
| 183 | 166 | ||
| 184 | private: | 167 | void DecodeInterleavedWithPerfAndResetOld(HLERequestContext& ctx) { |
| 185 | void DecodeInterleavedOld(HLERequestContext& ctx) { | 168 | IPC::RequestParser rp{ctx}; |
| 186 | LOG_DEBUG(Audio, "called"); | 169 | |
| 170 | auto reset{rp.Pop<bool>()}; | ||
| 171 | |||
| 172 | auto input_data{ctx.ReadBuffer(0)}; | ||
| 173 | output_data.resize_destructive(ctx.GetWriteBufferSize()); | ||
| 174 | |||
| 175 | u32 size{}; | ||
| 176 | u32 sample_count{}; | ||
| 177 | u64 time_taken{}; | ||
| 178 | auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data, | ||
| 179 | output_data, reset); | ||
| 180 | |||
| 181 | LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}", | ||
| 182 | reset, size, sample_count, time_taken); | ||
| 183 | |||
| 184 | ctx.WriteBuffer(output_data); | ||
| 187 | 185 | ||
| 188 | decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Disabled, | 186 | IPC::ResponseBuilder rb{ctx, 6}; |
| 189 | OpusDecoderState::ExtraBehavior::None); | 187 | rb.Push(result); |
| 188 | rb.Push(size); | ||
| 189 | rb.Push(sample_count); | ||
| 190 | rb.Push(time_taken); | ||
| 190 | } | 191 | } |
| 191 | 192 | ||
| 192 | void DecodeInterleavedWithPerfOld(HLERequestContext& ctx) { | 193 | void DecodeInterleavedForMultiStreamWithPerfAndResetOld(HLERequestContext& ctx) { |
| 193 | LOG_DEBUG(Audio, "called"); | 194 | IPC::RequestParser rp{ctx}; |
| 195 | |||
| 196 | auto reset{rp.Pop<bool>()}; | ||
| 197 | |||
| 198 | auto input_data{ctx.ReadBuffer(0)}; | ||
| 199 | output_data.resize_destructive(ctx.GetWriteBufferSize()); | ||
| 200 | |||
| 201 | u32 size{}; | ||
| 202 | u32 sample_count{}; | ||
| 203 | u64 time_taken{}; | ||
| 204 | auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count, | ||
| 205 | input_data, output_data, reset); | ||
| 194 | 206 | ||
| 195 | decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, | 207 | LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}", |
| 196 | OpusDecoderState::ExtraBehavior::None); | 208 | reset, size, sample_count, time_taken); |
| 209 | |||
| 210 | ctx.WriteBuffer(output_data); | ||
| 211 | |||
| 212 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 213 | rb.Push(result); | ||
| 214 | rb.Push(size); | ||
| 215 | rb.Push(sample_count); | ||
| 216 | rb.Push(time_taken); | ||
| 197 | } | 217 | } |
| 198 | 218 | ||
| 199 | void DecodeInterleaved(HLERequestContext& ctx) { | 219 | void DecodeInterleaved(HLERequestContext& ctx) { |
| 200 | LOG_DEBUG(Audio, "called"); | ||
| 201 | |||
| 202 | IPC::RequestParser rp{ctx}; | 220 | IPC::RequestParser rp{ctx}; |
| 203 | const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext | ||
| 204 | : OpusDecoderState::ExtraBehavior::None; | ||
| 205 | 221 | ||
| 206 | decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior); | 222 | auto reset{rp.Pop<bool>()}; |
| 223 | |||
| 224 | auto input_data{ctx.ReadBuffer(0)}; | ||
| 225 | output_data.resize_destructive(ctx.GetWriteBufferSize()); | ||
| 226 | |||
| 227 | u32 size{}; | ||
| 228 | u32 sample_count{}; | ||
| 229 | u64 time_taken{}; | ||
| 230 | auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data, | ||
| 231 | output_data, reset); | ||
| 232 | |||
| 233 | LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}", | ||
| 234 | reset, size, sample_count, time_taken); | ||
| 235 | |||
| 236 | ctx.WriteBuffer(output_data); | ||
| 237 | |||
| 238 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 239 | rb.Push(result); | ||
| 240 | rb.Push(size); | ||
| 241 | rb.Push(sample_count); | ||
| 242 | rb.Push(time_taken); | ||
| 207 | } | 243 | } |
| 208 | 244 | ||
| 209 | void DecodeInterleavedForMultiStream(HLERequestContext& ctx) { | 245 | void DecodeInterleavedForMultiStream(HLERequestContext& ctx) { |
| 210 | LOG_DEBUG(Audio, "called"); | ||
| 211 | |||
| 212 | IPC::RequestParser rp{ctx}; | 246 | IPC::RequestParser rp{ctx}; |
| 213 | const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext | ||
| 214 | : OpusDecoderState::ExtraBehavior::None; | ||
| 215 | 247 | ||
| 216 | decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior); | 248 | auto reset{rp.Pop<bool>()}; |
| 249 | |||
| 250 | auto input_data{ctx.ReadBuffer(0)}; | ||
| 251 | output_data.resize_destructive(ctx.GetWriteBufferSize()); | ||
| 252 | |||
| 253 | u32 size{}; | ||
| 254 | u32 sample_count{}; | ||
| 255 | u64 time_taken{}; | ||
| 256 | auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count, | ||
| 257 | input_data, output_data, reset); | ||
| 258 | |||
| 259 | LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}", | ||
| 260 | reset, size, sample_count, time_taken); | ||
| 261 | |||
| 262 | ctx.WriteBuffer(output_data); | ||
| 263 | |||
| 264 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 265 | rb.Push(result); | ||
| 266 | rb.Push(size); | ||
| 267 | rb.Push(sample_count); | ||
| 268 | rb.Push(time_taken); | ||
| 217 | } | 269 | } |
| 218 | 270 | ||
| 219 | OpusDecoderState decoder_state; | 271 | std::unique_ptr<AudioCore::OpusDecoder::OpusDecoder> impl; |
| 272 | Common::ScratchBuffer<u8> output_data; | ||
| 220 | }; | 273 | }; |
| 221 | 274 | ||
| 222 | std::size_t WorkerBufferSize(u32 channel_count) { | 275 | void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) { |
| 223 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); | 276 | IPC::RequestParser rp{ctx}; |
| 224 | constexpr int num_streams = 1; | ||
| 225 | const int num_stereo_streams = channel_count == 2 ? 1 : 0; | ||
| 226 | return opus_multistream_decoder_get_size(num_streams, num_stereo_streams); | ||
| 227 | } | ||
| 228 | 277 | ||
| 229 | // Creates the mapping table that maps the input channels to the particular | 278 | auto params = rp.PopRaw<OpusParameters>(); |
| 230 | // output channels. In the stereo case, we map the left and right input channels | 279 | auto transfer_memory_size{rp.Pop<u32>()}; |
| 231 | // to the left and right output channels respectively. | 280 | auto transfer_memory_handle{ctx.GetCopyHandle(0)}; |
| 232 | // | 281 | auto transfer_memory{ |
| 233 | // However, in the monophonic case, we only map the one available channel | 282 | system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( |
| 234 | // to the sole output channel. We specify 255 for the would-be right channel | 283 | transfer_memory_handle)}; |
| 235 | // as this is a special value defined by Opus to indicate to the decoder to | 284 | |
| 236 | // ignore that channel. | 285 | LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}", |
| 237 | std::array<u8, 2> CreateMappingTable(u32 channel_count) { | 286 | params.sample_rate, params.channel_count, transfer_memory_size); |
| 238 | if (channel_count == 2) { | ||
| 239 | return {{0, 1}}; | ||
| 240 | } | ||
| 241 | 287 | ||
| 242 | return {{0, 255}}; | 288 | auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())}; |
| 289 | |||
| 290 | OpusParametersEx ex{ | ||
| 291 | .sample_rate = params.sample_rate, | ||
| 292 | .channel_count = params.channel_count, | ||
| 293 | .use_large_frame_size = false, | ||
| 294 | }; | ||
| 295 | auto result = decoder->Initialize(ex, transfer_memory.GetPointerUnsafe(), transfer_memory_size); | ||
| 296 | |||
| 297 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 298 | rb.Push(result); | ||
| 299 | rb.PushIpcInterface(decoder); | ||
| 243 | } | 300 | } |
| 244 | } // Anonymous namespace | ||
| 245 | 301 | ||
| 246 | void HwOpus::GetWorkBufferSize(HLERequestContext& ctx) { | 302 | void HwOpus::GetWorkBufferSize(HLERequestContext& ctx) { |
| 247 | IPC::RequestParser rp{ctx}; | 303 | IPC::RequestParser rp{ctx}; |
| 248 | const auto sample_rate = rp.Pop<u32>(); | 304 | auto params = rp.PopRaw<OpusParameters>(); |
| 249 | const auto channel_count = rp.Pop<u32>(); | ||
| 250 | 305 | ||
| 251 | LOG_DEBUG(Audio, "called with sample_rate={}, channel_count={}", sample_rate, channel_count); | 306 | u64 size{}; |
| 307 | auto result = impl.GetWorkBufferSize(params, size); | ||
| 252 | 308 | ||
| 253 | ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || | 309 | LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} -- returned size 0x{:X}", |
| 254 | sample_rate == 12000 || sample_rate == 8000, | 310 | params.sample_rate, params.channel_count, size); |
| 255 | "Invalid sample rate"); | ||
| 256 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); | ||
| 257 | 311 | ||
| 258 | const u32 worker_buffer_sz = static_cast<u32>(WorkerBufferSize(channel_count)); | 312 | IPC::ResponseBuilder rb{ctx, 4}; |
| 259 | LOG_DEBUG(Audio, "worker_buffer_sz={}", worker_buffer_sz); | 313 | rb.Push(result); |
| 260 | 314 | rb.Push(size); | |
| 261 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 262 | rb.Push(ResultSuccess); | ||
| 263 | rb.Push<u32>(worker_buffer_sz); | ||
| 264 | } | 315 | } |
| 265 | 316 | ||
| 266 | void HwOpus::GetWorkBufferSizeEx(HLERequestContext& ctx) { | 317 | void HwOpus::OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx) { |
| 267 | GetWorkBufferSize(ctx); | 318 | IPC::RequestParser rp{ctx}; |
| 319 | |||
| 320 | auto input{ctx.ReadBuffer()}; | ||
| 321 | OpusMultiStreamParameters params; | ||
| 322 | std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParameters)); | ||
| 323 | |||
| 324 | auto transfer_memory_size{rp.Pop<u32>()}; | ||
| 325 | auto transfer_memory_handle{ctx.GetCopyHandle(0)}; | ||
| 326 | auto transfer_memory{ | ||
| 327 | system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( | ||
| 328 | transfer_memory_handle)}; | ||
| 329 | |||
| 330 | LOG_DEBUG(Service_Audio, | ||
| 331 | "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " | ||
| 332 | "transfer_memory_size 0x{:X}", | ||
| 333 | params.sample_rate, params.channel_count, params.total_stream_count, | ||
| 334 | params.stereo_stream_count, transfer_memory_size); | ||
| 335 | |||
| 336 | auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())}; | ||
| 337 | |||
| 338 | OpusMultiStreamParametersEx ex{ | ||
| 339 | .sample_rate = params.sample_rate, | ||
| 340 | .channel_count = params.channel_count, | ||
| 341 | .total_stream_count = params.total_stream_count, | ||
| 342 | .stereo_stream_count = params.stereo_stream_count, | ||
| 343 | .use_large_frame_size = false, | ||
| 344 | .mappings{}, | ||
| 345 | }; | ||
| 346 | std::memcpy(ex.mappings.data(), params.mappings.data(), sizeof(params.mappings)); | ||
| 347 | auto result = decoder->Initialize(ex, transfer_memory.GetPointerUnsafe(), transfer_memory_size); | ||
| 348 | |||
| 349 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 350 | rb.Push(result); | ||
| 351 | rb.PushIpcInterface(decoder); | ||
| 268 | } | 352 | } |
| 269 | 353 | ||
| 270 | void HwOpus::GetWorkBufferSizeExEx(HLERequestContext& ctx) { | 354 | void HwOpus::GetWorkBufferSizeForMultiStream(HLERequestContext& ctx) { |
| 271 | GetWorkBufferSizeEx(ctx); | 355 | IPC::RequestParser rp{ctx}; |
| 356 | |||
| 357 | auto input{ctx.ReadBuffer()}; | ||
| 358 | OpusMultiStreamParameters params; | ||
| 359 | std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParameters)); | ||
| 360 | |||
| 361 | u64 size{}; | ||
| 362 | auto result = impl.GetWorkBufferSizeForMultiStream(params, size); | ||
| 363 | |||
| 364 | LOG_DEBUG(Service_Audio, "size 0x{:X}", size); | ||
| 365 | |||
| 366 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 367 | rb.Push(result); | ||
| 368 | rb.Push(size); | ||
| 272 | } | 369 | } |
| 273 | 370 | ||
| 274 | void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) { | 371 | void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) { |
| 275 | OpusMultiStreamParametersEx param; | 372 | IPC::RequestParser rp{ctx}; |
| 276 | std::memcpy(¶m, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); | ||
| 277 | 373 | ||
| 278 | const auto sample_rate = param.sample_rate; | 374 | auto params = rp.PopRaw<OpusParametersEx>(); |
| 279 | const auto channel_count = param.channel_count; | 375 | auto transfer_memory_size{rp.Pop<u32>()}; |
| 280 | const auto number_streams = param.number_streams; | 376 | auto transfer_memory_handle{ctx.GetCopyHandle(0)}; |
| 281 | const auto number_stereo_streams = param.number_stereo_streams; | 377 | auto transfer_memory{ |
| 378 | system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( | ||
| 379 | transfer_memory_handle)}; | ||
| 282 | 380 | ||
| 283 | LOG_DEBUG( | 381 | LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}", |
| 284 | Audio, | 382 | params.sample_rate, params.channel_count, transfer_memory_size); |
| 285 | "called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}", | ||
| 286 | sample_rate, channel_count, number_streams, number_stereo_streams); | ||
| 287 | 383 | ||
| 288 | ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || | 384 | auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())}; |
| 289 | sample_rate == 12000 || sample_rate == 8000, | ||
| 290 | "Invalid sample rate"); | ||
| 291 | 385 | ||
| 292 | const u32 worker_buffer_sz = | 386 | auto result = |
| 293 | static_cast<u32>(opus_multistream_decoder_get_size(number_streams, number_stereo_streams)); | 387 | decoder->Initialize(params, transfer_memory.GetPointerUnsafe(), transfer_memory_size); |
| 294 | 388 | ||
| 295 | IPC::ResponseBuilder rb{ctx, 3}; | 389 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 296 | rb.Push(ResultSuccess); | 390 | rb.Push(result); |
| 297 | rb.Push<u32>(worker_buffer_sz); | 391 | rb.PushIpcInterface(decoder); |
| 298 | } | 392 | } |
| 299 | 393 | ||
| 300 | void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) { | 394 | void HwOpus::GetWorkBufferSizeEx(HLERequestContext& ctx) { |
| 301 | IPC::RequestParser rp{ctx}; | 395 | IPC::RequestParser rp{ctx}; |
| 302 | const auto sample_rate = rp.Pop<u32>(); | 396 | auto params = rp.PopRaw<OpusParametersEx>(); |
| 303 | const auto channel_count = rp.Pop<u32>(); | ||
| 304 | const auto buffer_sz = rp.Pop<u32>(); | ||
| 305 | |||
| 306 | LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate, | ||
| 307 | channel_count, buffer_sz); | ||
| 308 | |||
| 309 | ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || | ||
| 310 | sample_rate == 12000 || sample_rate == 8000, | ||
| 311 | "Invalid sample rate"); | ||
| 312 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); | ||
| 313 | |||
| 314 | const std::size_t worker_sz = WorkerBufferSize(channel_count); | ||
| 315 | ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large"); | ||
| 316 | |||
| 317 | const int num_stereo_streams = channel_count == 2 ? 1 : 0; | ||
| 318 | const auto mapping_table = CreateMappingTable(channel_count); | ||
| 319 | |||
| 320 | int error = 0; | ||
| 321 | OpusDecoderPtr decoder{ | ||
| 322 | opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1, | ||
| 323 | num_stereo_streams, mapping_table.data(), &error)}; | ||
| 324 | if (error != OPUS_OK || decoder == nullptr) { | ||
| 325 | LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error); | ||
| 326 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 327 | // TODO(ogniK): Use correct error code | ||
| 328 | rb.Push(ResultUnknown); | ||
| 329 | return; | ||
| 330 | } | ||
| 331 | 397 | ||
| 332 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 398 | u64 size{}; |
| 333 | rb.Push(ResultSuccess); | 399 | auto result = impl.GetWorkBufferSizeEx(params, size); |
| 334 | rb.PushIpcInterface<IHardwareOpusDecoderManager>( | 400 | |
| 335 | system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); | 401 | LOG_DEBUG(Service_Audio, "size 0x{:X}", size); |
| 402 | |||
| 403 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 404 | rb.Push(result); | ||
| 405 | rb.Push(size); | ||
| 336 | } | 406 | } |
| 337 | 407 | ||
| 338 | void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) { | 408 | void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) { |
| 339 | IPC::RequestParser rp{ctx}; | 409 | IPC::RequestParser rp{ctx}; |
| 340 | const auto sample_rate = rp.Pop<u32>(); | ||
| 341 | const auto channel_count = rp.Pop<u32>(); | ||
| 342 | 410 | ||
| 343 | LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); | 411 | auto input{ctx.ReadBuffer()}; |
| 412 | OpusMultiStreamParametersEx params; | ||
| 413 | std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx)); | ||
| 414 | |||
| 415 | auto transfer_memory_size{rp.Pop<u32>()}; | ||
| 416 | auto transfer_memory_handle{ctx.GetCopyHandle(0)}; | ||
| 417 | auto transfer_memory{ | ||
| 418 | system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( | ||
| 419 | transfer_memory_handle)}; | ||
| 344 | 420 | ||
| 345 | ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || | 421 | LOG_DEBUG(Service_Audio, |
| 346 | sample_rate == 12000 || sample_rate == 8000, | 422 | "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " |
| 347 | "Invalid sample rate"); | 423 | "use_large_frame_size {}" |
| 348 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); | 424 | "transfer_memory_size 0x{:X}", |
| 425 | params.sample_rate, params.channel_count, params.total_stream_count, | ||
| 426 | params.stereo_stream_count, params.use_large_frame_size, transfer_memory_size); | ||
| 349 | 427 | ||
| 350 | const int num_stereo_streams = channel_count == 2 ? 1 : 0; | 428 | auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())}; |
| 351 | const auto mapping_table = CreateMappingTable(channel_count); | ||
| 352 | 429 | ||
| 353 | int error = 0; | 430 | auto result = |
| 354 | OpusDecoderPtr decoder{ | 431 | decoder->Initialize(params, transfer_memory.GetPointerUnsafe(), transfer_memory_size); |
| 355 | opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1, | ||
| 356 | num_stereo_streams, mapping_table.data(), &error)}; | ||
| 357 | if (error != OPUS_OK || decoder == nullptr) { | ||
| 358 | LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error); | ||
| 359 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 360 | // TODO(ogniK): Use correct error code | ||
| 361 | rb.Push(ResultUnknown); | ||
| 362 | return; | ||
| 363 | } | ||
| 364 | 432 | ||
| 365 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 433 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 366 | rb.Push(ResultSuccess); | 434 | rb.Push(result); |
| 367 | rb.PushIpcInterface<IHardwareOpusDecoderManager>( | 435 | rb.PushIpcInterface(decoder); |
| 368 | system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); | ||
| 369 | } | 436 | } |
| 370 | 437 | ||
| 371 | void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) { | 438 | void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) { |
| 439 | IPC::RequestParser rp{ctx}; | ||
| 440 | |||
| 441 | auto input{ctx.ReadBuffer()}; | ||
| 372 | OpusMultiStreamParametersEx params; | 442 | OpusMultiStreamParametersEx params; |
| 373 | std::memcpy(¶ms, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); | 443 | std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx)); |
| 374 | |||
| 375 | const auto& sample_rate = params.sample_rate; | ||
| 376 | const auto& channel_count = params.channel_count; | ||
| 377 | |||
| 378 | LOG_INFO( | ||
| 379 | Audio, | ||
| 380 | "called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}", | ||
| 381 | sample_rate, channel_count, params.number_streams, params.number_stereo_streams); | ||
| 382 | |||
| 383 | ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || | ||
| 384 | sample_rate == 12000 || sample_rate == 8000, | ||
| 385 | "Invalid sample rate"); | ||
| 386 | |||
| 387 | int error = 0; | ||
| 388 | OpusDecoderPtr decoder{opus_multistream_decoder_create( | ||
| 389 | sample_rate, static_cast<int>(channel_count), params.number_streams, | ||
| 390 | params.number_stereo_streams, params.channel_mappings.data(), &error)}; | ||
| 391 | if (error != OPUS_OK || decoder == nullptr) { | ||
| 392 | LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error); | ||
| 393 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 394 | // TODO(ogniK): Use correct error code | ||
| 395 | rb.Push(ResultUnknown); | ||
| 396 | return; | ||
| 397 | } | ||
| 398 | 444 | ||
| 399 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 445 | u64 size{}; |
| 400 | rb.Push(ResultSuccess); | 446 | auto result = impl.GetWorkBufferSizeForMultiStreamEx(params, size); |
| 401 | rb.PushIpcInterface<IHardwareOpusDecoderManager>( | 447 | |
| 402 | system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); | 448 | LOG_DEBUG(Service_Audio, |
| 449 | "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " | ||
| 450 | "use_large_frame_size {} -- returned size 0x{:X}", | ||
| 451 | params.sample_rate, params.channel_count, params.total_stream_count, | ||
| 452 | params.stereo_stream_count, params.use_large_frame_size, size); | ||
| 453 | |||
| 454 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 455 | rb.Push(result); | ||
| 456 | rb.Push(size); | ||
| 457 | } | ||
| 458 | |||
| 459 | void HwOpus::GetWorkBufferSizeExEx(HLERequestContext& ctx) { | ||
| 460 | IPC::RequestParser rp{ctx}; | ||
| 461 | auto params = rp.PopRaw<OpusParametersEx>(); | ||
| 462 | |||
| 463 | u64 size{}; | ||
| 464 | auto result = impl.GetWorkBufferSizeExEx(params, size); | ||
| 465 | |||
| 466 | LOG_DEBUG(Service_Audio, "size 0x{:X}", size); | ||
| 467 | |||
| 468 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 469 | rb.Push(result); | ||
| 470 | rb.Push(size); | ||
| 471 | } | ||
| 472 | |||
| 473 | void HwOpus::GetWorkBufferSizeForMultiStreamExEx(HLERequestContext& ctx) { | ||
| 474 | IPC::RequestParser rp{ctx}; | ||
| 475 | |||
| 476 | auto input{ctx.ReadBuffer()}; | ||
| 477 | OpusMultiStreamParametersEx params; | ||
| 478 | std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx)); | ||
| 479 | |||
| 480 | u64 size{}; | ||
| 481 | auto result = impl.GetWorkBufferSizeForMultiStreamExEx(params, size); | ||
| 482 | |||
| 483 | LOG_DEBUG(Service_Audio, "size 0x{:X}", size); | ||
| 484 | |||
| 485 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 486 | rb.Push(result); | ||
| 487 | rb.Push(size); | ||
| 403 | } | 488 | } |
| 404 | 489 | ||
| 405 | HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { | 490 | HwOpus::HwOpus(Core::System& system_) |
| 491 | : ServiceFramework{system_, "hwopus"}, system{system_}, impl{system} { | ||
| 406 | static const FunctionInfo functions[] = { | 492 | static const FunctionInfo functions[] = { |
| 407 | {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"}, | 493 | {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"}, |
| 408 | {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"}, | 494 | {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"}, |
| 409 | {2, nullptr, "OpenOpusDecoderForMultiStream"}, | 495 | {2, &HwOpus::OpenHardwareOpusDecoderForMultiStream, "OpenOpusDecoderForMultiStream"}, |
| 410 | {3, nullptr, "GetWorkBufferSizeForMultiStream"}, | 496 | {3, &HwOpus::GetWorkBufferSizeForMultiStream, "GetWorkBufferSizeForMultiStream"}, |
| 411 | {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"}, | 497 | {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"}, |
| 412 | {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"}, | 498 | {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"}, |
| 413 | {6, &HwOpus::OpenHardwareOpusDecoderForMultiStreamEx, | 499 | {6, &HwOpus::OpenHardwareOpusDecoderForMultiStreamEx, |
| 414 | "OpenHardwareOpusDecoderForMultiStreamEx"}, | 500 | "OpenHardwareOpusDecoderForMultiStreamEx"}, |
| 415 | {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"}, | 501 | {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"}, |
| 416 | {8, &HwOpus::GetWorkBufferSizeExEx, "GetWorkBufferSizeExEx"}, | 502 | {8, &HwOpus::GetWorkBufferSizeExEx, "GetWorkBufferSizeExEx"}, |
| 417 | {9, nullptr, "GetWorkBufferSizeForMultiStreamExEx"}, | 503 | {9, &HwOpus::GetWorkBufferSizeForMultiStreamExEx, "GetWorkBufferSizeForMultiStreamExEx"}, |
| 418 | }; | 504 | }; |
| 419 | RegisterHandlers(functions); | 505 | RegisterHandlers(functions); |
| 420 | } | 506 | } |
diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h index 90867bf74..d3960065e 100644 --- a/src/core/hle/service/audio/hwopus.h +++ b/src/core/hle/service/audio/hwopus.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "audio_core/opus/decoder_manager.h" | ||
| 6 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 7 | 8 | ||
| 8 | namespace Core { | 9 | namespace Core { |
| @@ -11,18 +12,6 @@ class System; | |||
| 11 | 12 | ||
| 12 | namespace Service::Audio { | 13 | namespace Service::Audio { |
| 13 | 14 | ||
| 14 | struct OpusMultiStreamParametersEx { | ||
| 15 | u32 sample_rate; | ||
| 16 | u32 channel_count; | ||
| 17 | u32 number_streams; | ||
| 18 | u32 number_stereo_streams; | ||
| 19 | u32 use_large_frame_size; | ||
| 20 | u32 padding; | ||
| 21 | std::array<u8, 0x100> channel_mappings; | ||
| 22 | }; | ||
| 23 | static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118, | ||
| 24 | "OpusMultiStreamParametersEx has incorrect size"); | ||
| 25 | |||
| 26 | class HwOpus final : public ServiceFramework<HwOpus> { | 15 | class HwOpus final : public ServiceFramework<HwOpus> { |
| 27 | public: | 16 | public: |
| 28 | explicit HwOpus(Core::System& system_); | 17 | explicit HwOpus(Core::System& system_); |
| @@ -30,12 +19,18 @@ public: | |||
| 30 | 19 | ||
| 31 | private: | 20 | private: |
| 32 | void OpenHardwareOpusDecoder(HLERequestContext& ctx); | 21 | void OpenHardwareOpusDecoder(HLERequestContext& ctx); |
| 33 | void OpenHardwareOpusDecoderEx(HLERequestContext& ctx); | ||
| 34 | void OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx); | ||
| 35 | void GetWorkBufferSize(HLERequestContext& ctx); | 22 | void GetWorkBufferSize(HLERequestContext& ctx); |
| 23 | void OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx); | ||
| 24 | void GetWorkBufferSizeForMultiStream(HLERequestContext& ctx); | ||
| 25 | void OpenHardwareOpusDecoderEx(HLERequestContext& ctx); | ||
| 36 | void GetWorkBufferSizeEx(HLERequestContext& ctx); | 26 | void GetWorkBufferSizeEx(HLERequestContext& ctx); |
| 37 | void GetWorkBufferSizeExEx(HLERequestContext& ctx); | 27 | void OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx); |
| 38 | void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx); | 28 | void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx); |
| 29 | void GetWorkBufferSizeExEx(HLERequestContext& ctx); | ||
| 30 | void GetWorkBufferSizeForMultiStreamExEx(HLERequestContext& ctx); | ||
| 31 | |||
| 32 | Core::System& system; | ||
| 33 | AudioCore::OpusDecoder::OpusDecoderManager impl; | ||
| 39 | }; | 34 | }; |
| 40 | 35 | ||
| 41 | } // namespace Service::Audio | 36 | } // namespace Service::Audio |