summaryrefslogtreecommitdiff
path: root/src/audio_core/audio_renderer.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2018-08-04 17:45:14 -0400
committerGravatar bunnei2018-08-04 21:54:30 -0400
commitb46df98e935552ea48ed86360e8c8b34b294982d (patch)
tree95a17bc97a704c5e3dfb41d11c3579b88141dbe2 /src/audio_core/audio_renderer.cpp
parentaudio_core: Use s16 where possible for audio samples. (diff)
downloadyuzu-b46df98e935552ea48ed86360e8c8b34b294982d.tar.gz
yuzu-b46df98e935552ea48ed86360e8c8b34b294982d.tar.xz
yuzu-b46df98e935552ea48ed86360e8c8b34b294982d.zip
audio_core: Implement audren_u audio playback.
Diffstat (limited to 'src/audio_core/audio_renderer.cpp')
-rw-r--r--src/audio_core/audio_renderer.cpp234
1 files changed, 234 insertions, 0 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
new file mode 100644
index 000000000..282f345c5
--- /dev/null
+++ b/src/audio_core/audio_renderer.cpp
@@ -0,0 +1,234 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/audio_renderer.h"
6#include "common/assert.h"
7#include "common/logging/log.h"
8#include "core/memory.h"
9
10namespace AudioCore {
11
12constexpr u32 STREAM_SAMPLE_RATE{48000};
13constexpr u32 STREAM_NUM_CHANNELS{2};
14
15AudioRenderer::AudioRenderer(AudioRendererParameter params,
16 Kernel::SharedPtr<Kernel::Event> buffer_event)
17 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
18
19 audio_core = std::make_unique<AudioCore::AudioOut>();
20 stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
21 [=]() { buffer_event->Signal(); });
22 audio_core->StartStream(stream);
23
24 QueueMixedBuffer(0);
25 QueueMixedBuffer(1);
26 QueueMixedBuffer(2);
27}
28
29std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
30 // Copy UpdateDataHeader struct
31 UpdateDataHeader config{};
32 std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader));
33 u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
34
35 // Copy MemoryPoolInfo structs
36 std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
37 std::memcpy(mem_pool_info.data(),
38 input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
39 memory_pool_count * sizeof(MemoryPoolInfo));
40
41 // Copy VoiceInfo structs
42 size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
43 config.voice_resource_size};
44 for (auto& voice : voices) {
45 std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo));
46 offset += sizeof(VoiceInfo);
47 }
48
49 // Update voices
50 for (auto& voice : voices) {
51 voice.UpdateState();
52 if (!voice.GetInfo().is_in_use) {
53 continue;
54 }
55 if (voice.GetInfo().is_new) {
56 voice.SetWaveIndex(voice.GetInfo().wave_buffer_head);
57 }
58 }
59
60 // Update memory pool state
61 std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
62 for (size_t index = 0; index < memory_pool.size(); ++index) {
63 if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
64 memory_pool[index].state = MemoryPoolStates::Attached;
65 } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
66 memory_pool[index].state = MemoryPoolStates::Detached;
67 }
68 }
69
70 // Release previous buffers and queue next ones for playback
71 ReleaseAndQueueBuffers();
72
73 // Copy output header
74 UpdateDataHeader response_data{worker_params};
75 std::vector<u8> output_params(response_data.total_size);
76 std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
77
78 // Copy output memory pool entries
79 std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(),
80 response_data.memory_pools_size);
81
82 // Copy output voice status
83 size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
84 for (const auto& voice : voices) {
85 std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
86 sizeof(VoiceOutStatus));
87 voice_out_status_offset += sizeof(VoiceOutStatus);
88 }
89
90 return output_params;
91}
92
93void AudioRenderer::VoiceState::SetWaveIndex(size_t index) {
94 wave_index = index & 3;
95 is_refresh_pending = true;
96}
97
98std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) {
99 if (!IsPlaying()) {
100 return {};
101 }
102
103 if (is_refresh_pending) {
104 RefreshBuffer();
105 }
106
107 const size_t max_size{samples.size() - offset};
108 const size_t dequeue_offset{offset};
109 size_t size{sample_count * STREAM_NUM_CHANNELS};
110 if (size > max_size) {
111 size = max_size;
112 }
113
114 out_status.played_sample_count += size / STREAM_NUM_CHANNELS;
115 offset += size;
116
117 const auto& wave_buffer{info.wave_buffer[wave_index]};
118 if (offset == samples.size()) {
119 offset = 0;
120
121 if (!wave_buffer.is_looping) {
122 SetWaveIndex(wave_index + 1);
123 }
124
125 out_status.wave_buffer_consumed++;
126
127 if (wave_buffer.end_of_stream) {
128 info.play_state = PlayState::Paused;
129 }
130 }
131
132 return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size};
133}
134
135void AudioRenderer::VoiceState::UpdateState() {
136 if (is_in_use && !info.is_in_use) {
137 // No longer in use, reset state
138 is_refresh_pending = true;
139 wave_index = 0;
140 offset = 0;
141 out_status = {};
142 }
143 is_in_use = info.is_in_use;
144}
145
146void AudioRenderer::VoiceState::RefreshBuffer() {
147 std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16));
148 Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(),
149 info.wave_buffer[wave_index].buffer_sz);
150
151 switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
152 case Codec::PcmFormat::Int16: {
153 // PCM16 is played as-is
154 break;
155 }
156 case Codec::PcmFormat::Adpcm: {
157 // Decode ADPCM to PCM16
158 Codec::ADPCM_Coeff coeffs;
159 Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
160 new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
161 new_samples.size() * sizeof(s16), coeffs, adpcm_state);
162 break;
163 }
164 default:
165 LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
166 UNREACHABLE();
167 break;
168 }
169
170 switch (info.channel_count) {
171 case 1:
172 // 1 channel is upsampled to 2 channel
173 samples.resize(new_samples.size() * 2);
174 for (size_t index = 0; index < new_samples.size(); ++index) {
175 samples[index * 2] = new_samples[index];
176 samples[index * 2 + 1] = new_samples[index];
177 }
178 break;
179 case 2: {
180 // 2 channel is played as is
181 samples = std::move(new_samples);
182 break;
183 }
184 default:
185 LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
186 UNREACHABLE();
187 break;
188 }
189
190 is_refresh_pending = false;
191}
192
193static constexpr s16 ClampToS16(s32 value) {
194 return static_cast<s16>(std::clamp(value, -32768, 32767));
195}
196
197void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
198 constexpr size_t BUFFER_SIZE{512};
199 std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
200
201 for (auto& voice : voices) {
202 if (!voice.IsPlaying()) {
203 continue;
204 }
205
206 size_t offset{};
207 s64 samples_remaining{BUFFER_SIZE};
208 while (samples_remaining > 0) {
209 const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
210
211 if (samples.empty()) {
212 break;
213 }
214
215 samples_remaining -= samples.size();
216
217 for (const auto& sample : samples) {
218 const s32 buffer_sample{buffer[offset]};
219 buffer[offset++] =
220 ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume));
221 }
222 }
223 }
224 audio_core->QueueBuffer(stream, tag, std::move(buffer));
225}
226
227void AudioRenderer::ReleaseAndQueueBuffers() {
228 const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)};
229 for (const auto& tag : released_buffers) {
230 QueueMixedBuffer(tag);
231 }
232}
233
234} // namespace AudioCore