summaryrefslogtreecommitdiff
path: root/src
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
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')
-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
-rw-r--r--src/common/CMakeLists.txt5
-rw-r--r--src/common/bounded_threadsafe_queue.h4
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/result.h2
-rw-r--r--src/core/hle/service/audio/errors.h12
-rw-r--r--src/core/hle/service/audio/hwopus.cpp722
-rw-r--r--src/core/hle/service/audio/hwopus.h25
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 )
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
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()
890create_target_directory_groups(core) 890create_target_directory_groups(core)
891 891
892target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb) 892target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb)
893target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus renderdoc) 893target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls renderdoc)
894if (MINGW) 894if (MINGW)
895 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) 895 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
896endif() 896endif()
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};
20constexpr Result ResultInvalidHandle{ErrorModule::Audio, 1536}; 20constexpr Result ResultInvalidHandle{ErrorModule::Audio, 1536};
21constexpr Result ResultInvalidRevision{ErrorModule::Audio, 1537}; 21constexpr Result ResultInvalidRevision{ErrorModule::Audio, 1537};
22 22
23constexpr Result ResultLibOpusAllocFail{ErrorModule::HwOpus, 7};
24constexpr Result ResultInputDataTooSmall{ErrorModule::HwOpus, 8};
25constexpr Result ResultLibOpusInvalidState{ErrorModule::HwOpus, 6};
26constexpr Result ResultLibOpusUnimplemented{ErrorModule::HwOpus, 5};
27constexpr Result ResultLibOpusInvalidPacket{ErrorModule::HwOpus, 17};
28constexpr Result ResultLibOpusInternalError{ErrorModule::HwOpus, 4};
29constexpr Result ResultBufferTooSmall{ErrorModule::HwOpus, 3};
30constexpr Result ResultLibOpusBadArg{ErrorModule::HwOpus, 2};
31constexpr Result ResultInvalidOpusDSPReturnCode{ErrorModule::HwOpus, 259};
32constexpr Result ResultInvalidOpusSampleRate{ErrorModule::HwOpus, 1001};
33constexpr 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
18namespace Service::Audio { 16namespace Service::Audio {
19namespace { 17using namespace AudioCore::OpusDecoder;
20struct OpusDeleter { 18
21 void operator()(OpusMSDecoder* ptr) const { 19class IHardwareOpusDecoder final : public ServiceFramework<IHardwareOpusDecoder> {
22 opus_multistream_decoder_destroy(ptr); 20public:
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
26using 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
28struct 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};
34static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size");
35 51
36class OpusDecoderState { 52private:
37public: 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
67private: 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
161class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { 146 auto input_data{ctx.ReadBuffer(0)};
162public: 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
184private: 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
222std::size_t WorkerBufferSize(u32 channel_count) { 275void 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}",
237std::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
246void HwOpus::GetWorkBufferSize(HLERequestContext& ctx) { 302void 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
266void HwOpus::GetWorkBufferSizeEx(HLERequestContext& ctx) { 317void HwOpus::OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx) {
267 GetWorkBufferSize(ctx); 318 IPC::RequestParser rp{ctx};
319
320 auto input{ctx.ReadBuffer()};
321 OpusMultiStreamParameters params;
322 std::memcpy(&params, 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
270void HwOpus::GetWorkBufferSizeExEx(HLERequestContext& ctx) { 354void HwOpus::GetWorkBufferSizeForMultiStream(HLERequestContext& ctx) {
271 GetWorkBufferSizeEx(ctx); 355 IPC::RequestParser rp{ctx};
356
357 auto input{ctx.ReadBuffer()};
358 OpusMultiStreamParameters params;
359 std::memcpy(&params, 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
274void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) { 371void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) {
275 OpusMultiStreamParametersEx param; 372 IPC::RequestParser rp{ctx};
276 std::memcpy(&param, 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
300void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) { 394void 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
338void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) { 408void 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(&params, 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
371void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) { 438void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) {
439 IPC::RequestParser rp{ctx};
440
441 auto input{ctx.ReadBuffer()};
372 OpusMultiStreamParametersEx params; 442 OpusMultiStreamParametersEx params;
373 std::memcpy(&params, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); 443 std::memcpy(&params, 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
459void 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
473void HwOpus::GetWorkBufferSizeForMultiStreamExEx(HLERequestContext& ctx) {
474 IPC::RequestParser rp{ctx};
475
476 auto input{ctx.ReadBuffer()};
477 OpusMultiStreamParametersEx params;
478 std::memcpy(&params, 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
405HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { 490HwOpus::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
8namespace Core { 9namespace Core {
@@ -11,18 +12,6 @@ class System;
11 12
12namespace Service::Audio { 13namespace Service::Audio {
13 14
14struct 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};
23static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118,
24 "OpusMultiStreamParametersEx has incorrect size");
25
26class HwOpus final : public ServiceFramework<HwOpus> { 15class HwOpus final : public ServiceFramework<HwOpus> {
27public: 16public:
28 explicit HwOpus(Core::System& system_); 17 explicit HwOpus(Core::System& system_);
@@ -30,12 +19,18 @@ public:
30 19
31private: 20private:
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