summaryrefslogtreecommitdiff
path: root/src/audio_core/renderer/adsp
diff options
context:
space:
mode:
authorGravatar Kelebek12022-07-16 23:48:45 +0100
committerGravatar Kelebek12022-07-22 01:11:32 +0100
commit458da8a94877677f086f06cdeecf959ec4283a33 (patch)
tree583166d77602ad90a0d552f37de8729ad80fd6c1 /src/audio_core/renderer/adsp
parentMerge pull request #8598 from Link4565/recv-dontwait (diff)
downloadyuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.gz
yuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.xz
yuzu-458da8a94877677f086f06cdeecf959ec4283a33.zip
Project Andio
Diffstat (limited to 'src/audio_core/renderer/adsp')
-rw-r--r--src/audio_core/renderer/adsp/adsp.cpp118
-rw-r--r--src/audio_core/renderer/adsp/adsp.h173
-rw-r--r--src/audio_core/renderer/adsp/audio_renderer.cpp226
-rw-r--r--src/audio_core/renderer/adsp/audio_renderer.h203
-rw-r--r--src/audio_core/renderer/adsp/command_buffer.h21
-rw-r--r--src/audio_core/renderer/adsp/command_list_processor.cpp109
-rw-r--r--src/audio_core/renderer/adsp/command_list_processor.h118
7 files changed, 968 insertions, 0 deletions
diff --git a/src/audio_core/renderer/adsp/adsp.cpp b/src/audio_core/renderer/adsp/adsp.cpp
new file mode 100644
index 000000000..e05a22d86
--- /dev/null
+++ b/src/audio_core/renderer/adsp/adsp.cpp
@@ -0,0 +1,118 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/adsp.h"
5#include "audio_core/renderer/adsp/command_buffer.h"
6#include "audio_core/sink/sink.h"
7#include "common/logging/log.h"
8#include "core/core.h"
9#include "core/core_timing.h"
10#include "core/core_timing_util.h"
11#include "core/memory.h"
12
13namespace AudioCore::AudioRenderer::ADSP {
14
15ADSP::ADSP(Core::System& system_, Sink::Sink& sink_)
16 : system{system_}, memory{system.Memory()}, sink{sink_} {}
17
18ADSP::~ADSP() {
19 ClearCommandBuffers();
20}
21
22State ADSP::GetState() const {
23 if (running) {
24 return State::Started;
25 }
26 return State::Stopped;
27}
28
29AudioRenderer_Mailbox* ADSP::GetRenderMailbox() {
30 return &render_mailbox;
31}
32
33void ADSP::ClearRemainCount(const u32 session_id) {
34 render_mailbox.ClearRemainCount(session_id);
35}
36
37u64 ADSP::GetSignalledTick() const {
38 return render_mailbox.GetSignalledTick();
39}
40
41u64 ADSP::GetTimeTaken() const {
42 return render_mailbox.GetRenderTimeTaken();
43}
44
45u64 ADSP::GetRenderTimeTaken(const u32 session_id) {
46 return render_mailbox.GetCommandBuffer(session_id).render_time_taken;
47}
48
49u32 ADSP::GetRemainCommandCount(const u32 session_id) const {
50 return render_mailbox.GetRemainCommandCount(session_id);
51}
52
53void ADSP::SendCommandBuffer(const u32 session_id, CommandBuffer& command_buffer) {
54 render_mailbox.SetCommandBuffer(session_id, command_buffer);
55}
56
57u64 ADSP::GetRenderingStartTick(const u32 session_id) {
58 return render_mailbox.GetSignalledTick() +
59 render_mailbox.GetCommandBuffer(session_id).render_time_taken;
60}
61
62bool ADSP::Start() {
63 if (running) {
64 return running;
65 }
66
67 running = true;
68 systems_active++;
69 audio_renderer = std::make_unique<AudioRenderer>(system);
70 audio_renderer->Start(&render_mailbox);
71 render_mailbox.HostSendMessage(RenderMessage::AudioRenderer_InitializeOK);
72 if (render_mailbox.HostWaitMessage() != RenderMessage::AudioRenderer_InitializeOK) {
73 LOG_ERROR(
74 Service_Audio,
75 "Host Audio Renderer -- Failed to receive initialize message response from ADSP!");
76 }
77 return running;
78}
79
80void ADSP::Stop() {
81 systems_active--;
82 if (running && systems_active == 0) {
83 {
84 std::scoped_lock l{mailbox_lock};
85 render_mailbox.HostSendMessage(RenderMessage::AudioRenderer_Shutdown);
86 if (render_mailbox.HostWaitMessage() != RenderMessage::AudioRenderer_Shutdown) {
87 LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
88 "message response from ADSP!");
89 }
90 }
91 audio_renderer->Stop();
92 running = false;
93 }
94}
95
96void ADSP::Signal() {
97 const auto signalled_tick{system.CoreTiming().GetClockTicks()};
98 render_mailbox.SetSignalledTick(signalled_tick);
99 render_mailbox.HostSendMessage(RenderMessage::AudioRenderer_Render);
100}
101
102void ADSP::Wait() {
103 std::scoped_lock l{mailbox_lock};
104 auto response{render_mailbox.HostWaitMessage()};
105 if (response != RenderMessage::AudioRenderer_RenderResponse) {
106 LOG_ERROR(Service_Audio, "Invalid ADSP response message, expected 0x{:02X}, got 0x{:02X}",
107 static_cast<u32>(RenderMessage::AudioRenderer_RenderResponse),
108 static_cast<u32>(response));
109 }
110
111 ClearCommandBuffers();
112}
113
114void ADSP::ClearCommandBuffers() {
115 render_mailbox.ClearCommandBuffers();
116}
117
118} // namespace AudioCore::AudioRenderer::ADSP
diff --git a/src/audio_core/renderer/adsp/adsp.h b/src/audio_core/renderer/adsp/adsp.h
new file mode 100644
index 000000000..4dfcef4a5
--- /dev/null
+++ b/src/audio_core/renderer/adsp/adsp.h
@@ -0,0 +1,173 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <mutex>
8
9#include "audio_core/renderer/adsp/audio_renderer.h"
10#include "common/common_types.h"
11
12namespace Core {
13namespace Memory {
14class Memory;
15}
16class System;
17} // namespace Core
18
19namespace AudioCore {
20namespace Sink {
21class Sink;
22}
23
24namespace AudioRenderer::ADSP {
25struct CommandBuffer;
26
27enum class State {
28 Started,
29 Stopped,
30};
31
32/**
33 * Represents the ADSP embedded within the audio sysmodule.
34 * This is a 32-bit Linux4Tegra kernel from nVidia, which is launched with the sysmodule on boot.
35 *
36 * The kernel will run apps you program for it, Nintendo have the following:
37 *
38 * Gmix - Responsible for mixing final audio and sending it out to hardware. This is last place all
39 * audio samples end up, and we skip it entirely, since we have very different backends and
40 * mixing is implicitly handled by the OS (but also due to lack of research/simplicity).
41 *
42 * AudioRenderer - Receives command lists generated by the audio render
43 * system, processes them, and sends the samples to Gmix.
44 *
45 * OpusDecoder - Contains libopus, and controls processing Opus audio and sends it to Gmix.
46 * Not much research done here, TODO if needed.
47 *
48 * We only implement the AudioRenderer for now.
49 *
50 * Communication for the apps is done through mailboxes, and some shared memory.
51 */
52class ADSP {
53public:
54 explicit ADSP(Core::System& system, Sink::Sink& sink);
55 ~ADSP();
56
57 /**
58 * Start the ADSP.
59 *
60 * @return True if started or already running, otherwise false.
61 */
62 bool Start();
63
64 /**
65 * Stop the ADSP.
66 *
67 * @return True if started or already running, otherwise false.
68 */
69 void Stop();
70
71 /**
72 * Get the ADSP's state.
73 *
74 * @return Started or Stopped.
75 */
76 State GetState() const;
77
78 /**
79 * Get the AudioRenderer mailbox to communicate with it.
80 *
81 * @return The AudioRenderer mailbox.
82 */
83 AudioRenderer_Mailbox* GetRenderMailbox();
84
85 /**
86 * Get the tick the ADSP was signalled.
87 *
88 * @return The tick the ADSP was signalled.
89 */
90 u64 GetSignalledTick() const;
91
92 /**
93 * Get the total time it took for the ADSP to run the last command lists (both command lists).
94 *
95 * @return The tick the ADSP was signalled.
96 */
97 u64 GetTimeTaken() const;
98
99 /**
100 * Get the last time a given command list took to run.
101 *
102 * @param session_id - The session id to check (0 or 1).
103 * @return The time it took.
104 */
105 u64 GetRenderTimeTaken(u32 session_id);
106
107 /**
108 * Clear the remaining command count for a given session.
109 *
110 * @param session_id - The session id to check (0 or 1).
111 */
112 void ClearRemainCount(u32 session_id);
113
114 /**
115 * Get the remaining number of commands left to process for a command list.
116 *
117 * @param session_id - The session id to check (0 or 1).
118 * @return The number of commands remaining.
119 */
120 u32 GetRemainCommandCount(u32 session_id) const;
121
122 /**
123 * Get the last tick a command list started processing.
124 *
125 * @param session_id - The session id to check (0 or 1).
126 * @return The last tick the given command list started.
127 */
128 u64 GetRenderingStartTick(u32 session_id);
129
130 /**
131 * Set a command buffer to be processed.
132 *
133 * @param session_id - The session id to check (0 or 1).
134 * @param command_buffer - The command buffer to process.
135 */
136 void SendCommandBuffer(u32 session_id, CommandBuffer& command_buffer);
137
138 /**
139 * Clear the command buffers (does not clear the time taken or the remaining command count)
140 */
141 void ClearCommandBuffers();
142
143 /**
144 * Signal the AudioRenderer to begin processing.
145 */
146 void Signal();
147
148 /**
149 * Wait for the AudioRenderer to finish processing.
150 */
151 void Wait();
152
153private:
154 /// Core system
155 Core::System& system;
156 /// Core memory
157 Core::Memory::Memory& memory;
158 /// Number of systems active, used to prevent accidental shutdowns
159 u8 systems_active{0};
160 /// ADSP running state
161 std::atomic<bool> running{false};
162 /// Output sink used by the ADSP
163 Sink::Sink& sink;
164 /// AudioRenderer app
165 std::unique_ptr<AudioRenderer> audio_renderer{};
166 /// Communication for the AudioRenderer
167 AudioRenderer_Mailbox render_mailbox{};
168 /// Mailbox lock ffor the render mailbox
169 std::mutex mailbox_lock;
170};
171
172} // namespace AudioRenderer::ADSP
173} // namespace AudioCore
diff --git a/src/audio_core/renderer/adsp/audio_renderer.cpp b/src/audio_core/renderer/adsp/audio_renderer.cpp
new file mode 100644
index 000000000..3967ccfe6
--- /dev/null
+++ b/src/audio_core/renderer/adsp/audio_renderer.cpp
@@ -0,0 +1,226 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <array>
5#include <chrono>
6
7#include "audio_core/audio_core.h"
8#include "audio_core/common/common.h"
9#include "audio_core/renderer/adsp/audio_renderer.h"
10#include "audio_core/sink/sink.h"
11#include "common/logging/log.h"
12#include "common/microprofile.h"
13#include "common/thread.h"
14#include "core/core.h"
15#include "core/core_timing.h"
16#include "core/core_timing_util.h"
17
18MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97));
19
20namespace AudioCore::AudioRenderer::ADSP {
21
22void AudioRenderer_Mailbox::HostSendMessage(RenderMessage message_) {
23 adsp_messages.enqueue(message_);
24 adsp_event.Set();
25}
26
27RenderMessage AudioRenderer_Mailbox::HostWaitMessage() {
28 host_event.Wait();
29 RenderMessage msg{RenderMessage::Invalid};
30 if (!host_messages.try_dequeue(msg)) {
31 LOG_ERROR(Service_Audio, "Failed to dequeue host message!");
32 }
33 return msg;
34}
35
36void AudioRenderer_Mailbox::ADSPSendMessage(const RenderMessage message_) {
37 host_messages.enqueue(message_);
38 host_event.Set();
39}
40
41RenderMessage AudioRenderer_Mailbox::ADSPWaitMessage() {
42 adsp_event.Wait();
43 RenderMessage msg{RenderMessage::Invalid};
44 if (!adsp_messages.try_dequeue(msg)) {
45 LOG_ERROR(Service_Audio, "Failed to dequeue ADSP message!");
46 }
47 return msg;
48}
49
50CommandBuffer& AudioRenderer_Mailbox::GetCommandBuffer(const s32 session_id) {
51 return command_buffers[session_id];
52}
53
54void AudioRenderer_Mailbox::SetCommandBuffer(const u32 session_id, CommandBuffer& buffer) {
55 command_buffers[session_id] = buffer;
56}
57
58u64 AudioRenderer_Mailbox::GetRenderTimeTaken() const {
59 return command_buffers[0].render_time_taken + command_buffers[1].render_time_taken;
60}
61
62u64 AudioRenderer_Mailbox::GetSignalledTick() const {
63 return signalled_tick;
64}
65
66void AudioRenderer_Mailbox::SetSignalledTick(const u64 tick) {
67 signalled_tick = tick;
68}
69
70void AudioRenderer_Mailbox::ClearRemainCount(const u32 session_id) {
71 command_buffers[session_id].remaining_command_count = 0;
72}
73
74u32 AudioRenderer_Mailbox::GetRemainCommandCount(const u32 session_id) const {
75 return command_buffers[session_id].remaining_command_count;
76}
77
78void AudioRenderer_Mailbox::ClearCommandBuffers() {
79 command_buffers[0].buffer = 0;
80 command_buffers[0].size = 0;
81 command_buffers[0].reset_buffers = false;
82 command_buffers[1].buffer = 0;
83 command_buffers[1].size = 0;
84 command_buffers[1].reset_buffers = false;
85}
86
87AudioRenderer::AudioRenderer(Core::System& system_)
88 : system{system_}, sink{system.AudioCore().GetOutputSink()} {
89 CreateSinkStreams();
90}
91
92AudioRenderer::~AudioRenderer() {
93 Stop();
94 for (auto& stream : streams) {
95 if (stream) {
96 sink.CloseStream(stream);
97 }
98 stream = nullptr;
99 }
100}
101
102void AudioRenderer::Start(AudioRenderer_Mailbox* mailbox_) {
103 if (running) {
104 return;
105 }
106
107 mailbox = mailbox_;
108 thread = std::thread(&AudioRenderer::ThreadFunc, this);
109 for (auto& stream : streams) {
110 stream->Start();
111 }
112 running = true;
113}
114
115void AudioRenderer::Stop() {
116 if (!running) {
117 return;
118 }
119
120 for (auto& stream : streams) {
121 stream->Stop();
122 }
123 thread.join();
124 running = false;
125}
126
127void AudioRenderer::CreateSinkStreams() {
128 u32 channels{sink.GetDeviceChannels()};
129 for (u32 i = 0; i < MaxRendererSessions; i++) {
130 std::string name{fmt::format("ADSP_RenderStream-{}", i)};
131 streams[i] =
132 sink.AcquireSinkStream(system, channels, name, ::AudioCore::Sink::StreamType::Render);
133 }
134}
135
136void AudioRenderer::ThreadFunc() {
137 constexpr char name[]{"yuzu:AudioRenderer"};
138 MicroProfileOnThreadCreate(name);
139 Common::SetCurrentThreadName(name);
140 Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
141 if (mailbox->ADSPWaitMessage() != RenderMessage::AudioRenderer_InitializeOK) {
142 LOG_ERROR(Service_Audio,
143 "ADSP Audio Renderer -- Failed to receive initialize message from host!");
144 return;
145 }
146
147 mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_InitializeOK);
148
149 constexpr u64 max_process_time{2'304'000ULL};
150
151 while (true) {
152 auto message{mailbox->ADSPWaitMessage()};
153 switch (message) {
154 case RenderMessage::AudioRenderer_Shutdown:
155 mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_Shutdown);
156 return;
157
158 case RenderMessage::AudioRenderer_Render: {
159 std::array<bool, MaxRendererSessions> buffers_reset{};
160 std::array<u64, MaxRendererSessions> render_times_taken{};
161 const auto start_time{system.CoreTiming().GetClockTicks()};
162
163 for (u32 index = 0; index < 2; index++) {
164 auto& command_buffer{mailbox->GetCommandBuffer(index)};
165 auto& command_list_processor{command_list_processors[index]};
166
167 // Check this buffer is valid, as it may not be used.
168 if (command_buffer.buffer != 0) {
169 // If there are no remaining commands (from the previous list),
170 // this is a new command list, initalize it.
171 if (command_buffer.remaining_command_count == 0) {
172 command_list_processor.Initialize(system, command_buffer.buffer,
173 command_buffer.size, streams[index]);
174 }
175
176 if (command_buffer.reset_buffers && !buffers_reset[index]) {
177 streams[index]->ClearQueue();
178 buffers_reset[index] = true;
179 }
180
181 u64 max_time{max_process_time};
182 if (index == 1 && command_buffer.applet_resource_user_id ==
183 mailbox->GetCommandBuffer(0).applet_resource_user_id) {
184 max_time = max_process_time -
185 Core::Timing::CyclesToNs(render_times_taken[0]).count();
186 if (render_times_taken[0] > max_process_time) {
187 max_time = 0;
188 }
189 }
190
191 max_time = std::min(command_buffer.time_limit, max_time);
192 command_list_processor.SetProcessTimeMax(max_time);
193
194 // Process the command list
195 {
196 MICROPROFILE_SCOPE(Audio_Renderer);
197 render_times_taken[index] =
198 command_list_processor.Process(index) - start_time;
199 }
200
201 if (index == 0) {
202 auto stream{command_list_processor.GetOutputSinkStream()};
203 system.AudioCore().SetStreamQueue(stream->GetQueueSize());
204 }
205
206 const auto end_time{system.CoreTiming().GetClockTicks()};
207
208 command_buffer.remaining_command_count =
209 command_list_processor.GetRemainingCommandCount();
210 command_buffer.render_time_taken = end_time - start_time;
211 }
212 }
213
214 mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_RenderResponse);
215 } break;
216
217 default:
218 LOG_WARNING(Service_Audio,
219 "ADSP AudioRenderer received an invalid message, msg={:02X}!",
220 static_cast<u32>(message));
221 break;
222 }
223 }
224}
225
226} // namespace AudioCore::AudioRenderer::ADSP
diff --git a/src/audio_core/renderer/adsp/audio_renderer.h b/src/audio_core/renderer/adsp/audio_renderer.h
new file mode 100644
index 000000000..b6ced9d2b
--- /dev/null
+++ b/src/audio_core/renderer/adsp/audio_renderer.h
@@ -0,0 +1,203 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <memory>
8#include <thread>
9
10#include "audio_core/renderer/adsp/command_buffer.h"
11#include "audio_core/renderer/adsp/command_list_processor.h"
12#include "common/common_types.h"
13#include "common/reader_writer_queue.h"
14#include "common/thread.h"
15
16namespace Core {
17namespace Timing {
18struct EventType;
19}
20class System;
21} // namespace Core
22
23namespace AudioCore {
24namespace Sink {
25class Sink;
26}
27
28namespace AudioRenderer::ADSP {
29
30enum class RenderMessage {
31 /* 0x00 */ Invalid,
32 /* 0x01 */ AudioRenderer_MapUnmap_Map,
33 /* 0x02 */ AudioRenderer_MapUnmap_MapResponse,
34 /* 0x03 */ AudioRenderer_MapUnmap_Unmap,
35 /* 0x04 */ AudioRenderer_MapUnmap_UnmapResponse,
36 /* 0x05 */ AudioRenderer_MapUnmap_InvalidateCache,
37 /* 0x06 */ AudioRenderer_MapUnmap_InvalidateCacheResponse,
38 /* 0x07 */ AudioRenderer_MapUnmap_Shutdown,
39 /* 0x08 */ AudioRenderer_MapUnmap_ShutdownResponse,
40 /* 0x16 */ AudioRenderer_InitializeOK = 0x16,
41 /* 0x20 */ AudioRenderer_RenderResponse = 0x20,
42 /* 0x2A */ AudioRenderer_Render = 0x2A,
43 /* 0x34 */ AudioRenderer_Shutdown = 0x34,
44};
45
46/**
47 * A mailbox for the AudioRenderer, allowing communication between the host and the AudioRenderer
48 * running on the ADSP.
49 */
50class AudioRenderer_Mailbox {
51public:
52 /**
53 * Send a message from the host to the AudioRenderer.
54 *
55 * @param message_ - The message to send to the AudioRenderer.
56 */
57 void HostSendMessage(RenderMessage message);
58
59 /**
60 * Host wait for a message from the AudioRenderer.
61 *
62 * @return The message returned from the AudioRenderer.
63 */
64 RenderMessage HostWaitMessage();
65
66 /**
67 * Send a message from the AudioRenderer to the host.
68 *
69 * @param message_ - The message to send to the host.
70 */
71 void ADSPSendMessage(RenderMessage message);
72
73 /**
74 * AudioRenderer wait for a message from the host.
75 *
76 * @return The message returned from the AudioRenderer.
77 */
78 RenderMessage ADSPWaitMessage();
79
80 /**
81 * Get the command buffer with the given session id (0 or 1).
82 *
83 * @param session_id - The session id to get (0 or 1).
84 * @return The command buffer.
85 */
86 CommandBuffer& GetCommandBuffer(s32 session_id);
87
88 /**
89 * Set the command buffer with the given session id (0 or 1).
90 *
91 * @param session_id - The session id to get (0 or 1).
92 * @param buffer - The command buffer to set.
93 */
94 void SetCommandBuffer(u32 session_id, CommandBuffer& buffer);
95
96 /**
97 * Get the total render time taken for the last command lists sent.
98 *
99 * @return Total render time taken for the last command lists.
100 */
101 u64 GetRenderTimeTaken() const;
102
103 /**
104 * Get the tick the AudioRenderer was signalled.
105 *
106 * @return The tick the AudioRenderer was signalled.
107 */
108 u64 GetSignalledTick() const;
109
110 /**
111 * Set the tick the AudioRenderer was signalled.
112 *
113 * @param tick - The tick the AudioRenderer was signalled.
114 */
115 void SetSignalledTick(u64 tick);
116
117 /**
118 * Clear the remaining command count.
119 *
120 * @param session_id - Index for which command list to clear (0 or 1).
121 */
122 void ClearRemainCount(u32 session_id);
123
124 /**
125 * Get the remaining command count for a given command list.
126 *
127 * @param session_id - Index for which command list to clear (0 or 1).
128 * @return The remaining command count.
129 */
130 u32 GetRemainCommandCount(u32 session_id) const;
131
132 /**
133 * Clear the command buffers (does not clear the time taken or the remaining command count).
134 */
135 void ClearCommandBuffers();
136
137private:
138 /// Host signalling event
139 Common::Event host_event{};
140 /// AudioRenderer signalling event
141 Common::Event adsp_event{};
142 /// Host message queue
143
144 Common::ReaderWriterQueue<RenderMessage> host_messages{};
145 /// AudioRenderer message queue
146
147 Common::ReaderWriterQueue<RenderMessage> adsp_messages{};
148 /// Command buffers
149
150 std::array<CommandBuffer, MaxRendererSessions> command_buffers{};
151 /// Tick the AudioRnederer was signalled
152 u64 signalled_tick{};
153};
154
155/**
156 * The AudioRenderer application running on the ADSP.
157 */
158class AudioRenderer {
159public:
160 explicit AudioRenderer(Core::System& system);
161 ~AudioRenderer();
162
163 /**
164 * Start the AudioRenderer.
165 *
166 * @param The mailbox to use for this session.
167 */
168 void Start(AudioRenderer_Mailbox* mailbox);
169
170 /**
171 * Stop the AudioRenderer.
172 */
173 void Stop();
174
175private:
176 /**
177 * Main AudioRenderer thread, responsible for processing the command lists.
178 */
179 void ThreadFunc();
180
181 /**
182 * Creates the streams which will receive the processed samples.
183 */
184 void CreateSinkStreams();
185
186 /// Core system
187 Core::System& system;
188 /// Main thread
189 std::thread thread{};
190 /// The current state
191 std::atomic<bool> running{};
192 /// The active mailbox
193 AudioRenderer_Mailbox* mailbox{};
194 /// The command lists to process
195 std::array<CommandListProcessor, MaxRendererSessions> command_list_processors{};
196 /// The output sink the AudioRenderer will use
197 Sink::Sink& sink;
198 /// The streams which will receive the processed samples
199 std::array<Sink::SinkStream*, MaxRendererSessions> streams;
200};
201
202} // namespace AudioRenderer::ADSP
203} // namespace AudioCore
diff --git a/src/audio_core/renderer/adsp/command_buffer.h b/src/audio_core/renderer/adsp/command_buffer.h
new file mode 100644
index 000000000..880b279d8
--- /dev/null
+++ b/src/audio_core/renderer/adsp/command_buffer.h
@@ -0,0 +1,21 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "audio_core/common/common.h"
7#include "common/common_types.h"
8
9namespace AudioCore::AudioRenderer::ADSP {
10
11struct CommandBuffer {
12 CpuAddr buffer;
13 u64 size;
14 u64 time_limit;
15 u32 remaining_command_count;
16 bool reset_buffers;
17 u64 applet_resource_user_id;
18 u64 render_time_taken;
19};
20
21} // namespace AudioCore::AudioRenderer::ADSP
diff --git a/src/audio_core/renderer/adsp/command_list_processor.cpp b/src/audio_core/renderer/adsp/command_list_processor.cpp
new file mode 100644
index 000000000..e3bf2d7ec
--- /dev/null
+++ b/src/audio_core/renderer/adsp/command_list_processor.cpp
@@ -0,0 +1,109 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <string>
5
6#include "audio_core/renderer/adsp/command_list_processor.h"
7#include "audio_core/renderer/command/command_list_header.h"
8#include "audio_core/renderer/command/commands.h"
9#include "common/settings.h"
10#include "core/core.h"
11#include "core/core_timing.h"
12#include "core/core_timing_util.h"
13#include "core/memory.h"
14
15namespace AudioCore::AudioRenderer::ADSP {
16
17void CommandListProcessor::Initialize(Core::System& system_, CpuAddr buffer, u64 size,
18 Sink::SinkStream* stream_) {
19 system = &system_;
20 memory = &system->Memory();
21 stream = stream_;
22 header = reinterpret_cast<CommandListHeader*>(buffer);
23 commands = reinterpret_cast<u8*>(buffer + sizeof(CommandListHeader));
24 commands_buffer_size = size;
25 command_count = header->command_count;
26 sample_count = header->sample_count;
27 target_sample_rate = header->sample_rate;
28 mix_buffers = header->samples_buffer;
29 buffer_count = header->buffer_count;
30 processed_command_count = 0;
31}
32
33void CommandListProcessor::SetProcessTimeMax(const u64 time) {
34 max_process_time = time;
35}
36
37u32 CommandListProcessor::GetRemainingCommandCount() const {
38 return command_count - processed_command_count;
39}
40
41void CommandListProcessor::SetBuffer(const CpuAddr buffer, const u64 size) {
42 commands = reinterpret_cast<u8*>(buffer + sizeof(CommandListHeader));
43 commands_buffer_size = size;
44}
45
46Sink::SinkStream* CommandListProcessor::GetOutputSinkStream() const {
47 return stream;
48}
49
50u64 CommandListProcessor::Process(u32 session_id) {
51 const auto start_time_{system->CoreTiming().GetClockTicks()};
52 const auto command_base{CpuAddr(commands)};
53
54 if (processed_command_count > 0) {
55 current_processing_time += start_time_ - end_time;
56 } else {
57 start_time = start_time_;
58 current_processing_time = 0;
59 }
60
61 std::string dump{fmt::format("\nSession {}\n", session_id)};
62
63 for (u32 index = 0; index < command_count; index++) {
64 auto& command{*reinterpret_cast<ICommand*>(commands)};
65
66 if (command.magic != 0xCAFEBABE) {
67 LOG_ERROR(Service_Audio, "Command has invalid magic! Expected 0xCAFEBABE, got {:08X}",
68 command.magic);
69 return system->CoreTiming().GetClockTicks() - start_time_;
70 }
71
72 auto current_offset{CpuAddr(commands) - command_base};
73
74 if (current_offset + command.size > commands_buffer_size) {
75 LOG_ERROR(Service_Audio,
76 "Command exceeded command buffer, buffer size {:08X}, command ends at {:08X}",
77 commands_buffer_size,
78 CpuAddr(commands) + command.size - sizeof(CommandListHeader));
79 return system->CoreTiming().GetClockTicks() - start_time_;
80 }
81
82 if (Settings::values.dump_audio_commands) {
83 command.Dump(*this, dump);
84 }
85
86 if (!command.Verify(*this)) {
87 break;
88 }
89
90 if (command.enabled) {
91 command.Process(*this);
92 } else {
93 dump += fmt::format("\tDisabled!\n");
94 }
95
96 processed_command_count++;
97 commands += command.size;
98 }
99
100 if (Settings::values.dump_audio_commands && dump != last_dump) {
101 LOG_WARNING(Service_Audio, "{}", dump);
102 last_dump = dump;
103 }
104
105 end_time = system->CoreTiming().GetClockTicks();
106 return end_time - start_time_;
107}
108
109} // namespace AudioCore::AudioRenderer::ADSP
diff --git a/src/audio_core/renderer/adsp/command_list_processor.h b/src/audio_core/renderer/adsp/command_list_processor.h
new file mode 100644
index 000000000..3f99173e3
--- /dev/null
+++ b/src/audio_core/renderer/adsp/command_list_processor.h
@@ -0,0 +1,118 @@
1// SPDX-FileCopyrightText: Copyright 2022 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/common/common.h"
9#include "common/common_types.h"
10
11namespace Core {
12namespace Memory {
13class Memory;
14}
15class System;
16} // namespace Core
17
18namespace AudioCore {
19namespace Sink {
20class SinkStream;
21}
22
23namespace AudioRenderer {
24struct CommandListHeader;
25
26namespace ADSP {
27
28/**
29 * A processor for command lists given to the AudioRenderer.
30 */
31class CommandListProcessor {
32public:
33 /**
34 * Initialize the processor.
35 *
36 * @param system_ - The core system.
37 * @param buffer - The command buffer to process.
38 * @param size - The size of the buffer.
39 * @param stream_ - The stream to be used for sending the samples.
40 */
41 void Initialize(Core::System& system, CpuAddr buffer, u64 size, Sink::SinkStream* stream);
42
43 /**
44 * Set the maximum processing time for this command list.
45 *
46 * @param time - The maximum process time.
47 */
48 void SetProcessTimeMax(u64 time);
49
50 /**
51 * Get the remaining command count for this list.
52 *
53 * @return The remaining command count.
54 */
55 u32 GetRemainingCommandCount() const;
56
57 /**
58 * Set the command buffer.
59 *
60 * @param buffer - The buffer to use.
61 * @param size - The size of the buffer.
62 */
63 void SetBuffer(CpuAddr buffer, u64 size);
64
65 /**
66 * Get the stream for this command list.
67 *
68 * @return The stream associated with this command list.
69 */
70 Sink::SinkStream* GetOutputSinkStream() const;
71
72 /**
73 * Process the command list.
74 *
75 * @param index - Index of the current command list.
76 * @return The time taken to process.
77 */
78 u64 Process(u32 session_id);
79
80 /// Core system
81 Core::System* system{};
82 /// Core memory
83 Core::Memory::Memory* memory{};
84 /// Stream for the processed samples
85 Sink::SinkStream* stream{};
86 /// Header info for this command list
87 CommandListHeader* header{};
88 /// The command buffer
89 u8* commands{};
90 /// The command buffer size
91 u64 commands_buffer_size{};
92 /// The maximum processing time alloted
93 u64 max_process_time{};
94 /// The number of commands in the buffer
95 u32 command_count{};
96 /// The target sample count for output
97 u32 sample_count{};
98 /// The target sample rate for output
99 u32 target_sample_rate{};
100 /// The mixing buffers used by the commands
101 std::span<s32> mix_buffers{};
102 /// The number of mix buffers
103 u32 buffer_count{};
104 /// The number of processed commands so far
105 u32 processed_command_count{};
106 /// The processing start time of this list
107 u64 start_time{};
108 /// The current processing time for this list
109 u64 current_processing_time{};
110 /// The end processing time for this list
111 u64 end_time{};
112 /// Last command list string generated, used for dumping audio commands to console
113 std::string last_dump{};
114};
115
116} // namespace ADSP
117} // namespace AudioRenderer
118} // namespace AudioCore