summaryrefslogtreecommitdiff
path: root/src/audio_core/renderer/voice
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/voice
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/voice')
-rw-r--r--src/audio_core/renderer/voice/voice_channel_resource.h38
-rw-r--r--src/audio_core/renderer/voice/voice_context.cpp86
-rw-r--r--src/audio_core/renderer/voice/voice_context.h126
-rw-r--r--src/audio_core/renderer/voice/voice_info.cpp408
-rw-r--r--src/audio_core/renderer/voice/voice_info.h378
-rw-r--r--src/audio_core/renderer/voice/voice_state.h70
6 files changed, 1106 insertions, 0 deletions
diff --git a/src/audio_core/renderer/voice/voice_channel_resource.h b/src/audio_core/renderer/voice/voice_channel_resource.h
new file mode 100644
index 000000000..26ab4ccce
--- /dev/null
+++ b/src/audio_core/renderer/voice/voice_channel_resource.h
@@ -0,0 +1,38 @@
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
8#include "audio_core/common/common.h"
9#include "common/common_types.h"
10
11namespace AudioCore::AudioRenderer {
12/**
13 * Represents one channel for mixing a voice.
14 */
15class VoiceChannelResource {
16public:
17 struct InParameter {
18 /* 0x00 */ u32 id;
19 /* 0x04 */ std::array<f32, MaxMixBuffers> mix_volumes;
20 /* 0x64 */ bool in_use;
21 /* 0x65 */ char unk65[0xB];
22 };
23 static_assert(sizeof(InParameter) == 0x70,
24 "VoiceChannelResource::InParameter has the wrong size!");
25
26 explicit VoiceChannelResource(u32 id_) : id{id_} {}
27
28 /// Current volume for each mix buffer
29 std::array<f32, MaxMixBuffers> mix_volumes{};
30 /// Previous volume for each mix buffer
31 std::array<f32, MaxMixBuffers> prev_mix_volumes{};
32 /// Id of this resource
33 const u32 id;
34 /// Is this resource in use?
35 bool in_use{};
36};
37
38} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/voice/voice_context.cpp b/src/audio_core/renderer/voice/voice_context.cpp
new file mode 100644
index 000000000..eafb51b01
--- /dev/null
+++ b/src/audio_core/renderer/voice/voice_context.cpp
@@ -0,0 +1,86 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <ranges>
5
6#include "audio_core/renderer/voice/voice_context.h"
7
8namespace AudioCore::AudioRenderer {
9
10VoiceState& VoiceContext::GetDspSharedState(const u32 index) {
11 if (index >= dsp_states.size()) {
12 LOG_ERROR(Service_Audio, "Invalid voice dsp state index {:04X}", index);
13 }
14 return dsp_states[index];
15}
16
17VoiceChannelResource& VoiceContext::GetChannelResource(const u32 index) {
18 if (index >= channel_resources.size()) {
19 LOG_ERROR(Service_Audio, "Invalid voice channel resource index {:04X}", index);
20 }
21 return channel_resources[index];
22}
23
24void VoiceContext::Initialize(std::span<VoiceInfo*> sorted_voice_infos_,
25 std::span<VoiceInfo> voice_infos_,
26 std::span<VoiceChannelResource> voice_channel_resources_,
27 std::span<VoiceState> cpu_states_, std::span<VoiceState> dsp_states_,
28 const u32 voice_count_) {
29 sorted_voice_info = sorted_voice_infos_;
30 voices = voice_infos_;
31 channel_resources = voice_channel_resources_;
32 cpu_states = cpu_states_;
33 dsp_states = dsp_states_;
34 voice_count = voice_count_;
35 active_count = 0;
36}
37
38VoiceInfo* VoiceContext::GetSortedInfo(const u32 index) {
39 if (index >= sorted_voice_info.size()) {
40 LOG_ERROR(Service_Audio, "Invalid voice sorted info index {:04X}", index);
41 }
42 return sorted_voice_info[index];
43}
44
45VoiceInfo& VoiceContext::GetInfo(const u32 index) {
46 if (index >= voices.size()) {
47 LOG_ERROR(Service_Audio, "Invalid voice info index {:04X}", index);
48 }
49 return voices[index];
50}
51
52VoiceState& VoiceContext::GetState(const u32 index) {
53 if (index >= cpu_states.size()) {
54 LOG_ERROR(Service_Audio, "Invalid voice cpu state index {:04X}", index);
55 }
56 return cpu_states[index];
57}
58
59u32 VoiceContext::GetCount() const {
60 return voice_count;
61}
62
63u32 VoiceContext::GetActiveCount() const {
64 return active_count;
65}
66
67void VoiceContext::SetActiveCount(const u32 active_count_) {
68 active_count = active_count_;
69}
70
71void VoiceContext::SortInfo() {
72 for (u32 i = 0; i < voice_count; i++) {
73 sorted_voice_info[i] = &voices[i];
74 }
75
76 std::ranges::sort(sorted_voice_info, [](const VoiceInfo* a, const VoiceInfo* b) {
77 return a->priority != b->priority ? a->priority < b->priority
78 : a->sort_order < b->sort_order;
79 });
80}
81
82void VoiceContext::UpdateStateByDspShared() {
83 std::memcpy(cpu_states.data(), dsp_states.data(), voice_count * sizeof(VoiceState));
84}
85
86} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/voice/voice_context.h b/src/audio_core/renderer/voice/voice_context.h
new file mode 100644
index 000000000..43b677154
--- /dev/null
+++ b/src/audio_core/renderer/voice/voice_context.h
@@ -0,0 +1,126 @@
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/renderer/voice/voice_channel_resource.h"
9#include "audio_core/renderer/voice/voice_info.h"
10#include "audio_core/renderer/voice/voice_state.h"
11#include "common/common_types.h"
12
13namespace AudioCore::AudioRenderer {
14/**
15 * Contains all voices, with utility functions for managing them.
16 */
17class VoiceContext {
18public:
19 /**
20 * Get the AudioRenderer state for a given index
21 *
22 * @param index - State index to get.
23 * @return The requested voice state.
24 */
25 VoiceState& GetDspSharedState(u32 index);
26
27 /**
28 * Get the channel resource for a given index
29 *
30 * @param index - Resource index to get.
31 * @return The requested voice resource.
32 */
33 VoiceChannelResource& GetChannelResource(u32 index);
34
35 /**
36 * Initialize the voice context.
37 *
38 * @param sorted_voice_infos - Workbuffer for the sorted voices.
39 * @param voice_infos - Workbuffer for the voices.
40 * @param voice_channel_resources - Workbuffer for the voice channel resources.
41 * @param cpu_states - Workbuffer for the host-side voice states.
42 * @param dsp_states - Workbuffer for the AudioRenderer-side voice states.
43 * @param voice_count - The number of voices in each workbuffer.
44 */
45 void Initialize(std::span<VoiceInfo*> sorted_voice_infos, std::span<VoiceInfo> voice_infos,
46 std::span<VoiceChannelResource> voice_channel_resources,
47 std::span<VoiceState> cpu_states, std::span<VoiceState> dsp_states,
48 u32 voice_count);
49
50 /**
51 * Get a sorted voice with the given index.
52 *
53 * @param index - The sorted voice index to get.
54 * @return The sorted voice.
55 */
56 VoiceInfo* GetSortedInfo(u32 index);
57
58 /**
59 * Get a voice with the given index.
60 *
61 * @param index - The voice index to get.
62 * @return The voice.
63 */
64 VoiceInfo& GetInfo(u32 index);
65
66 /**
67 * Get a host voice state with the given index.
68 *
69 * @param index - The host voice state index to get.
70 * @return The voice state.
71 */
72 VoiceState& GetState(u32 index);
73
74 /**
75 * Get the maximum number of voices.
76 * Not all voices in the buffers may be in use, see GetActiveCount.
77 *
78 * @return The maximum number of voices.
79 */
80 u32 GetCount() const;
81
82 /**
83 * Get the number of active voices.
84 * Can be less than or equal to the maximum number of voices.
85 *
86 * @return The number of active voices.
87 */
88 u32 GetActiveCount() const;
89
90 /**
91 * Set the number of active voices.
92 * Can be less than or equal to the maximum number of voices.
93 *
94 * @param active_count - The new number of active voices.
95 */
96 void SetActiveCount(u32 active_count);
97
98 /**
99 * Sort all voices. Results are available via GetSortedInfo.
100 * Voices are sorted descendingly, according to priority, and then sort order.
101 */
102 void SortInfo();
103
104 /**
105 * Update all voice states, copying AudioRenderer-side states to host-side states.
106 */
107 void UpdateStateByDspShared();
108
109private:
110 /// Sorted voices
111 std::span<VoiceInfo*> sorted_voice_info{};
112 /// Voices
113 std::span<VoiceInfo> voices{};
114 /// Channel resources
115 std::span<VoiceChannelResource> channel_resources{};
116 /// Host-side voice states
117 std::span<VoiceState> cpu_states{};
118 /// AudioRenderer-side voice states
119 std::span<VoiceState> dsp_states{};
120 /// Maximum number of voices
121 u32 voice_count{};
122 /// Number of active voices
123 u32 active_count{};
124};
125
126} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/voice/voice_info.cpp b/src/audio_core/renderer/voice/voice_info.cpp
new file mode 100644
index 000000000..1849eeb57
--- /dev/null
+++ b/src/audio_core/renderer/voice/voice_info.cpp
@@ -0,0 +1,408 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/memory/pool_mapper.h"
5#include "audio_core/renderer/voice/voice_context.h"
6#include "audio_core/renderer/voice/voice_info.h"
7#include "audio_core/renderer/voice/voice_state.h"
8
9namespace AudioCore::AudioRenderer {
10
11VoiceInfo::VoiceInfo() {
12 Initialize();
13}
14
15void VoiceInfo::Initialize() {
16 in_use = false;
17 is_new = false;
18 id = 0;
19 node_id = 0;
20 current_play_state = ServerPlayState::Stopped;
21 src_quality = SrcQuality::Medium;
22 priority = LowestVoicePriority;
23 sample_format = SampleFormat::Invalid;
24 sample_rate = 0;
25 channel_count = 0;
26 wave_buffer_count = 0;
27 wave_buffer_index = 0;
28 pitch = 0.0f;
29 volume = 0.0f;
30 prev_volume = 0.0f;
31 mix_id = UnusedMixId;
32 splitter_id = UnusedSplitterId;
33 biquads = {};
34 biquad_initialized = {};
35 voice_dropped = false;
36 data_unmapped = false;
37 buffer_unmapped = false;
38 flush_buffer_count = 0;
39
40 data_address.Setup(0, 0);
41 for (auto& wavebuffer : wavebuffers) {
42 wavebuffer.Initialize();
43 }
44}
45
46bool VoiceInfo::ShouldUpdateParameters(const InParameter& params) const {
47 return data_address.GetCpuAddr() != params.src_data_address ||
48 data_address.GetSize() != params.src_data_size || data_unmapped;
49}
50
51void VoiceInfo::UpdateParameters(BehaviorInfo::ErrorInfo& error_info, const InParameter& params,
52 const PoolMapper& pool_mapper, const BehaviorInfo& behavior) {
53 in_use = params.in_use;
54 id = params.id;
55 node_id = params.node_id;
56 UpdatePlayState(params.play_state);
57 UpdateSrcQuality(params.src_quality);
58 priority = params.priority;
59 sort_order = params.sort_order;
60 sample_rate = params.sample_rate;
61 sample_format = params.sample_format;
62 channel_count = static_cast<s8>(params.channel_count);
63 pitch = params.pitch;
64 volume = params.volume;
65 biquads = params.biquads;
66 wave_buffer_count = params.wave_buffer_count;
67 wave_buffer_index = params.wave_buffer_index;
68
69 if (behavior.IsFlushVoiceWaveBuffersSupported()) {
70 flush_buffer_count += params.flush_buffer_count;
71 }
72
73 mix_id = params.mix_id;
74
75 if (behavior.IsSplitterSupported()) {
76 splitter_id = params.splitter_id;
77 } else {
78 splitter_id = UnusedSplitterId;
79 }
80
81 channel_resource_ids = params.channel_resource_ids;
82
83 flags &= u16(~0b11);
84 if (behavior.IsVoicePlayedSampleCountResetAtLoopPointSupported()) {
85 flags |= u16(params.flags.IsVoicePlayedSampleCountResetAtLoopPointSupported);
86 }
87
88 if (behavior.IsVoicePitchAndSrcSkippedSupported()) {
89 flags |= u16(params.flags.IsVoicePitchAndSrcSkippedSupported);
90 }
91
92 if (params.clear_voice_drop) {
93 voice_dropped = false;
94 }
95
96 if (ShouldUpdateParameters(params)) {
97 data_unmapped = !pool_mapper.TryAttachBuffer(error_info, data_address,
98 params.src_data_address, params.src_data_size);
99 } else {
100 error_info.error_code = ResultSuccess;
101 error_info.address = CpuAddr(0);
102 }
103}
104
105void VoiceInfo::UpdatePlayState(const PlayState state) {
106 last_play_state = current_play_state;
107
108 switch (state) {
109 case PlayState::Started:
110 current_play_state = ServerPlayState::Started;
111 break;
112 case PlayState::Stopped:
113 if (current_play_state != ServerPlayState::Stopped) {
114 current_play_state = ServerPlayState::RequestStop;
115 }
116 break;
117 case PlayState::Paused:
118 current_play_state = ServerPlayState::Paused;
119 break;
120 default:
121 LOG_ERROR(Service_Audio, "Invalid input play state {}", static_cast<u32>(state));
122 break;
123 }
124}
125
126void VoiceInfo::UpdateSrcQuality(const SrcQuality quality) {
127 switch (quality) {
128 case SrcQuality::Medium:
129 src_quality = quality;
130 break;
131 case SrcQuality::High:
132 src_quality = quality;
133 break;
134 case SrcQuality::Low:
135 src_quality = quality;
136 break;
137 default:
138 LOG_ERROR(Service_Audio, "Invalid input src quality {}", static_cast<u32>(quality));
139 break;
140 }
141}
142
143void VoiceInfo::UpdateWaveBuffers(std::span<std::array<BehaviorInfo::ErrorInfo, 2>> error_infos,
144 [[maybe_unused]] u32 error_count, const InParameter& params,
145 std::span<VoiceState*> voice_states,
146 const PoolMapper& pool_mapper, const BehaviorInfo& behavior) {
147 if (params.is_new) {
148 for (size_t i = 0; i < wavebuffers.size(); i++) {
149 wavebuffers[i].Initialize();
150 }
151
152 for (s8 channel = 0; channel < static_cast<s8>(params.channel_count); channel++) {
153 voice_states[channel]->wave_buffer_valid.fill(false);
154 }
155 }
156
157 for (u32 i = 0; i < MaxWaveBuffers; i++) {
158 UpdateWaveBuffer(error_infos[i], wavebuffers[i], params.wave_buffer_internal[i],
159 params.sample_format, voice_states[0]->wave_buffer_valid[i], pool_mapper,
160 behavior);
161 }
162}
163
164void VoiceInfo::UpdateWaveBuffer(std::span<BehaviorInfo::ErrorInfo> error_info,
165 WaveBuffer& wave_buffer,
166 const WaveBufferInternal& wave_buffer_internal,
167 const SampleFormat sample_format_, const bool valid,
168 const PoolMapper& pool_mapper, const BehaviorInfo& behavior) {
169 if (!valid && wave_buffer.sent_to_DSP && wave_buffer.buffer_address.GetCpuAddr() != 0) {
170 pool_mapper.ForceUnmapPointer(wave_buffer.buffer_address);
171 wave_buffer.buffer_address.Setup(0, 0);
172 }
173
174 if (!ShouldUpdateWaveBuffer(wave_buffer_internal)) {
175 return;
176 }
177
178 switch (sample_format_) {
179 case SampleFormat::PcmInt16: {
180 constexpr auto byte_size{GetSampleFormatByteSize(SampleFormat::PcmInt16)};
181 if (wave_buffer_internal.start_offset * byte_size > wave_buffer_internal.size ||
182 wave_buffer_internal.end_offset * byte_size > wave_buffer_internal.size) {
183 LOG_ERROR(Service_Audio, "Invalid PCM16 start/end wavebuffer sizes!");
184 error_info[0].error_code = Service::Audio::ERR_INVALID_UPDATE_DATA;
185 error_info[0].address = wave_buffer_internal.address;
186 return;
187 }
188 } break;
189
190 case SampleFormat::PcmFloat: {
191 constexpr auto byte_size{GetSampleFormatByteSize(SampleFormat::PcmFloat)};
192 if (wave_buffer_internal.start_offset * byte_size > wave_buffer_internal.size ||
193 wave_buffer_internal.end_offset * byte_size > wave_buffer_internal.size) {
194 LOG_ERROR(Service_Audio, "Invalid PCMFloat start/end wavebuffer sizes!");
195 error_info[0].error_code = Service::Audio::ERR_INVALID_UPDATE_DATA;
196 error_info[0].address = wave_buffer_internal.address;
197 return;
198 }
199 } break;
200
201 case SampleFormat::Adpcm: {
202 const auto start_frame{wave_buffer_internal.start_offset / 14};
203 auto start_extra{wave_buffer_internal.start_offset % 14 == 0
204 ? 0
205 : (wave_buffer_internal.start_offset % 14) / 2 + 1 +
206 ((wave_buffer_internal.start_offset % 14) % 2)};
207 const auto start{start_frame * 8 + start_extra};
208
209 const auto end_frame{wave_buffer_internal.end_offset / 14};
210 const auto end_extra{wave_buffer_internal.end_offset % 14 == 0
211 ? 0
212 : (wave_buffer_internal.end_offset % 14) / 2 + 1 +
213 ((wave_buffer_internal.end_offset % 14) % 2)};
214 const auto end{end_frame * 8 + end_extra};
215
216 if (start > static_cast<s64>(wave_buffer_internal.size) ||
217 end > static_cast<s64>(wave_buffer_internal.size)) {
218 LOG_ERROR(Service_Audio, "Invalid ADPCM start/end wavebuffer sizes!");
219 error_info[0].error_code = Service::Audio::ERR_INVALID_UPDATE_DATA;
220 error_info[0].address = wave_buffer_internal.address;
221 return;
222 }
223 } break;
224
225 default:
226 break;
227 }
228
229 if (wave_buffer_internal.start_offset < 0 || wave_buffer_internal.end_offset < 0) {
230 LOG_ERROR(Service_Audio, "Invalid input start/end wavebuffer sizes!");
231 error_info[0].error_code = Service::Audio::ERR_INVALID_UPDATE_DATA;
232 error_info[0].address = wave_buffer_internal.address;
233 return;
234 }
235
236 wave_buffer.start_offset = wave_buffer_internal.start_offset;
237 wave_buffer.end_offset = wave_buffer_internal.end_offset;
238 wave_buffer.loop = wave_buffer_internal.loop;
239 wave_buffer.stream_ended = wave_buffer_internal.stream_ended;
240 wave_buffer.sent_to_DSP = false;
241 wave_buffer.loop_start_offset = wave_buffer_internal.loop_start;
242 wave_buffer.loop_end_offset = wave_buffer_internal.loop_end;
243 wave_buffer.loop_count = wave_buffer_internal.loop_count;
244
245 buffer_unmapped =
246 !pool_mapper.TryAttachBuffer(error_info[0], wave_buffer.buffer_address,
247 wave_buffer_internal.address, wave_buffer_internal.size);
248
249 if (sample_format_ == SampleFormat::Adpcm && behavior.IsAdpcmLoopContextBugFixed() &&
250 wave_buffer_internal.context_address != 0) {
251 buffer_unmapped = !pool_mapper.TryAttachBuffer(error_info[1], wave_buffer.context_address,
252 wave_buffer_internal.context_address,
253 wave_buffer_internal.context_size) ||
254 data_unmapped;
255 } else {
256 wave_buffer.context_address.Setup(0, 0);
257 }
258}
259
260bool VoiceInfo::ShouldUpdateWaveBuffer(const WaveBufferInternal& wave_buffer_internal) const {
261 return !wave_buffer_internal.sent_to_DSP || buffer_unmapped;
262}
263
264void VoiceInfo::WriteOutStatus(OutStatus& out_status, const InParameter& params,
265 std::span<VoiceState*> voice_states) {
266 if (params.is_new) {
267 is_new = true;
268 }
269
270 if (params.is_new || is_new) {
271 out_status.played_sample_count = 0;
272 out_status.wave_buffers_consumed = 0;
273 out_status.voice_dropped = false;
274 } else {
275 out_status.played_sample_count = voice_states[0]->played_sample_count;
276 out_status.wave_buffers_consumed = voice_states[0]->wave_buffers_consumed;
277 out_status.voice_dropped = voice_dropped;
278 }
279}
280
281bool VoiceInfo::ShouldSkip() const {
282 return !in_use || wave_buffer_count == 0 || data_unmapped || buffer_unmapped || voice_dropped;
283}
284
285bool VoiceInfo::HasAnyConnection() const {
286 return mix_id != UnusedMixId || splitter_id != UnusedSplitterId;
287}
288
289void VoiceInfo::FlushWaveBuffers(const u32 flush_count, std::span<VoiceState*> voice_states,
290 const s8 channel_count_) {
291 auto wave_index{wave_buffer_index};
292
293 for (size_t i = 0; i < flush_count; i++) {
294 wavebuffers[wave_index].sent_to_DSP = true;
295
296 for (s8 j = 0; j < channel_count_; j++) {
297 auto voice_state{voice_states[j]};
298 if (voice_state->wave_buffer_index == wave_index) {
299 voice_state->wave_buffer_index =
300 (voice_state->wave_buffer_index + 1) % MaxWaveBuffers;
301 voice_state->wave_buffers_consumed++;
302 }
303 voice_state->wave_buffer_valid[wave_index] = false;
304 }
305
306 wave_index = (wave_index + 1) % MaxWaveBuffers;
307 }
308}
309
310bool VoiceInfo::UpdateParametersForCommandGeneration(std::span<VoiceState*> voice_states) {
311 if (flush_buffer_count > 0) {
312 FlushWaveBuffers(flush_buffer_count, voice_states, channel_count);
313 flush_buffer_count = 0;
314 }
315
316 switch (current_play_state) {
317 case ServerPlayState::Started:
318 for (u32 i = 0; i < MaxWaveBuffers; i++) {
319 if (!wavebuffers[i].sent_to_DSP) {
320 for (s8 channel = 0; channel < channel_count; channel++) {
321 voice_states[channel]->wave_buffer_valid[i] = true;
322 }
323 wavebuffers[i].sent_to_DSP = true;
324 }
325 }
326
327 was_playing = false;
328
329 for (u32 i = 0; i < MaxWaveBuffers; i++) {
330 if (voice_states[0]->wave_buffer_valid[i]) {
331 return true;
332 }
333 }
334 break;
335
336 case ServerPlayState::Stopped:
337 case ServerPlayState::Paused:
338 for (auto& wavebuffer : wavebuffers) {
339 if (!wavebuffer.sent_to_DSP) {
340 wavebuffer.buffer_address.GetReference(true);
341 wavebuffer.context_address.GetReference(true);
342 }
343 }
344
345 if (sample_format == SampleFormat::Adpcm && data_address.GetCpuAddr() != 0) {
346 data_address.GetReference(true);
347 }
348
349 was_playing = last_play_state == ServerPlayState::Started;
350 break;
351
352 case ServerPlayState::RequestStop:
353 for (u32 i = 0; i < MaxWaveBuffers; i++) {
354 wavebuffers[i].sent_to_DSP = true;
355
356 for (s8 channel = 0; channel < channel_count; channel++) {
357 if (voice_states[channel]->wave_buffer_valid[i]) {
358 voice_states[channel]->wave_buffer_index =
359 (voice_states[channel]->wave_buffer_index + 1) % MaxWaveBuffers;
360 voice_states[channel]->wave_buffers_consumed++;
361 }
362 voice_states[channel]->wave_buffer_valid[i] = false;
363 }
364 }
365
366 for (s8 channel = 0; channel < channel_count; channel++) {
367 voice_states[channel]->offset = 0;
368 voice_states[channel]->played_sample_count = 0;
369 voice_states[channel]->adpcm_context = {};
370 voice_states[channel]->sample_history.fill(0);
371 voice_states[channel]->fraction = 0;
372 }
373
374 current_play_state = ServerPlayState::Stopped;
375 was_playing = last_play_state == ServerPlayState::Started;
376 break;
377 }
378
379 return was_playing;
380}
381
382bool VoiceInfo::UpdateForCommandGeneration(VoiceContext& voice_context) {
383 std::array<VoiceState*, MaxChannels> voice_states{};
384
385 if (is_new) {
386 ResetResources(voice_context);
387 prev_volume = volume;
388 is_new = false;
389 }
390
391 for (s8 channel = 0; channel < channel_count; channel++) {
392 voice_states[channel] = &voice_context.GetDspSharedState(channel_resource_ids[channel]);
393 }
394
395 return UpdateParametersForCommandGeneration(voice_states);
396}
397
398void VoiceInfo::ResetResources(VoiceContext& voice_context) const {
399 for (s8 channel = 0; channel < channel_count; channel++) {
400 auto& state{voice_context.GetDspSharedState(channel_resource_ids[channel])};
401 state = {};
402
403 auto& channel_resource{voice_context.GetChannelResource(channel_resource_ids[channel])};
404 channel_resource.prev_mix_volumes = channel_resource.mix_volumes;
405 }
406}
407
408} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/voice/voice_info.h b/src/audio_core/renderer/voice/voice_info.h
new file mode 100644
index 000000000..896723e0c
--- /dev/null
+++ b/src/audio_core/renderer/voice/voice_info.h
@@ -0,0 +1,378 @@
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 <bitset>
8
9#include "audio_core/common/common.h"
10#include "audio_core/common/wave_buffer.h"
11#include "audio_core/renderer/behavior/behavior_info.h"
12#include "audio_core/renderer/memory/address_info.h"
13#include "common/common_types.h"
14
15namespace AudioCore::AudioRenderer {
16class PoolMapper;
17class VoiceContext;
18struct VoiceState;
19
20/**
21 * Represents one voice. Voices are essentially noises, and they can be further mixed and have
22 * effects applied to them, but voices are the basis of all sounds.
23 */
24class VoiceInfo {
25public:
26 enum class ServerPlayState {
27 Started,
28 Stopped,
29 RequestStop,
30 Paused,
31 };
32
33 struct Flags {
34 u8 IsVoicePlayedSampleCountResetAtLoopPointSupported : 1;
35 u8 IsVoicePitchAndSrcSkippedSupported : 1;
36 };
37
38 /**
39 * A wavebuffer contains information on the data source buffers.
40 */
41 struct WaveBuffer {
42 void Copy(WaveBufferVersion1& other) {
43 other.buffer = buffer_address.GetReference(true);
44 other.buffer_size = buffer_address.GetSize();
45 other.start_offset = start_offset;
46 other.end_offset = end_offset;
47 other.loop = loop;
48 other.stream_ended = stream_ended;
49
50 if (context_address.GetCpuAddr()) {
51 other.context = context_address.GetReference(true);
52 other.context_size = context_address.GetSize();
53 } else {
54 other.context = CpuAddr(0);
55 other.context_size = 0;
56 }
57 }
58
59 void Copy(WaveBufferVersion2& other) {
60 other.buffer = buffer_address.GetReference(true);
61 other.buffer_size = buffer_address.GetSize();
62 other.start_offset = start_offset;
63 other.end_offset = end_offset;
64 other.loop_start_offset = loop_start_offset;
65 other.loop_end_offset = loop_end_offset;
66 other.loop = loop;
67 other.loop_count = loop_count;
68 other.stream_ended = stream_ended;
69
70 if (context_address.GetCpuAddr()) {
71 other.context = context_address.GetReference(true);
72 other.context_size = context_address.GetSize();
73 } else {
74 other.context = CpuAddr(0);
75 other.context_size = 0;
76 }
77 }
78
79 void Initialize() {
80 buffer_address.Setup(0, 0);
81 context_address.Setup(0, 0);
82 start_offset = 0;
83 end_offset = 0;
84 loop = false;
85 stream_ended = false;
86 sent_to_DSP = true;
87 loop_start_offset = 0;
88 loop_end_offset = 0;
89 loop_count = 0;
90 }
91 /// Game memory address of the wavebuffer data
92 AddressInfo buffer_address{0, 0};
93 /// Context for decoding, used for ADPCM
94 AddressInfo context_address{0, 0};
95 /// Starting offset for the wavebuffer
96 u32 start_offset{};
97 /// Ending offset the wavebuffer
98 u32 end_offset{};
99 /// Should this wavebuffer loop?
100 bool loop{};
101 /// Has this wavebuffer ended?
102 bool stream_ended{};
103 /// Has this wavebuffer been sent to the AudioRenderer?
104 bool sent_to_DSP{true};
105 /// Starting offset when looping, can differ from start_offset
106 u32 loop_start_offset{};
107 /// Ending offset when looping, can differ from end_offset
108 u32 loop_end_offset{};
109 /// Number of times to loop this wavebuffer
110 s32 loop_count{};
111 };
112
113 struct WaveBufferInternal {
114 /* 0x00 */ CpuAddr address;
115 /* 0x08 */ u64 size;
116 /* 0x10 */ s32 start_offset;
117 /* 0x14 */ s32 end_offset;
118 /* 0x18 */ bool loop;
119 /* 0x19 */ bool stream_ended;
120 /* 0x1A */ bool sent_to_DSP;
121 /* 0x1C */ s32 loop_count;
122 /* 0x20 */ CpuAddr context_address;
123 /* 0x28 */ u64 context_size;
124 /* 0x30 */ u32 loop_start;
125 /* 0x34 */ u32 loop_end;
126 };
127 static_assert(sizeof(WaveBufferInternal) == 0x38,
128 "VoiceInfo::WaveBufferInternal has the wrong size!");
129
130 struct BiquadFilterParameter {
131 /* 0x00 */ bool enabled;
132 /* 0x02 */ std::array<s16, 3> b;
133 /* 0x08 */ std::array<s16, 2> a;
134 };
135 static_assert(sizeof(BiquadFilterParameter) == 0xC,
136 "VoiceInfo::BiquadFilterParameter has the wrong size!");
137
138 struct InParameter {
139 /* 0x000 */ u32 id;
140 /* 0x004 */ u32 node_id;
141 /* 0x008 */ bool is_new;
142 /* 0x009 */ bool in_use;
143 /* 0x00A */ PlayState play_state;
144 /* 0x00B */ SampleFormat sample_format;
145 /* 0x00C */ u32 sample_rate;
146 /* 0x010 */ s32 priority;
147 /* 0x014 */ s32 sort_order;
148 /* 0x018 */ u32 channel_count;
149 /* 0x01C */ f32 pitch;
150 /* 0x020 */ f32 volume;
151 /* 0x024 */ std::array<BiquadFilterParameter, MaxBiquadFilters> biquads;
152 /* 0x03C */ u32 wave_buffer_count;
153 /* 0x040 */ u16 wave_buffer_index;
154 /* 0x042 */ char unk042[0x6];
155 /* 0x048 */ CpuAddr src_data_address;
156 /* 0x050 */ u64 src_data_size;
157 /* 0x058 */ u32 mix_id;
158 /* 0x05C */ u32 splitter_id;
159 /* 0x060 */ std::array<WaveBufferInternal, MaxWaveBuffers> wave_buffer_internal;
160 /* 0x140 */ std::array<u32, MaxChannels> channel_resource_ids;
161 /* 0x158 */ bool clear_voice_drop;
162 /* 0x159 */ u8 flush_buffer_count;
163 /* 0x15A */ char unk15A[0x2];
164 /* 0x15C */ Flags flags;
165 /* 0x15D */ char unk15D[0x1];
166 /* 0x15E */ SrcQuality src_quality;
167 /* 0x15F */ char unk15F[0x11];
168 };
169 static_assert(sizeof(InParameter) == 0x170, "VoiceInfo::InParameter has the wrong size!");
170
171 struct OutStatus {
172 /* 0x00 */ u64 played_sample_count;
173 /* 0x08 */ u32 wave_buffers_consumed;
174 /* 0x0C */ bool voice_dropped;
175 };
176 static_assert(sizeof(OutStatus) == 0x10, "OutStatus::InParameter has the wrong size!");
177
178 VoiceInfo();
179
180 /**
181 * Initialize this voice.
182 */
183 void Initialize();
184
185 /**
186 * Does this voice ned an update?
187 *
188 * @param params - Input parametetrs to check matching.
189 * @return True if this voice needs an update, otherwise false.
190 */
191 bool ShouldUpdateParameters(const InParameter& params) const;
192
193 /**
194 * Update the parameters of this voice.
195 *
196 * @param error_info - Output error code.
197 * @param params - Input parametters to udpate from.
198 * @param pool_mapper - Used to map buffers.
199 * @param behavior - behavior to check supported features.
200 */
201 void UpdateParameters(BehaviorInfo::ErrorInfo& error_info, const InParameter& params,
202 const PoolMapper& pool_mapper, const BehaviorInfo& behavior);
203
204 /**
205 * Update the current play state.
206 *
207 * @param state - New play state for this voice.
208 */
209 void UpdatePlayState(PlayState state);
210
211 /**
212 * Update the current sample rate conversion quality.
213 *
214 * @param quality - New quality.
215 */
216 void UpdateSrcQuality(SrcQuality quality);
217
218 /**
219 * Update all wavebuffers.
220 *
221 * @param error_infos - Output 2D array of errors, 2 per wavebuffer.
222 * @param error_count - Number of errors provided. Unused.
223 * @param params - Input parametters to be used for the update.
224 * @param voice_states - The voice states for each channel in this voice to be updated.
225 * @param pool_mapper - Used to map the wavebuffers.
226 * @param behavior - Used to check for supported features.
227 */
228 void UpdateWaveBuffers(std::span<std::array<BehaviorInfo::ErrorInfo, 2>> error_infos,
229 u32 error_count, const InParameter& params,
230 std::span<VoiceState*> voice_states, const PoolMapper& pool_mapper,
231 const BehaviorInfo& behavior);
232
233 /**
234 * Update a wavebuffer.
235 *
236 * @param error_infos - Output array of errors.
237 * @param wave_buffer - The wavebuffer to be updated.
238 * @param wave_buffer_internal - Input parametters to be used for the update.
239 * @param sample_format - Sample format of the wavebuffer.
240 * @param valid - Is this wavebuffer valid?
241 * @param pool_mapper - Used to map the wavebuffers.
242 * @param behavior - Used to check for supported features.
243 */
244 void UpdateWaveBuffer(std::span<BehaviorInfo::ErrorInfo> error_info, WaveBuffer& wave_buffer,
245 const WaveBufferInternal& wave_buffer_internal,
246 SampleFormat sample_format, bool valid, const PoolMapper& pool_mapper,
247 const BehaviorInfo& behavior);
248
249 /**
250 * Check if the input wavebuffer needs an update.
251 *
252 * @param wave_buffer_internal - Input wavebuffer parameters to check.
253 * @return True if the given wavebuffer needs an update, otherwise false.
254 */
255 bool ShouldUpdateWaveBuffer(const WaveBufferInternal& wave_buffer_internal) const;
256
257 /**
258 * Write the number of played samples, number of consumed wavebuffers and if this voice was
259 * dropped, to the given out_status.
260 *
261 * @param out_status - Output status to be written to.
262 * @param in_params - Input parameters to check if the wavebuffer is new.
263 * @param voice_states - Current host voice states for this voice, source of the output.
264 */
265 void WriteOutStatus(OutStatus& out_status, const InParameter& in_params,
266 std::span<VoiceState*> voice_states);
267
268 /**
269 * Check if this voice should be skipped for command generation.
270 * Checks various things such as usage state, whether data is mapped etc.
271 *
272 * @return True if this voice should not be generated, otherwise false.
273 */
274 bool ShouldSkip() const;
275
276 /**
277 * Check if this voice has any mixing connections.
278 *
279 * @return True if this voice participes in mixing, otherwise false.
280 */
281 bool HasAnyConnection() const;
282
283 /**
284 * Flush flush_count wavebuffers, marking them as consumed.
285 *
286 * @param flush_count - Number of wavebuffers to flush.
287 * @param voice_states - Voice states for these wavebuffers.
288 * @param channel_count - Number of active channels.
289 */
290 void FlushWaveBuffers(u32 flush_count, std::span<VoiceState*> voice_states, s8 channel_count);
291
292 /**
293 * Update this voice's parameters on command generation,
294 * updating voice states and flushing if needed.
295 *
296 * @param voice_states - Voice states for these wavebuffers.
297 * @return True if this voice should be generated, otherwise false.
298 */
299 bool UpdateParametersForCommandGeneration(std::span<VoiceState*> voice_states);
300
301 /**
302 * Update this voice on command generation.
303 *
304 * @param voice_states - Voice states for these wavebuffers.
305 * @return True if this voice should be generated, otherwise false.
306 */
307 bool UpdateForCommandGeneration(VoiceContext& voice_context);
308
309 /**
310 * Reset the AudioRenderer-side voice states, and the channel resources for this voice.
311 *
312 * @param voice_context - Context from which to get the resources.
313 */
314 void ResetResources(VoiceContext& voice_context) const;
315
316 /// Is this voice in use?
317 bool in_use{};
318 /// Is this voice new?
319 bool is_new{};
320 /// Was this voice last playing? Used for depopping
321 bool was_playing{};
322 /// Sample format of the wavebuffers in this voice
323 SampleFormat sample_format{};
324 /// Sample rate of the wavebuffers in this voice
325 u32 sample_rate{};
326 /// Number of channels in this voice
327 s8 channel_count{};
328 /// Id of this voice
329 u32 id{};
330 /// Node id of this voice
331 u32 node_id{};
332 /// Mix id this voice is mixed to
333 u32 mix_id{};
334 /// Play state of this voice
335 ServerPlayState current_play_state{ServerPlayState::Stopped};
336 /// Last play state of this voice
337 ServerPlayState last_play_state{ServerPlayState::Started};
338 /// Priority of this voice, lower is higher
339 s32 priority{};
340 /// Sort order of this voice, used when same priority
341 s32 sort_order{};
342 /// Pitch of this voice (for sample rate conversion)
343 f32 pitch{};
344 /// Current volume of this voice
345 f32 volume{};
346 /// Previous volume of this voice
347 f32 prev_volume{};
348 /// Biquad filters for generating filter commands on this voice
349 std::array<BiquadFilterParameter, MaxBiquadFilters> biquads{};
350 /// Number of active wavebuffers
351 u32 wave_buffer_count{};
352 /// Current playing wavebuffer index
353 u16 wave_buffer_index{};
354 /// Flags controlling decode behavior
355 u16 flags{};
356 /// Game memory for ADPCM coefficients
357 AddressInfo data_address{0, 0};
358 /// Wavebuffers
359 std::array<WaveBuffer, MaxWaveBuffers> wavebuffers{};
360 /// Channel resources for this voice
361 std::array<u32, MaxChannels> channel_resource_ids{};
362 /// Splitter id this voice is connected with
363 s32 splitter_id{UnusedSplitterId};
364 /// Sample rate conversion quality
365 SrcQuality src_quality{SrcQuality::Medium};
366 /// Was this voice dropped due to limited time?
367 bool voice_dropped{};
368 /// Is this voice's coefficient (data_address) unmapped?
369 bool data_unmapped{};
370 /// Is this voice's buffers (wavebuffer data and ADPCM context) unmapped?
371 bool buffer_unmapped{};
372 /// Initialisation state of the biquads
373 std::array<bool, MaxBiquadFilters> biquad_initialized{};
374 /// Number of wavebuffers to flush
375 u8 flush_buffer_count{};
376};
377
378} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/voice/voice_state.h b/src/audio_core/renderer/voice/voice_state.h
new file mode 100644
index 000000000..d5497e2fb
--- /dev/null
+++ b/src/audio_core/renderer/voice/voice_state.h
@@ -0,0 +1,70 @@
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
8#include "audio_core/common/common.h"
9#include "common/common_types.h"
10#include "common/fixed_point.h"
11
12namespace AudioCore::AudioRenderer {
13/**
14 * Holds a state for a voice. One is kept host-side, and one is used by the AudioRenderer,
15 * host-side is updated on the next iteration.
16 */
17struct VoiceState {
18 /**
19 * State of the voice's biquad filter.
20 */
21 struct BiquadFilterState {
22 Common::FixedPoint<50, 14> s0;
23 Common::FixedPoint<50, 14> s1;
24 Common::FixedPoint<50, 14> s2;
25 Common::FixedPoint<50, 14> s3;
26 };
27
28 /**
29 * Context for ADPCM decoding.
30 */
31 struct AdpcmContext {
32 u16 header;
33 s16 yn0;
34 s16 yn1;
35 };
36
37 /// Number of samples played
38 u64 played_sample_count;
39 /// Current offset from the starting offset
40 u32 offset;
41 /// Currently active wavebuffer index
42 u32 wave_buffer_index;
43 /// Array of which wavebuffers are currently valid
44
45 std::array<bool, MaxWaveBuffers> wave_buffer_valid;
46 /// Number of wavebuffers consumed, given back to the game
47 u32 wave_buffers_consumed;
48 /// History of samples, used for rate conversion
49
50 std::array<s16, MaxWaveBuffers * 2> sample_history;
51 /// Current read fraction, used for resampling
52 Common::FixedPoint<49, 15> fraction;
53 /// Current adpcm context
54 AdpcmContext adpcm_context;
55 /// Current biquad states, used when filtering
56
57 std::array<std::array<BiquadFilterState, MaxBiquadFilters>, MaxBiquadFilters> biquad_states;
58 /// Previous samples
59 std::array<s32, MaxMixBuffers> previous_samples;
60 /// Unused
61 u32 external_context_size;
62 /// Unused
63 bool external_context_enabled;
64 /// Was this voice dropped?
65 bool voice_dropped;
66 /// Number of times the wavebuffer has looped
67 s32 loop_count;
68};
69
70} // namespace AudioCore::AudioRenderer