summaryrefslogtreecommitdiff
path: root/src/audio_core
diff options
context:
space:
mode:
authorGravatar Kelebek12023-08-31 15:09:15 +0100
committerGravatar Liam2023-09-16 11:56:25 -0400
commit67e2d5c28b8423c4f3f1d5b00f87325684158a6f (patch)
treee419a2bb6c064ddc69a49046705b6187772fee48 /src/audio_core
parentMerge pull request #11519 from german77/system-policy (diff)
downloadyuzu-67e2d5c28b8423c4f3f1d5b00f87325684158a6f.tar.gz
yuzu-67e2d5c28b8423c4f3f1d5b00f87325684158a6f.tar.xz
yuzu-67e2d5c28b8423c4f3f1d5b00f87325684158a6f.zip
Reimplement HardwareOpus
Diffstat (limited to 'src/audio_core')
-rw-r--r--src/audio_core/CMakeLists.txt16
-rw-r--r--src/audio_core/adsp/adsp.cpp13
-rw-r--r--src/audio_core/adsp/adsp.h3
-rw-r--r--src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp48
-rw-r--r--src/audio_core/adsp/apps/audio_renderer/audio_renderer.h45
-rw-r--r--src/audio_core/adsp/apps/opus/opus_decode_object.cpp107
-rw-r--r--src/audio_core/adsp/apps/opus/opus_decode_object.h38
-rw-r--r--src/audio_core/adsp/apps/opus/opus_decoder.cpp269
-rw-r--r--src/audio_core/adsp/apps/opus/opus_decoder.h92
-rw-r--r--src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp111
-rw-r--r--src/audio_core/adsp/apps/opus/opus_multistream_decode_object.h39
-rw-r--r--src/audio_core/adsp/apps/opus/shared_memory.h17
-rw-r--r--src/audio_core/adsp/mailbox.h27
-rw-r--r--src/audio_core/opus/decoder.cpp179
-rw-r--r--src/audio_core/opus/decoder.h53
-rw-r--r--src/audio_core/opus/decoder_manager.cpp102
-rw-r--r--src/audio_core/opus/decoder_manager.h38
-rw-r--r--src/audio_core/opus/hardware_opus.cpp241
-rw-r--r--src/audio_core/opus/hardware_opus.h45
-rw-r--r--src/audio_core/opus/parameters.h54
20 files changed, 1465 insertions, 72 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 67dfe0290..400988c5f 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -10,6 +10,13 @@ add_library(audio_core STATIC
10 adsp/apps/audio_renderer/command_buffer.h 10 adsp/apps/audio_renderer/command_buffer.h
11 adsp/apps/audio_renderer/command_list_processor.cpp 11 adsp/apps/audio_renderer/command_list_processor.cpp
12 adsp/apps/audio_renderer/command_list_processor.h 12 adsp/apps/audio_renderer/command_list_processor.h
13 adsp/apps/opus/opus_decoder.cpp
14 adsp/apps/opus/opus_decoder.h
15 adsp/apps/opus/opus_decode_object.cpp
16 adsp/apps/opus/opus_decode_object.h
17 adsp/apps/opus/opus_multistream_decode_object.cpp
18 adsp/apps/opus/opus_multistream_decode_object.h
19 adsp/apps/opus/shared_memory.h
13 audio_core.cpp 20 audio_core.cpp
14 audio_core.h 21 audio_core.h
15 audio_event.h 22 audio_event.h
@@ -35,6 +42,13 @@ add_library(audio_core STATIC
35 in/audio_in.h 42 in/audio_in.h
36 in/audio_in_system.cpp 43 in/audio_in_system.cpp
37 in/audio_in_system.h 44 in/audio_in_system.h
45 opus/hardware_opus.cpp
46 opus/hardware_opus.h
47 opus/decoder_manager.cpp
48 opus/decoder_manager.h
49 opus/decoder.cpp
50 opus/decoder.h
51 opus/parameters.h
38 out/audio_out.cpp 52 out/audio_out.cpp
39 out/audio_out.h 53 out/audio_out.h
40 out/audio_out_system.cpp 54 out/audio_out_system.cpp
@@ -214,7 +228,7 @@ else()
214 ) 228 )
215endif() 229endif()
216 230
217target_link_libraries(audio_core PUBLIC common core) 231target_link_libraries(audio_core PUBLIC common core Opus::opus)
218if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) 232if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
219 target_link_libraries(audio_core PRIVATE dynarmic::dynarmic) 233 target_link_libraries(audio_core PRIVATE dynarmic::dynarmic)
220endif() 234endif()
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 @@
7namespace AudioCore::ADSP { 7namespace AudioCore::ADSP {
8 8
9ADSP::ADSP(Core::System& system, Sink::Sink& sink) { 9ADSP::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
14AudioRenderer::AudioRenderer& ADSP::AudioRenderer() { 19AudioRenderer::AudioRenderer& ADSP::AudioRenderer() {
15 return *audio_renderer.get(); 20 return *audio_renderer.get();
16} 21}
17 22
23OpusDecoder::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
9namespace Core { 10namespace 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
44private: 46private:
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
17MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97)); 17MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP_AudioRenderer", MP_RGB(60, 19, 97));
18 18
19namespace AudioCore::ADSP::AudioRenderer { 19namespace AudioCore::ADSP::AudioRenderer {
20 20
21AudioRenderer::AudioRenderer(Core::System& system_, Core::Memory::Memory& memory_, 21AudioRenderer::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
25AudioRenderer::~AudioRenderer() { 24AudioRenderer::~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
68void AudioRenderer::Signal() { 67void 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
73void AudioRenderer::Wait() { 72void 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
83void AudioRenderer::Send(Direction dir, MailboxMessage message) { 82void AudioRenderer::Send(Direction dir, u32 message) {
84 mailbox.Send(dir, std::move(message)); 83 mailbox.Send(dir, std::move(message));
85} 84}
86 85
87MailboxMessage AudioRenderer::Receive(Direction dir, bool block) { 86u32 AudioRenderer::Receive(Direction dir) {
88 return mailbox.Receive(dir, block); 87 return mailbox.Receive(dir);
89} 88}
90 89
91void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit, 90void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit,
@@ -120,7 +119,7 @@ void AudioRenderer::CreateSinkStreams() {
120} 119}
121 120
122void AudioRenderer::Main(std::stop_token stop_token) { 121void 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
18namespace Core { 18namespace Core {
19class System; 19class System;
20namespace Timing {
21struct EventType;
22}
23namespace Memory {
24class Memory;
25}
26class System;
27} // namespace Core 20} // namespace Core
28 21
29namespace AudioCore { 22namespace AudioCore {
@@ -34,19 +27,19 @@ class Sink;
34namespace ADSP::AudioRenderer { 27namespace ADSP::AudioRenderer {
35 28
36enum Message : u32 { 29enum 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 */
55class AudioRenderer { 48class AudioRenderer {
56public: 49public:
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
7namespace AudioCore::ADSP::OpusDecoder {
8namespace {
9bool IsValidChannelCount(u32 channel_count) {
10 return channel_count == 1 || channel_count == 2;
11}
12} // namespace
13
14u32 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
21OpusDecodeObject& 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
37s32 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
66s32 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
82s32 OpusDecodeObject::ResetDecoder() {
83 return opus_decoder_ctl(decoder, OPUS_RESET_STATE);
84}
85
86s32 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
10namespace AudioCore::ADSP::OpusDecoder {
11using LibOpusDecoder = ::OpusDecoder;
12static constexpr u32 DecodeObjectMagic = 0xDEADBEEF;
13
14class OpusDecodeObject {
15public:
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
28private:
29 u32 magic;
30 bool initialized;
31 bool state_valid;
32 OpusDecodeObject* self;
33 u32 final_range;
34 LibOpusDecoder* decoder;
35};
36static_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
18MICROPROFILE_DEFINE(OpusDecoder, "Audio", "DSP_OpusDecoder", MP_RGB(60, 19, 97));
19
20namespace AudioCore::ADSP::OpusDecoder {
21
22namespace {
23constexpr size_t OpusStreamCountMax = 255;
24
25bool IsValidChannelCount(u32 channel_count) {
26 return channel_count == 1 || channel_count == 2;
27}
28
29bool IsValidMultiStreamChannelCount(u32 channel_count) {
30 return channel_count <= OpusStreamCountMax;
31}
32
33bool 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
39OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} {
40 init_thread = std::jthread([this](std::stop_token stop_token) { Init(stop_token); });
41}
42
43OpusDecoder::~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
59void OpusDecoder::Send(Direction dir, u32 message) {
60 mailbox.Send(dir, std::move(message));
61}
62
63u32 OpusDecoder::Receive(Direction dir, std::stop_token stop_token) {
64 return mailbox.Receive(dir, stop_token);
65}
66
67void 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
80void 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
13namespace Core {
14class System;
15} // namespace Core
16
17namespace AudioCore::ADSP::OpusDecoder {
18
19enum 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 */
51class OpusDecoder {
52public:
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
67private:
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
7namespace AudioCore::ADSP::OpusDecoder {
8
9namespace {
10bool IsValidChannelCount(u32 channel_count) {
11 return channel_count == 1 || channel_count == 2;
12}
13
14bool 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
20u32 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
29OpusMultiStreamDecodeObject& 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
45s32 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
70s32 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
86s32 OpusMultiStreamDecodeObject::ResetDecoder() {
87 return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE);
88}
89
90s32 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
10namespace AudioCore::ADSP::OpusDecoder {
11using LibOpusMSDecoder = ::OpusMSDecoder;
12static constexpr u32 DecodeMultiStreamObjectMagic = 0xDEADBEEF;
13
14class OpusMultiStreamDecodeObject {
15public:
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
29private:
30 u32 magic;
31 bool initialized;
32 bool state_valid;
33 OpusMultiStreamDecodeObject* self;
34 u32 final_range;
35 LibOpusMSDecoder* decoder;
36};
37static_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
9namespace AudioCore::ADSP::OpusDecoder {
10
11struct 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
22struct MailboxMessage {
23 u32 msg;
24 std::span<u8> data;
25};
26
27class Mailbox { 24class Mailbox {
28public: 25public:
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
63private: 54private:
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
11namespace AudioCore::OpusDecoder {
12using namespace Service::Audio;
13namespace {
14OpusPacketHeader 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
22OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_)
23 : system{system_}, hardware_opus{hardware_opus_} {}
24
25OpusDecoder::~OpusDecoder() {
26 if (decode_object_initialized) {
27 hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size);
28 }
29}
30
31Result 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
62Result 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
97Result 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
133Result 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
139Result 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
13namespace Core {
14class System;
15}
16
17namespace AudioCore::OpusDecoder {
18class HardwareOpus;
19
20class OpusDecoder {
21public:
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
36private:
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
9namespace AudioCore::OpusDecoder {
10using namespace Service::Audio;
11
12namespace {
13bool IsValidChannelCount(u32 channel_count) {
14 return channel_count == 1 || channel_count == 2;
15}
16
17bool IsValidMultiStreamChannelCount(u32 channel_count) {
18 return channel_count > 0 && channel_count <= OpusStreamCountMax;
19}
20
21bool 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
26bool 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
34OpusDecoderManager::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
41Result 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
50Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) {
51 R_RETURN(GetWorkBufferSizeExEx(params, out_size));
52}
53
54Result 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
66Result 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
79Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params,
80 u64& out_size) {
81 R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size));
82}
83
84Result 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
11namespace Core {
12class System;
13}
14
15namespace AudioCore::OpusDecoder {
16
17class OpusDecoderManager {
18public:
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
32private:
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
10namespace AudioCore::OpusDecoder {
11namespace {
12using namespace Service::Audio;
13
14static 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
40HardwareOpus::HardwareOpus(Core::System& system_)
41 : system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} {
42 opus_decoder.SetSharedMemory(shared_memory);
43}
44
45u64 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
61u64 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
76Result 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
95Result 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
122Result 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
136Result 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
151Result 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
180Result 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
211Result 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
226Result 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
14namespace AudioCore::OpusDecoder {
15class HardwareOpus {
16public:
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
39private:
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
9namespace AudioCore::OpusDecoder {
10constexpr size_t OpusStreamCountMax = 255;
11constexpr size_t MaxChannels = 2;
12
13struct OpusParameters {
14 /* 0x00 */ u32 sample_rate;
15 /* 0x04 */ u32 channel_count;
16}; // size = 0x8
17static_assert(sizeof(OpusParameters) == 0x8, "OpusParameters has the wrong size!");
18
19struct 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
25static_assert(sizeof(OpusParametersEx) == 0x10, "OpusParametersEx has the wrong size!");
26
27struct 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
34static_assert(sizeof(OpusMultiStreamParameters) == 0x110,
35 "OpusMultiStreamParameters has the wrong size!");
36
37struct 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
46static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118,
47 "OpusMultiStreamParametersEx has the wrong size!");
48
49struct OpusPacketHeader {
50 /* 0x00 */ u32 size;
51 /* 0x04 */ u32 final_range;
52}; // size = 0x8
53static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusPacketHeader has the wrong size!");
54} // namespace AudioCore::OpusDecoder