summaryrefslogtreecommitdiff
path: root/src/audio_core/audio_renderer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio_core/audio_renderer.cpp')
-rw-r--r--src/audio_core/audio_renderer.cpp178
1 files changed, 103 insertions, 75 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 80ffddb10..0757cd804 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -12,6 +12,7 @@
12#include "audio_core/voice_context.h" 12#include "audio_core/voice_context.h"
13#include "common/logging/log.h" 13#include "common/logging/log.h"
14#include "common/settings.h" 14#include "common/settings.h"
15#include "core/core_timing.h"
15#include "core/memory.h" 16#include "core/memory.h"
16 17
17namespace { 18namespace {
@@ -68,7 +69,9 @@ namespace {
68} // namespace 69} // namespace
69 70
70namespace AudioCore { 71namespace AudioCore {
71AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, 72constexpr s32 NUM_BUFFERS = 2;
73
74AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_,
72 AudioCommon::AudioRendererParameter params, 75 AudioCommon::AudioRendererParameter params,
73 Stream::ReleaseCallback&& release_callback, 76 Stream::ReleaseCallback&& release_callback,
74 std::size_t instance_number) 77 std::size_t instance_number)
@@ -77,7 +80,8 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory
77 sink_context(params.sink_count), splitter_context(), 80 sink_context(params.sink_count), splitter_context(),
78 voices(params.voice_count), memory{memory_}, 81 voices(params.voice_count), memory{memory_},
79 command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context, 82 command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context,
80 memory) { 83 memory),
84 core_timing{core_timing_} {
81 behavior_info.SetUserRevision(params.revision); 85 behavior_info.SetUserRevision(params.revision);
82 splitter_context.Initialize(behavior_info, params.splitter_count, 86 splitter_context.Initialize(behavior_info, params.splitter_count,
83 params.num_splitter_send_channels); 87 params.num_splitter_send_channels);
@@ -86,16 +90,27 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory
86 stream = audio_out->OpenStream( 90 stream = audio_out->OpenStream(
87 core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, 91 core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
88 fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback)); 92 fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback));
89 audio_out->StartStream(stream); 93 process_event = Core::Timing::CreateEvent(
90 94 fmt::format("AudioRenderer-Instance{}-Process", instance_number),
91 QueueMixedBuffer(0); 95 [this](std::uintptr_t, std::chrono::nanoseconds) { ReleaseAndQueueBuffers(); });
92 QueueMixedBuffer(1); 96 for (s32 i = 0; i < NUM_BUFFERS; ++i) {
93 QueueMixedBuffer(2); 97 QueueMixedBuffer(i);
94 QueueMixedBuffer(3); 98 }
95} 99}
96 100
97AudioRenderer::~AudioRenderer() = default; 101AudioRenderer::~AudioRenderer() = default;
98 102
103ResultCode AudioRenderer::Start() {
104 audio_out->StartStream(stream);
105 ReleaseAndQueueBuffers();
106 return ResultSuccess;
107}
108
109ResultCode AudioRenderer::Stop() {
110 audio_out->StopStream(stream);
111 return ResultSuccess;
112}
113
99u32 AudioRenderer::GetSampleRate() const { 114u32 AudioRenderer::GetSampleRate() const {
100 return worker_params.sample_rate; 115 return worker_params.sample_rate;
101} 116}
@@ -114,89 +129,88 @@ Stream::State AudioRenderer::GetStreamState() const {
114 129
115ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params, 130ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
116 std::vector<u8>& output_params) { 131 std::vector<u8>& output_params) {
132 {
133 std::scoped_lock lock{mutex};
134 InfoUpdater info_updater{input_params, output_params, behavior_info};
117 135
118 InfoUpdater info_updater{input_params, output_params, behavior_info}; 136 if (!info_updater.UpdateBehaviorInfo(behavior_info)) {
119 137 LOG_ERROR(Audio, "Failed to update behavior info input parameters");
120 if (!info_updater.UpdateBehaviorInfo(behavior_info)) { 138 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
121 LOG_ERROR(Audio, "Failed to update behavior info input parameters"); 139 }
122 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
123 }
124
125 if (!info_updater.UpdateMemoryPools(memory_pool_info)) {
126 LOG_ERROR(Audio, "Failed to update memory pool parameters");
127 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
128 }
129
130 if (!info_updater.UpdateVoiceChannelResources(voice_context)) {
131 LOG_ERROR(Audio, "Failed to update voice channel resource parameters");
132 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
133 }
134 140
135 if (!info_updater.UpdateVoices(voice_context, memory_pool_info, 0)) { 141 if (!info_updater.UpdateMemoryPools(memory_pool_info)) {
136 LOG_ERROR(Audio, "Failed to update voice parameters"); 142 LOG_ERROR(Audio, "Failed to update memory pool parameters");
137 return AudioCommon::Audren::ERR_INVALID_PARAMETERS; 143 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
138 } 144 }
139 145
140 // TODO(ogniK): Deal with stopped audio renderer but updates still taking place 146 if (!info_updater.UpdateVoiceChannelResources(voice_context)) {
141 if (!info_updater.UpdateEffects(effect_context, true)) { 147 LOG_ERROR(Audio, "Failed to update voice channel resource parameters");
142 LOG_ERROR(Audio, "Failed to update effect parameters"); 148 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
143 return AudioCommon::Audren::ERR_INVALID_PARAMETERS; 149 }
144 }
145 150
146 if (behavior_info.IsSplitterSupported()) { 151 if (!info_updater.UpdateVoices(voice_context, memory_pool_info, 0)) {
147 if (!info_updater.UpdateSplitterInfo(splitter_context)) { 152 LOG_ERROR(Audio, "Failed to update voice parameters");
148 LOG_ERROR(Audio, "Failed to update splitter parameters");
149 return AudioCommon::Audren::ERR_INVALID_PARAMETERS; 153 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
150 } 154 }
151 }
152 155
153 const auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count, 156 // TODO(ogniK): Deal with stopped audio renderer but updates still taking place
154 splitter_context, effect_context); 157 if (!info_updater.UpdateEffects(effect_context, true)) {
158 LOG_ERROR(Audio, "Failed to update effect parameters");
159 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
160 }
155 161
156 if (mix_result.IsError()) { 162 if (behavior_info.IsSplitterSupported()) {
157 LOG_ERROR(Audio, "Failed to update mix parameters"); 163 if (!info_updater.UpdateSplitterInfo(splitter_context)) {
158 return mix_result; 164 LOG_ERROR(Audio, "Failed to update splitter parameters");
159 } 165 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
166 }
167 }
160 168
161 // TODO(ogniK): Sinks 169 const auto mix_result = info_updater.UpdateMixes(
162 if (!info_updater.UpdateSinks(sink_context)) { 170 mix_context, worker_params.mix_buffer_count, splitter_context, effect_context);
163 LOG_ERROR(Audio, "Failed to update sink parameters");
164 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
165 }
166 171
167 // TODO(ogniK): Performance buffer 172 if (mix_result.IsError()) {
168 if (!info_updater.UpdatePerformanceBuffer()) { 173 LOG_ERROR(Audio, "Failed to update mix parameters");
169 LOG_ERROR(Audio, "Failed to update performance buffer parameters"); 174 return mix_result;
170 return AudioCommon::Audren::ERR_INVALID_PARAMETERS; 175 }
171 }
172 176
173 if (!info_updater.UpdateErrorInfo(behavior_info)) { 177 // TODO(ogniK): Sinks
174 LOG_ERROR(Audio, "Failed to update error info"); 178 if (!info_updater.UpdateSinks(sink_context)) {
175 return AudioCommon::Audren::ERR_INVALID_PARAMETERS; 179 LOG_ERROR(Audio, "Failed to update sink parameters");
176 } 180 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
181 }
177 182
178 if (behavior_info.IsElapsedFrameCountSupported()) { 183 // TODO(ogniK): Performance buffer
179 if (!info_updater.UpdateRendererInfo(elapsed_frame_count)) { 184 if (!info_updater.UpdatePerformanceBuffer()) {
180 LOG_ERROR(Audio, "Failed to update renderer info"); 185 LOG_ERROR(Audio, "Failed to update performance buffer parameters");
181 return AudioCommon::Audren::ERR_INVALID_PARAMETERS; 186 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
182 } 187 }
183 }
184 // TODO(ogniK): Statistics
185 188
186 if (!info_updater.WriteOutputHeader()) { 189 if (!info_updater.UpdateErrorInfo(behavior_info)) {
187 LOG_ERROR(Audio, "Failed to write output header"); 190 LOG_ERROR(Audio, "Failed to update error info");
188 return AudioCommon::Audren::ERR_INVALID_PARAMETERS; 191 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
189 } 192 }
190 193
191 // TODO(ogniK): Check when all sections are implemented 194 if (behavior_info.IsElapsedFrameCountSupported()) {
195 if (!info_updater.UpdateRendererInfo(elapsed_frame_count)) {
196 LOG_ERROR(Audio, "Failed to update renderer info");
197 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
198 }
199 }
200 // TODO(ogniK): Statistics
192 201
193 if (!info_updater.CheckConsumedSize()) { 202 if (!info_updater.WriteOutputHeader()) {
194 LOG_ERROR(Audio, "Audio buffers were not consumed!"); 203 LOG_ERROR(Audio, "Failed to write output header");
195 return AudioCommon::Audren::ERR_INVALID_PARAMETERS; 204 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
196 } 205 }
197 206
198 ReleaseAndQueueBuffers(); 207 // TODO(ogniK): Check when all sections are implemented
199 208
209 if (!info_updater.CheckConsumedSize()) {
210 LOG_ERROR(Audio, "Audio buffers were not consumed!");
211 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
212 }
213 }
200 return ResultSuccess; 214 return ResultSuccess;
201} 215}
202 216
@@ -315,10 +329,24 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
315} 329}
316 330
317void AudioRenderer::ReleaseAndQueueBuffers() { 331void AudioRenderer::ReleaseAndQueueBuffers() {
318 const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)}; 332 if (!stream->IsPlaying()) {
319 for (const auto& tag : released_buffers) { 333 return;
320 QueueMixedBuffer(tag); 334 }
335
336 {
337 std::scoped_lock lock{mutex};
338 const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)};
339 for (const auto& tag : released_buffers) {
340 QueueMixedBuffer(tag);
341 }
321 } 342 }
343
344 const f32 sample_rate = static_cast<f32>(GetSampleRate());
345 const f32 sample_count = static_cast<f32>(GetSampleCount());
346 const f32 consume_rate = sample_rate / (sample_count * (sample_count / 240));
347 const s32 ms = (1000 / static_cast<s32>(consume_rate)) - 1;
348 const std::chrono::milliseconds next_event_time(std::max(ms / NUM_BUFFERS, 1));
349 core_timing.ScheduleEvent(next_event_time, process_event, {});
322} 350}
323 351
324} // namespace AudioCore 352} // namespace AudioCore