summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/core.cpp51
-rw-r--r--src/core/core.h19
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp38
-rw-r--r--src/core/hle/kernel/hle_ipc.h8
-rw-r--r--src/core/hle/kernel/kernel.cpp34
-rw-r--r--src/core/hle/kernel/kernel.h3
-rw-r--r--src/core/hle/service/am/am.cpp10
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/audio/audin_u.cpp384
-rw-r--r--src/core/hle/service/audio/audin_u.h47
-rw-r--r--src/core/hle/service/audio/audout_u.cpp327
-rw-r--r--src/core/hle/service/audio/audout_u.h21
-rw-r--r--src/core/hle/service/audio/audren_u.cpp692
-rw-r--r--src/core/hle/service/audio/audren_u.h22
-rw-r--r--src/core/hle/service/audio/errors.h9
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/memory.cpp6
-rw-r--r--src/core/memory.h13
18 files changed, 923 insertions, 764 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 7723d9782..0ede0d85c 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -8,6 +8,7 @@
8#include <memory> 8#include <memory>
9#include <utility> 9#include <utility>
10 10
11#include "audio_core/audio_core.h"
11#include "common/fs/fs.h" 12#include "common/fs/fs.h"
12#include "common/logging/log.h" 13#include "common/logging/log.h"
13#include "common/microprofile.h" 14#include "common/microprofile.h"
@@ -140,6 +141,8 @@ struct System::Impl {
140 core_timing.SyncPause(false); 141 core_timing.SyncPause(false);
141 is_paused = false; 142 is_paused = false;
142 143
144 audio_core->PauseSinks(false);
145
143 return status; 146 return status;
144 } 147 }
145 148
@@ -147,6 +150,8 @@ struct System::Impl {
147 std::unique_lock<std::mutex> lk(suspend_guard); 150 std::unique_lock<std::mutex> lk(suspend_guard);
148 status = SystemResultStatus::Success; 151 status = SystemResultStatus::Success;
149 152
153 audio_core->PauseSinks(true);
154
150 core_timing.SyncPause(true); 155 core_timing.SyncPause(true);
151 kernel.Suspend(true); 156 kernel.Suspend(true);
152 is_paused = true; 157 is_paused = true;
@@ -154,6 +159,11 @@ struct System::Impl {
154 return status; 159 return status;
155 } 160 }
156 161
162 bool IsPaused() const {
163 std::unique_lock lk(suspend_guard);
164 return is_paused;
165 }
166
157 std::unique_lock<std::mutex> StallProcesses() { 167 std::unique_lock<std::mutex> StallProcesses() {
158 std::unique_lock<std::mutex> lk(suspend_guard); 168 std::unique_lock<std::mutex> lk(suspend_guard);
159 kernel.Suspend(true); 169 kernel.Suspend(true);
@@ -214,6 +224,8 @@ struct System::Impl {
214 return SystemResultStatus::ErrorVideoCore; 224 return SystemResultStatus::ErrorVideoCore;
215 } 225 }
216 226
227 audio_core = std::make_unique<AudioCore::AudioCore>(system);
228
217 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); 229 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
218 services = std::make_unique<Service::Services>(service_manager, system); 230 services = std::make_unique<Service::Services>(service_manager, system);
219 interrupt_manager = std::make_unique<Hardware::InterruptManager>(system); 231 interrupt_manager = std::make_unique<Hardware::InterruptManager>(system);
@@ -290,7 +302,7 @@ struct System::Impl {
290 if (Settings::values.gamecard_current_game) { 302 if (Settings::values.gamecard_current_game) {
291 fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath)); 303 fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath));
292 } else if (!Settings::values.gamecard_path.GetValue().empty()) { 304 } else if (!Settings::values.gamecard_path.GetValue().empty()) {
293 const auto gamecard_path = Settings::values.gamecard_path.GetValue(); 305 const auto& gamecard_path = Settings::values.gamecard_path.GetValue();
294 fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, gamecard_path)); 306 fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, gamecard_path));
295 } 307 }
296 } 308 }
@@ -308,6 +320,8 @@ struct System::Impl {
308 } 320 }
309 321
310 void Shutdown() { 322 void Shutdown() {
323 SetShuttingDown(true);
324
311 // Log last frame performance stats if game was loded 325 // Log last frame performance stats if game was loded
312 if (perf_stats) { 326 if (perf_stats) {
313 const auto perf_results = GetAndResetPerfStats(); 327 const auto perf_results = GetAndResetPerfStats();
@@ -333,14 +347,15 @@ struct System::Impl {
333 kernel.ShutdownCores(); 347 kernel.ShutdownCores();
334 cpu_manager.Shutdown(); 348 cpu_manager.Shutdown();
335 debugger.reset(); 349 debugger.reset();
350 kernel.CloseServices();
336 services.reset(); 351 services.reset();
337 service_manager.reset(); 352 service_manager.reset();
338 cheat_engine.reset(); 353 cheat_engine.reset();
339 telemetry_session.reset(); 354 telemetry_session.reset();
340 cpu_manager.Shutdown();
341 time_manager.Shutdown(); 355 time_manager.Shutdown();
342 core_timing.Shutdown(); 356 core_timing.Shutdown();
343 app_loader.reset(); 357 app_loader.reset();
358 audio_core.reset();
344 gpu_core.reset(); 359 gpu_core.reset();
345 perf_stats.reset(); 360 perf_stats.reset();
346 kernel.Shutdown(); 361 kernel.Shutdown();
@@ -350,6 +365,14 @@ struct System::Impl {
350 LOG_DEBUG(Core, "Shutdown OK"); 365 LOG_DEBUG(Core, "Shutdown OK");
351 } 366 }
352 367
368 bool IsShuttingDown() const {
369 return is_shutting_down;
370 }
371
372 void SetShuttingDown(bool shutting_down) {
373 is_shutting_down = shutting_down;
374 }
375
353 Loader::ResultStatus GetGameName(std::string& out) const { 376 Loader::ResultStatus GetGameName(std::string& out) const {
354 if (app_loader == nullptr) 377 if (app_loader == nullptr)
355 return Loader::ResultStatus::ErrorNotInitialized; 378 return Loader::ResultStatus::ErrorNotInitialized;
@@ -392,8 +415,9 @@ struct System::Impl {
392 return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs()); 415 return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());
393 } 416 }
394 417
395 std::mutex suspend_guard; 418 mutable std::mutex suspend_guard;
396 bool is_paused{}; 419 bool is_paused{};
420 std::atomic<bool> is_shutting_down{};
397 421
398 Timing::CoreTiming core_timing; 422 Timing::CoreTiming core_timing;
399 Kernel::KernelCore kernel; 423 Kernel::KernelCore kernel;
@@ -407,6 +431,7 @@ struct System::Impl {
407 std::unique_ptr<Tegra::GPU> gpu_core; 431 std::unique_ptr<Tegra::GPU> gpu_core;
408 std::unique_ptr<Hardware::InterruptManager> interrupt_manager; 432 std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
409 std::unique_ptr<Core::DeviceMemory> device_memory; 433 std::unique_ptr<Core::DeviceMemory> device_memory;
434 std::unique_ptr<AudioCore::AudioCore> audio_core;
410 Core::Memory::Memory memory; 435 Core::Memory::Memory memory;
411 Core::HID::HIDCore hid_core; 436 Core::HID::HIDCore hid_core;
412 CpuManager cpu_manager; 437 CpuManager cpu_manager;
@@ -479,6 +504,10 @@ SystemResultStatus System::Pause() {
479 return impl->Pause(); 504 return impl->Pause();
480} 505}
481 506
507bool System::IsPaused() const {
508 return impl->IsPaused();
509}
510
482void System::InvalidateCpuInstructionCaches() { 511void System::InvalidateCpuInstructionCaches() {
483 impl->kernel.InvalidateAllInstructionCaches(); 512 impl->kernel.InvalidateAllInstructionCaches();
484} 513}
@@ -491,6 +520,14 @@ void System::Shutdown() {
491 impl->Shutdown(); 520 impl->Shutdown();
492} 521}
493 522
523bool System::IsShuttingDown() const {
524 return impl->IsShuttingDown();
525}
526
527void System::SetShuttingDown(bool shutting_down) {
528 impl->SetShuttingDown(shutting_down);
529}
530
494void System::DetachDebugger() { 531void System::DetachDebugger() {
495 if (impl->debugger) { 532 if (impl->debugger) {
496 impl->debugger->NotifyShutdown(); 533 impl->debugger->NotifyShutdown();
@@ -640,6 +677,14 @@ const HID::HIDCore& System::HIDCore() const {
640 return impl->hid_core; 677 return impl->hid_core;
641} 678}
642 679
680AudioCore::AudioCore& System::AudioCore() {
681 return *impl->audio_core;
682}
683
684const AudioCore::AudioCore& System::AudioCore() const {
685 return *impl->audio_core;
686}
687
643Timing::CoreTiming& System::CoreTiming() { 688Timing::CoreTiming& System::CoreTiming() {
644 return impl->core_timing; 689 return impl->core_timing;
645} 690}
diff --git a/src/core/core.h b/src/core/core.h
index 60efe4410..a49d1214b 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -81,6 +81,10 @@ namespace VideoCore {
81class RendererBase; 81class RendererBase;
82} // namespace VideoCore 82} // namespace VideoCore
83 83
84namespace AudioCore {
85class AudioCore;
86} // namespace AudioCore
87
84namespace Core::Timing { 88namespace Core::Timing {
85class CoreTiming; 89class CoreTiming;
86} 90}
@@ -148,6 +152,9 @@ public:
148 */ 152 */
149 [[nodiscard]] SystemResultStatus Pause(); 153 [[nodiscard]] SystemResultStatus Pause();
150 154
155 /// Check if the core is currently paused.
156 [[nodiscard]] bool IsPaused() const;
157
151 /** 158 /**
152 * Invalidate the CPU instruction caches 159 * Invalidate the CPU instruction caches
153 * This function should only be used by GDB Stub to support breakpoints, memory updates and 160 * This function should only be used by GDB Stub to support breakpoints, memory updates and
@@ -160,6 +167,12 @@ public:
160 /// Shutdown the emulated system. 167 /// Shutdown the emulated system.
161 void Shutdown(); 168 void Shutdown();
162 169
170 /// Check if the core is shutting down.
171 [[nodiscard]] bool IsShuttingDown() const;
172
173 /// Set the shutting down state.
174 void SetShuttingDown(bool shutting_down);
175
163 /// Forcibly detach the debugger if it is running. 176 /// Forcibly detach the debugger if it is running.
164 void DetachDebugger(); 177 void DetachDebugger();
165 178
@@ -250,6 +263,12 @@ public:
250 /// Gets an immutable reference to the renderer. 263 /// Gets an immutable reference to the renderer.
251 [[nodiscard]] const VideoCore::RendererBase& Renderer() const; 264 [[nodiscard]] const VideoCore::RendererBase& Renderer() const;
252 265
266 /// Gets a mutable reference to the audio interface
267 [[nodiscard]] AudioCore::AudioCore& AudioCore();
268
269 /// Gets an immutable reference to the audio interface.
270 [[nodiscard]] const AudioCore::AudioCore& AudioCore() const;
271
253 /// Gets the global scheduler 272 /// Gets the global scheduler
254 [[nodiscard]] Kernel::GlobalSchedulerContext& GlobalSchedulerContext(); 273 [[nodiscard]] Kernel::GlobalSchedulerContext& GlobalSchedulerContext();
255 274
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 45135a07f..5b3feec66 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -287,18 +287,52 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
287 BufferDescriptorB().size() > buffer_index && 287 BufferDescriptorB().size() > buffer_index &&
288 BufferDescriptorB()[buffer_index].Size() >= size, 288 BufferDescriptorB()[buffer_index].Size() >= size,
289 { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size); 289 { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size);
290 memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); 290 WriteBufferB(buffer, size, buffer_index);
291 } else { 291 } else {
292 ASSERT_OR_EXECUTE_MSG( 292 ASSERT_OR_EXECUTE_MSG(
293 BufferDescriptorC().size() > buffer_index && 293 BufferDescriptorC().size() > buffer_index &&
294 BufferDescriptorC()[buffer_index].Size() >= size, 294 BufferDescriptorC()[buffer_index].Size() >= size,
295 { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size); 295 { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size);
296 memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); 296 WriteBufferC(buffer, size, buffer_index);
297 } 297 }
298 298
299 return size; 299 return size;
300} 300}
301 301
302std::size_t HLERequestContext::WriteBufferB(const void* buffer, std::size_t size,
303 std::size_t buffer_index) const {
304 if (buffer_index >= BufferDescriptorB().size() || size == 0) {
305 return 0;
306 }
307
308 const auto buffer_size{BufferDescriptorB()[buffer_index].Size()};
309 if (size > buffer_size) {
310 LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
311 buffer_size);
312 size = buffer_size; // TODO(bunnei): This needs to be HW tested
313 }
314
315 memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
316 return size;
317}
318
319std::size_t HLERequestContext::WriteBufferC(const void* buffer, std::size_t size,
320 std::size_t buffer_index) const {
321 if (buffer_index >= BufferDescriptorC().size() || size == 0) {
322 return 0;
323 }
324
325 const auto buffer_size{BufferDescriptorC()[buffer_index].Size()};
326 if (size > buffer_size) {
327 LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
328 buffer_size);
329 size = buffer_size; // TODO(bunnei): This needs to be HW tested
330 }
331
332 memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
333 return size;
334}
335
302std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const { 336std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const {
303 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && 337 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
304 BufferDescriptorA()[buffer_index].Size()}; 338 BufferDescriptorA()[buffer_index].Size()};
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index d3abeee85..99265ce90 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -277,6 +277,14 @@ public:
277 std::size_t WriteBuffer(const void* buffer, std::size_t size, 277 std::size_t WriteBuffer(const void* buffer, std::size_t size,
278 std::size_t buffer_index = 0) const; 278 std::size_t buffer_index = 0) const;
279 279
280 /// Helper function to write buffer B
281 std::size_t WriteBufferB(const void* buffer, std::size_t size,
282 std::size_t buffer_index = 0) const;
283
284 /// Helper function to write buffer C
285 std::size_t WriteBufferC(const void* buffer, std::size_t size,
286 std::size_t buffer_index = 0) const;
287
280 /* Helper function to write a buffer using the appropriate buffer descriptor 288 /* Helper function to write a buffer using the appropriate buffer descriptor
281 * 289 *
282 * @tparam T an arbitrary container that satisfies the 290 * @tparam T an arbitrary container that satisfies the
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 7307cf262..f23c629dc 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -95,19 +95,7 @@ struct KernelCore::Impl {
95 95
96 process_list.clear(); 96 process_list.clear();
97 97
98 // Close all open server sessions and ports. 98 CloseServices();
99 std::unordered_set<KAutoObject*> server_objects_;
100 {
101 std::scoped_lock lk(server_objects_lock);
102 server_objects_ = server_objects;
103 server_objects.clear();
104 }
105 for (auto* server_object : server_objects_) {
106 server_object->Close();
107 }
108
109 // Ensures all service threads gracefully shutdown.
110 ClearServiceThreads();
111 99
112 next_object_id = 0; 100 next_object_id = 0;
113 next_kernel_process_id = KProcess::InitialKIPIDMin; 101 next_kernel_process_id = KProcess::InitialKIPIDMin;
@@ -191,6 +179,22 @@ struct KernelCore::Impl {
191 global_object_list_container.reset(); 179 global_object_list_container.reset();
192 } 180 }
193 181
182 void CloseServices() {
183 // Close all open server sessions and ports.
184 std::unordered_set<KAutoObject*> server_objects_;
185 {
186 std::scoped_lock lk(server_objects_lock);
187 server_objects_ = server_objects;
188 server_objects.clear();
189 }
190 for (auto* server_object : server_objects_) {
191 server_object->Close();
192 }
193
194 // Ensures all service threads gracefully shutdown.
195 ClearServiceThreads();
196 }
197
194 void InitializePhysicalCores() { 198 void InitializePhysicalCores() {
195 exclusive_monitor = 199 exclusive_monitor =
196 Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); 200 Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES);
@@ -813,6 +817,10 @@ void KernelCore::Shutdown() {
813 impl->Shutdown(); 817 impl->Shutdown();
814} 818}
815 819
820void KernelCore::CloseServices() {
821 impl->CloseServices();
822}
823
816const KResourceLimit* KernelCore::GetSystemResourceLimit() const { 824const KResourceLimit* KernelCore::GetSystemResourceLimit() const {
817 return impl->system_resource_limit; 825 return impl->system_resource_limit;
818} 826}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index aa0ebaa02..6c7cf6af2 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -109,6 +109,9 @@ public:
109 /// Clears all resources in use by the kernel instance. 109 /// Clears all resources in use by the kernel instance.
110 void Shutdown(); 110 void Shutdown();
111 111
112 /// Close all active services in use by the kernel instance.
113 void CloseServices();
114
112 /// Retrieves a shared pointer to the system resource limit instance. 115 /// Retrieves a shared pointer to the system resource limit instance.
113 const KResourceLimit* GetSystemResourceLimit() const; 116 const KResourceLimit* GetSystemResourceLimit() const;
114 117
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 9116dd77c..118f226e4 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -5,7 +5,6 @@
5#include <array> 5#include <array>
6#include <cinttypes> 6#include <cinttypes>
7#include <cstring> 7#include <cstring>
8#include "audio_core/audio_renderer.h"
9#include "common/settings.h" 8#include "common/settings.h"
10#include "core/core.h" 9#include "core/core.h"
11#include "core/file_sys/control_metadata.h" 10#include "core/file_sys/control_metadata.h"
@@ -286,7 +285,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
286 {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"}, 285 {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
287 {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"}, 286 {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
288 {64, nullptr, "SetInputDetectionSourceSet"}, 287 {64, nullptr, "SetInputDetectionSourceSet"},
289 {65, nullptr, "ReportUserIsActive"}, 288 {65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"},
290 {66, nullptr, "GetCurrentIlluminance"}, 289 {66, nullptr, "GetCurrentIlluminance"},
291 {67, nullptr, "IsIlluminanceAvailable"}, 290 {67, nullptr, "IsIlluminanceAvailable"},
292 {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"}, 291 {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"},
@@ -518,6 +517,13 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
518 rb.Push<u32>(idle_time_detection_extension); 517 rb.Push<u32>(idle_time_detection_extension);
519} 518}
520 519
520void ISelfController::ReportUserIsActive(Kernel::HLERequestContext& ctx) {
521 LOG_WARNING(Service_AM, "(STUBBED) called");
522
523 IPC::ResponseBuilder rb{ctx, 2};
524 rb.Push(ResultSuccess);
525}
526
521void ISelfController::SetAutoSleepDisabled(Kernel::HLERequestContext& ctx) { 527void ISelfController::SetAutoSleepDisabled(Kernel::HLERequestContext& ctx) {
522 IPC::RequestParser rp{ctx}; 528 IPC::RequestParser rp{ctx};
523 is_auto_sleep_disabled = rp.Pop<bool>(); 529 is_auto_sleep_disabled = rp.Pop<bool>();
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 53144427b..bb75c6281 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -175,6 +175,7 @@ private:
175 void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); 175 void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
176 void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); 176 void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
177 void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); 177 void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
178 void ReportUserIsActive(Kernel::HLERequestContext& ctx);
178 void SetAutoSleepDisabled(Kernel::HLERequestContext& ctx); 179 void SetAutoSleepDisabled(Kernel::HLERequestContext& ctx);
179 void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx); 180 void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx);
180 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); 181 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 18d3ae682..48a9a73a0 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -1,68 +1,211 @@
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 "audio_core/in/audio_in_system.h"
5#include "audio_core/renderer/audio_device.h"
6#include "common/common_funcs.h"
4#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/string_util.h"
5#include "core/core.h" 9#include "core/core.h"
6#include "core/hle/ipc_helpers.h" 10#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/k_event.h" 11#include "core/hle/kernel/k_event.h"
8#include "core/hle/service/audio/audin_u.h" 12#include "core/hle/service/audio/audin_u.h"
9 13
10namespace Service::Audio { 14namespace Service::Audio {
15using namespace AudioCore::AudioIn;
11 16
12IAudioIn::IAudioIn(Core::System& system_) 17class IAudioIn final : public ServiceFramework<IAudioIn> {
13 : ServiceFramework{system_, "IAudioIn"}, service_context{system_, "IAudioIn"} { 18public:
14 // clang-format off 19 explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
15 static const FunctionInfo functions[] = { 20 std::string& device_name, const AudioInParameter& in_params, u32 handle,
16 {0, nullptr, "GetAudioInState"}, 21 u64 applet_resource_user_id)
17 {1, &IAudioIn::Start, "Start"}, 22 : ServiceFramework{system_, "IAudioIn"},
18 {2, nullptr, "Stop"}, 23 service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")},
19 {3, nullptr, "AppendAudioInBuffer"}, 24 impl{std::make_shared<In>(system_, manager, event, session_id)} {
20 {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"}, 25 // clang-format off
21 {5, nullptr, "GetReleasedAudioInBuffer"}, 26 static const FunctionInfo functions[] = {
22 {6, nullptr, "ContainsAudioInBuffer"}, 27 {0, &IAudioIn::GetAudioInState, "GetAudioInState"},
23 {7, nullptr, "AppendUacInBuffer"}, 28 {1, &IAudioIn::Start, "Start"},
24 {8, &IAudioIn::AppendAudioInBufferAuto, "AppendAudioInBufferAuto"}, 29 {2, &IAudioIn::Stop, "Stop"},
25 {9, nullptr, "GetReleasedAudioInBuffersAuto"}, 30 {3, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBuffer"},
26 {10, nullptr, "AppendUacInBufferAuto"}, 31 {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"},
27 {11, nullptr, "GetAudioInBufferCount"}, 32 {5, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffer"},
28 {12, nullptr, "SetDeviceGain"}, 33 {6, &IAudioIn::ContainsAudioInBuffer, "ContainsAudioInBuffer"},
29 {13, nullptr, "GetDeviceGain"}, 34 {7, &IAudioIn::AppendAudioInBuffer, "AppendUacInBuffer"},
30 {14, nullptr, "FlushAudioInBuffers"}, 35 {8, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBufferAuto"},
31 }; 36 {9, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffersAuto"},
32 // clang-format on 37 {10, &IAudioIn::AppendAudioInBuffer, "AppendUacInBufferAuto"},
38 {11, &IAudioIn::GetAudioInBufferCount, "GetAudioInBufferCount"},
39 {12, &IAudioIn::SetDeviceGain, "SetDeviceGain"},
40 {13, &IAudioIn::GetDeviceGain, "GetDeviceGain"},
41 {14, &IAudioIn::FlushAudioInBuffers, "FlushAudioInBuffers"},
42 };
43 // clang-format on
33 44
34 RegisterHandlers(functions); 45 RegisterHandlers(functions);
35 46
36 buffer_event = service_context.CreateEvent("IAudioIn:BufferEvent"); 47 if (impl->GetSystem()
37} 48 .Initialize(device_name, in_params, handle, applet_resource_user_id)
49 .IsError()) {
50 LOG_ERROR(Service_Audio, "Failed to initialize the AudioIn System!");
51 }
52 }
38 53
39IAudioIn::~IAudioIn() { 54 ~IAudioIn() override {
40 service_context.CloseEvent(buffer_event); 55 impl->Free();
41} 56 service_context.CloseEvent(event);
57 }
42 58
43void IAudioIn::Start(Kernel::HLERequestContext& ctx) { 59 [[nodiscard]] std::shared_ptr<In> GetImpl() {
44 LOG_WARNING(Service_Audio, "(STUBBED) called"); 60 return impl;
61 }
45 62
46 IPC::ResponseBuilder rb{ctx, 2}; 63private:
47 rb.Push(ResultSuccess); 64 void GetAudioInState(Kernel::HLERequestContext& ctx) {
48} 65 const auto state = static_cast<u32>(impl->GetState());
49 66
50void IAudioIn::RegisterBufferEvent(Kernel::HLERequestContext& ctx) { 67 LOG_DEBUG(Service_Audio, "called. State={}", state);
51 LOG_WARNING(Service_Audio, "(STUBBED) called");
52 68
53 IPC::ResponseBuilder rb{ctx, 2, 1}; 69 IPC::ResponseBuilder rb{ctx, 3};
54 rb.Push(ResultSuccess); 70 rb.Push(ResultSuccess);
55 rb.PushCopyObjects(buffer_event->GetReadableEvent()); 71 rb.Push(state);
56} 72 }
57 73
58void IAudioIn::AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx) { 74 void Start(Kernel::HLERequestContext& ctx) {
59 LOG_WARNING(Service_Audio, "(STUBBED) called"); 75 LOG_DEBUG(Service_Audio, "called");
60 76
61 IPC::ResponseBuilder rb{ctx, 2}; 77 auto result = impl->StartSystem();
62 rb.Push(ResultSuccess); 78
63} 79 IPC::ResponseBuilder rb{ctx, 2};
80 rb.Push(result);
81 }
82
83 void Stop(Kernel::HLERequestContext& ctx) {
84 LOG_DEBUG(Service_Audio, "called");
85
86 auto result = impl->StopSystem();
87
88 IPC::ResponseBuilder rb{ctx, 2};
89 rb.Push(result);
90 }
91
92 void AppendAudioInBuffer(Kernel::HLERequestContext& ctx) {
93 IPC::RequestParser rp{ctx};
94 u64 tag = rp.PopRaw<u64>();
64 95
65AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} { 96 const auto in_buffer_size{ctx.GetReadBufferSize()};
97 if (in_buffer_size < sizeof(AudioInBuffer)) {
98 LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!");
99 }
100
101 const auto& in_buffer = ctx.ReadBuffer();
102 AudioInBuffer buffer{};
103 std::memcpy(&buffer, in_buffer.data(), sizeof(AudioInBuffer));
104
105 [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
106 LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag);
107
108 auto result = impl->AppendBuffer(buffer, tag);
109
110 IPC::ResponseBuilder rb{ctx, 2};
111 rb.Push(result);
112 }
113
114 void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
115 LOG_DEBUG(Service_Audio, "called");
116
117 auto& buffer_event = impl->GetBufferEvent();
118
119 IPC::ResponseBuilder rb{ctx, 2, 1};
120 rb.Push(ResultSuccess);
121 rb.PushCopyObjects(buffer_event);
122 }
123
124 void GetReleasedAudioInBuffer(Kernel::HLERequestContext& ctx) {
125 auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64);
126 std::vector<u64> released_buffers(write_buffer_size, 0);
127
128 auto count = impl->GetReleasedBuffers(released_buffers);
129
130 [[maybe_unused]] std::string tags{};
131 for (u32 i = 0; i < count; i++) {
132 tags += fmt::format("{:08X}, ", released_buffers[i]);
133 }
134 [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
135 LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
136 tags);
137
138 ctx.WriteBuffer(released_buffers);
139 IPC::ResponseBuilder rb{ctx, 3};
140 rb.Push(ResultSuccess);
141 rb.Push(count);
142 }
143
144 void ContainsAudioInBuffer(Kernel::HLERequestContext& ctx) {
145 IPC::RequestParser rp{ctx};
146
147 const u64 tag{rp.Pop<u64>()};
148 const auto buffer_queued{impl->ContainsAudioBuffer(tag)};
149
150 LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued);
151
152 IPC::ResponseBuilder rb{ctx, 3};
153 rb.Push(ResultSuccess);
154 rb.Push(buffer_queued);
155 }
156
157 void GetAudioInBufferCount(Kernel::HLERequestContext& ctx) {
158 const auto buffer_count = impl->GetBufferCount();
159
160 LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count);
161
162 IPC::ResponseBuilder rb{ctx, 3};
163
164 rb.Push(ResultSuccess);
165 rb.Push(buffer_count);
166 }
167
168 void SetDeviceGain(Kernel::HLERequestContext& ctx) {
169 IPC::RequestParser rp{ctx};
170
171 const auto volume{rp.Pop<f32>()};
172 LOG_DEBUG(Service_Audio, "called. Gain {}", volume);
173
174 impl->SetVolume(volume);
175
176 IPC::ResponseBuilder rb{ctx, 2};
177 rb.Push(ResultSuccess);
178 }
179
180 void GetDeviceGain(Kernel::HLERequestContext& ctx) {
181 auto volume{impl->GetVolume()};
182
183 LOG_DEBUG(Service_Audio, "called. Gain {}", volume);
184
185 IPC::ResponseBuilder rb{ctx, 3};
186 rb.Push(ResultSuccess);
187 rb.Push(volume);
188 }
189
190 void FlushAudioInBuffers(Kernel::HLERequestContext& ctx) {
191 bool flushed{impl->FlushAudioInBuffers()};
192
193 LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed);
194
195 IPC::ResponseBuilder rb{ctx, 3};
196 rb.Push(ResultSuccess);
197 rb.Push(flushed);
198 }
199
200 KernelHelpers::ServiceContext service_context;
201 Kernel::KEvent* event;
202 std::shared_ptr<AudioCore::AudioIn::In> impl;
203};
204
205AudInU::AudInU(Core::System& system_)
206 : ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew},
207 service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>(
208 system_)} {
66 // clang-format off 209 // clang-format off
67 static const FunctionInfo functions[] = { 210 static const FunctionInfo functions[] = {
68 {0, &AudInU::ListAudioIns, "ListAudioIns"}, 211 {0, &AudInU::ListAudioIns, "ListAudioIns"},
@@ -80,59 +223,152 @@ AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} {
80AudInU::~AudInU() = default; 223AudInU::~AudInU() = default;
81 224
82void AudInU::ListAudioIns(Kernel::HLERequestContext& ctx) { 225void AudInU::ListAudioIns(Kernel::HLERequestContext& ctx) {
226 using namespace AudioCore::AudioRenderer;
227
83 LOG_DEBUG(Service_Audio, "called"); 228 LOG_DEBUG(Service_Audio, "called");
84 const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioInDeviceName);
85 229
86 const std::size_t device_count = std::min(count, audio_device_names.size()); 230 const auto write_count =
87 std::vector<AudioInDeviceName> device_names; 231 static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
88 device_names.reserve(device_count); 232 std::vector<AudioDevice::AudioDeviceName> device_names{};
89 233
90 for (std::size_t i = 0; i < device_count; i++) { 234 u32 out_count{0};
91 const auto& device_name = audio_device_names[i]; 235 if (write_count > 0) {
92 auto& entry = device_names.emplace_back(); 236 out_count = impl->GetDeviceNames(device_names, write_count, false);
93 device_name.copy(entry.data(), device_name.size()); 237 ctx.WriteBuffer(device_names);
94 } 238 }
95 239
96 ctx.WriteBuffer(device_names);
97
98 IPC::ResponseBuilder rb{ctx, 3}; 240 IPC::ResponseBuilder rb{ctx, 3};
99 rb.Push(ResultSuccess); 241 rb.Push(ResultSuccess);
100 rb.Push(static_cast<u32>(device_names.size())); 242 rb.Push(out_count);
101} 243}
102 244
103void AudInU::ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx) { 245void AudInU::ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx) {
246 using namespace AudioCore::AudioRenderer;
247
104 LOG_DEBUG(Service_Audio, "called"); 248 LOG_DEBUG(Service_Audio, "called");
105 constexpr u32 device_count = 0;
106 249
107 // Since we don't actually use any other audio input devices, we return 0 devices. Filtered 250 const auto write_count =
108 // device listing just omits the default input device 251 static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
252 std::vector<AudioDevice::AudioDeviceName> device_names{};
253
254 u32 out_count{0};
255 if (write_count > 0) {
256 out_count = impl->GetDeviceNames(device_names, write_count, true);
257 ctx.WriteBuffer(device_names);
258 }
109 259
110 IPC::ResponseBuilder rb{ctx, 3}; 260 IPC::ResponseBuilder rb{ctx, 3};
111 rb.Push(ResultSuccess); 261 rb.Push(ResultSuccess);
112 rb.Push(static_cast<u32>(device_count)); 262 rb.Push(out_count);
113} 263}
114 264
115void AudInU::OpenInOutImpl(Kernel::HLERequestContext& ctx) { 265void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) {
116 AudInOutParams params{}; 266 IPC::RequestParser rp{ctx};
117 params.channel_count = 2; 267 auto in_params{rp.PopRaw<AudioInParameter>()};
118 params.sample_format = SampleFormat::PCM16; 268 auto applet_resource_user_id{rp.PopRaw<u64>()};
119 params.sample_rate = 48000; 269 const auto device_name_data{ctx.ReadBuffer()};
120 params.state = State::Started; 270 auto device_name = Common::StringFromBuffer(device_name_data);
271 auto handle{ctx.GetCopyHandle(0)};
272
273 std::scoped_lock l{impl->mutex};
274 auto link{impl->LinkToManager()};
275 if (link.IsError()) {
276 LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager");
277 IPC::ResponseBuilder rb{ctx, 2};
278 rb.Push(link);
279 return;
280 }
281
282 size_t new_session_id{};
283 auto result{impl->AcquireSessionId(new_session_id)};
284 if (result.IsError()) {
285 IPC::ResponseBuilder rb{ctx, 2};
286 rb.Push(result);
287 return;
288 }
289
290 LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
291 impl->num_free_sessions);
292
293 auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
294 in_params, handle, applet_resource_user_id);
295 impl->sessions[new_session_id] = audio_in->GetImpl();
296 impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
297
298 auto& out_system = impl->sessions[new_session_id]->GetSystem();
299 AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
300 .channel_count = out_system.GetChannelCount(),
301 .sample_format =
302 static_cast<u32>(out_system.GetSampleFormat()),
303 .state = static_cast<u32>(out_system.GetState())};
121 304
122 IPC::ResponseBuilder rb{ctx, 6, 0, 1}; 305 IPC::ResponseBuilder rb{ctx, 6, 0, 1};
123 rb.Push(ResultSuccess);
124 rb.PushRaw<AudInOutParams>(params);
125 rb.PushIpcInterface<IAudioIn>(system);
126}
127 306
128void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) { 307 std::string out_name{out_system.GetName()};
129 LOG_WARNING(Service_Audio, "(STUBBED) called"); 308 ctx.WriteBuffer(out_name);
130 OpenInOutImpl(ctx); 309
310 rb.Push(ResultSuccess);
311 rb.PushRaw<AudioInParameterInternal>(out_params);
312 rb.PushIpcInterface<IAudioIn>(audio_in);
131} 313}
132 314
133void AudInU::OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx) { 315void AudInU::OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx) {
134 LOG_WARNING(Service_Audio, "(STUBBED) called"); 316 IPC::RequestParser rp{ctx};
135 OpenInOutImpl(ctx); 317 auto protocol_specified{rp.PopRaw<u64>()};
318 auto in_params{rp.PopRaw<AudioInParameter>()};
319 auto applet_resource_user_id{rp.PopRaw<u64>()};
320 const auto device_name_data{ctx.ReadBuffer()};
321 auto device_name = Common::StringFromBuffer(device_name_data);
322 auto handle{ctx.GetCopyHandle(0)};
323
324 std::scoped_lock l{impl->mutex};
325 auto link{impl->LinkToManager()};
326 if (link.IsError()) {
327 LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager");
328 IPC::ResponseBuilder rb{ctx, 2};
329 rb.Push(link);
330 return;
331 }
332
333 size_t new_session_id{};
334 auto result{impl->AcquireSessionId(new_session_id)};
335 if (result.IsError()) {
336 IPC::ResponseBuilder rb{ctx, 2};
337 rb.Push(result);
338 return;
339 }
340
341 LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
342 impl->num_free_sessions);
343
344 auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
345 in_params, handle, applet_resource_user_id);
346 impl->sessions[new_session_id] = audio_in->GetImpl();
347 impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
348
349 auto& out_system = impl->sessions[new_session_id]->GetSystem();
350 AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
351 .channel_count = out_system.GetChannelCount(),
352 .sample_format =
353 static_cast<u32>(out_system.GetSampleFormat()),
354 .state = static_cast<u32>(out_system.GetState())};
355
356 IPC::ResponseBuilder rb{ctx, 6, 0, 1};
357
358 std::string out_name{out_system.GetName()};
359 if (protocol_specified == 0) {
360 if (out_system.IsUac()) {
361 out_name = "UacIn";
362 } else {
363 out_name = "DeviceIn";
364 }
365 }
366
367 ctx.WriteBuffer(out_name);
368
369 rb.Push(ResultSuccess);
370 rb.PushRaw<AudioInParameterInternal>(out_params);
371 rb.PushIpcInterface<IAudioIn>(audio_in);
136} 372}
137 373
138} // namespace Service::Audio 374} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audin_u.h b/src/core/hle/service/audio/audin_u.h
index 2bfa38ecc..b45fda78a 100644
--- a/src/core/hle/service/audio/audin_u.h
+++ b/src/core/hle/service/audio/audin_u.h
@@ -3,6 +3,8 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "audio_core/audio_in_manager.h"
7#include "audio_core/in/audio_in.h"
6#include "core/hle/service/kernel_helpers.h" 8#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
8 10
@@ -14,22 +16,12 @@ namespace Kernel {
14class HLERequestContext; 16class HLERequestContext;
15} 17}
16 18
17namespace Service::Audio { 19namespace AudioCore::AudioOut {
18 20class Manager;
19class IAudioIn final : public ServiceFramework<IAudioIn> { 21class In;
20public: 22} // namespace AudioCore::AudioOut
21 explicit IAudioIn(Core::System& system_);
22 ~IAudioIn() override;
23
24private:
25 void Start(Kernel::HLERequestContext& ctx);
26 void RegisterBufferEvent(Kernel::HLERequestContext& ctx);
27 void AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx);
28
29 KernelHelpers::ServiceContext service_context;
30 23
31 Kernel::KEvent* buffer_event; 24namespace Service::Audio {
32};
33 25
34class AudInU final : public ServiceFramework<AudInU> { 26class AudInU final : public ServiceFramework<AudInU> {
35public: 27public:
@@ -37,33 +29,14 @@ public:
37 ~AudInU() override; 29 ~AudInU() override;
38 30
39private: 31private:
40 enum class SampleFormat : u32_le {
41 PCM16 = 2,
42 };
43
44 enum class State : u32_le {
45 Started = 0,
46 Stopped = 1,
47 };
48
49 struct AudInOutParams {
50 u32_le sample_rate{};
51 u32_le channel_count{};
52 SampleFormat sample_format{};
53 State state{};
54 };
55 static_assert(sizeof(AudInOutParams) == 0x10, "AudInOutParams is an invalid size");
56
57 using AudioInDeviceName = std::array<char, 256>;
58 static constexpr std::array<std::string_view, 1> audio_device_names{{
59 "BuiltInHeadset",
60 }};
61
62 void ListAudioIns(Kernel::HLERequestContext& ctx); 32 void ListAudioIns(Kernel::HLERequestContext& ctx);
63 void ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx); 33 void ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx);
64 void OpenInOutImpl(Kernel::HLERequestContext& ctx); 34 void OpenInOutImpl(Kernel::HLERequestContext& ctx);
65 void OpenAudioIn(Kernel::HLERequestContext& ctx); 35 void OpenAudioIn(Kernel::HLERequestContext& ctx);
66 void OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx); 36 void OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx);
37
38 KernelHelpers::ServiceContext service_context;
39 std::unique_ptr<AudioCore::AudioIn::Manager> impl;
67}; 40};
68 41
69} // namespace Service::Audio 42} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index b0dad6053..a44dd842a 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -5,56 +5,43 @@
5#include <cstring> 5#include <cstring>
6#include <vector> 6#include <vector>
7 7
8#include "audio_core/audio_out.h" 8#include "audio_core/out/audio_out_system.h"
9#include "audio_core/codec.h" 9#include "audio_core/renderer/audio_device.h"
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/string_util.h"
12#include "common/swap.h" 13#include "common/swap.h"
13#include "core/core.h" 14#include "core/core.h"
14#include "core/hle/ipc_helpers.h" 15#include "core/hle/ipc_helpers.h"
15#include "core/hle/kernel/k_event.h" 16#include "core/hle/kernel/k_event.h"
16#include "core/hle/service/audio/audout_u.h" 17#include "core/hle/service/audio/audout_u.h"
17#include "core/hle/service/audio/errors.h" 18#include "core/hle/service/audio/errors.h"
18#include "core/hle/service/kernel_helpers.h"
19#include "core/memory.h" 19#include "core/memory.h"
20 20
21namespace Service::Audio { 21namespace Service::Audio {
22 22using namespace AudioCore::AudioOut;
23constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}};
24constexpr int DefaultSampleRate{48000};
25
26struct AudoutParams {
27 s32_le sample_rate;
28 u16_le channel_count;
29 INSERT_PADDING_BYTES_NOINIT(2);
30};
31static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size");
32
33enum class AudioState : u32 {
34 Started,
35 Stopped,
36};
37 23
38class IAudioOut final : public ServiceFramework<IAudioOut> { 24class IAudioOut final : public ServiceFramework<IAudioOut> {
39public: 25public:
40 explicit IAudioOut(Core::System& system_, AudoutParams audio_params_, 26 explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
41 AudioCore::AudioOut& audio_core_, std::string&& device_name_, 27 size_t session_id, std::string& device_name,
42 std::string&& unique_name) 28 const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id)
43 : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, 29 : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
44 audio_core{audio_core_}, device_name{std::move(device_name_)}, 30 service_context{system_, "IAudioOut"}, event{service_context.CreateEvent(
45 audio_params{audio_params_}, main_memory{system.Memory()}, service_context{system_, 31 "AudioOutEvent")},
46 "IAudioOut"} { 32 impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} {
33
47 // clang-format off 34 // clang-format off
48 static const FunctionInfo functions[] = { 35 static const FunctionInfo functions[] = {
49 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, 36 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
50 {1, &IAudioOut::StartAudioOut, "Start"}, 37 {1, &IAudioOut::Start, "Start"},
51 {2, &IAudioOut::StopAudioOut, "Stop"}, 38 {2, &IAudioOut::Stop, "Stop"},
52 {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"}, 39 {3, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBuffer"},
53 {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"}, 40 {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
54 {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffers"}, 41 {5, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffers"},
55 {6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"}, 42 {6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"},
56 {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"}, 43 {7, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBufferAuto"},
57 {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"}, 44 {8, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffersAuto"},
58 {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"}, 45 {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
59 {10, &IAudioOut::GetAudioOutPlayedSampleCount, "GetAudioOutPlayedSampleCount"}, 46 {10, &IAudioOut::GetAudioOutPlayedSampleCount, "GetAudioOutPlayedSampleCount"},
60 {11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"}, 47 {11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"},
@@ -64,241 +51,263 @@ public:
64 // clang-format on 51 // clang-format on
65 RegisterHandlers(functions); 52 RegisterHandlers(functions);
66 53
67 // This is the event handle used to check if the audio buffer was released 54 if (impl->GetSystem()
68 buffer_event = service_context.CreateEvent("IAudioOutBufferReleased"); 55 .Initialize(device_name, in_params, handle, applet_resource_user_id)
69 56 .IsError()) {
70 stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, 57 LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!");
71 audio_params.channel_count, std::move(unique_name), [this] { 58 }
72 const auto guard = LockService();
73 buffer_event->GetWritableEvent().Signal();
74 });
75 } 59 }
76 60
77 ~IAudioOut() override { 61 ~IAudioOut() override {
78 service_context.CloseEvent(buffer_event); 62 impl->Free();
63 service_context.CloseEvent(event);
79 } 64 }
80 65
81private: 66 [[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() {
82 struct AudioBuffer { 67 return impl;
83 u64_le next; 68 }
84 u64_le buffer;
85 u64_le buffer_capacity;
86 u64_le buffer_size;
87 u64_le offset;
88 };
89 static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size");
90 69
70private:
91 void GetAudioOutState(Kernel::HLERequestContext& ctx) { 71 void GetAudioOutState(Kernel::HLERequestContext& ctx) {
92 LOG_DEBUG(Service_Audio, "called"); 72 const auto state = static_cast<u32>(impl->GetState());
73
74 LOG_DEBUG(Service_Audio, "called. State={}", state);
93 75
94 IPC::ResponseBuilder rb{ctx, 3}; 76 IPC::ResponseBuilder rb{ctx, 3};
95 rb.Push(ResultSuccess); 77 rb.Push(ResultSuccess);
96 rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped)); 78 rb.Push(state);
97 } 79 }
98 80
99 void StartAudioOut(Kernel::HLERequestContext& ctx) { 81 void Start(Kernel::HLERequestContext& ctx) {
100 LOG_DEBUG(Service_Audio, "called"); 82 LOG_DEBUG(Service_Audio, "called");
101 83
102 if (stream->IsPlaying()) { 84 auto result = impl->StartSystem();
103 IPC::ResponseBuilder rb{ctx, 2};
104 rb.Push(ERR_OPERATION_FAILED);
105 return;
106 }
107
108 audio_core.StartStream(stream);
109 85
110 IPC::ResponseBuilder rb{ctx, 2}; 86 IPC::ResponseBuilder rb{ctx, 2};
111 rb.Push(ResultSuccess); 87 rb.Push(result);
112 } 88 }
113 89
114 void StopAudioOut(Kernel::HLERequestContext& ctx) { 90 void Stop(Kernel::HLERequestContext& ctx) {
115 LOG_DEBUG(Service_Audio, "called"); 91 LOG_DEBUG(Service_Audio, "called");
116 92
117 if (stream->IsPlaying()) { 93 auto result = impl->StopSystem();
118 audio_core.StopStream(stream);
119 }
120 94
121 IPC::ResponseBuilder rb{ctx, 2}; 95 IPC::ResponseBuilder rb{ctx, 2};
122 rb.Push(ResultSuccess); 96 rb.Push(result);
123 } 97 }
124 98
125 void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { 99 void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) {
126 LOG_DEBUG(Service_Audio, "called");
127
128 IPC::ResponseBuilder rb{ctx, 2, 1};
129 rb.Push(ResultSuccess);
130 rb.PushCopyObjects(buffer_event->GetReadableEvent());
131 }
132
133 void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
134 LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description());
135 IPC::RequestParser rp{ctx}; 100 IPC::RequestParser rp{ctx};
101 u64 tag = rp.PopRaw<u64>();
136 102
137 const auto& input_buffer{ctx.ReadBuffer()}; 103 const auto in_buffer_size{ctx.GetReadBufferSize()};
138 ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer), 104 if (in_buffer_size < sizeof(AudioOutBuffer)) {
139 "AudioBuffer input is an invalid size!"); 105 LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!");
140 AudioBuffer audio_buffer{}; 106 }
141 std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
142 const u64 tag{rp.Pop<u64>()};
143 107
144 std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16)); 108 const auto& in_buffer = ctx.ReadBuffer();
145 main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size); 109 AudioOutBuffer buffer{};
110 std::memcpy(&buffer, in_buffer.data(), sizeof(AudioOutBuffer));
146 111
147 if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) { 112 [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
148 IPC::ResponseBuilder rb{ctx, 2}; 113 LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag);
149 rb.Push(ERR_BUFFER_COUNT_EXCEEDED); 114
150 return; 115 auto result = impl->AppendBuffer(buffer, tag);
151 }
152 116
153 IPC::ResponseBuilder rb{ctx, 2}; 117 IPC::ResponseBuilder rb{ctx, 2};
118 rb.Push(result);
119 }
120
121 void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
122 LOG_DEBUG(Service_Audio, "called");
123
124 auto& buffer_event = impl->GetBufferEvent();
125
126 IPC::ResponseBuilder rb{ctx, 2, 1};
154 rb.Push(ResultSuccess); 127 rb.Push(ResultSuccess);
128 rb.PushCopyObjects(buffer_event);
155 } 129 }
156 130
157 void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { 131 void GetReleasedAudioOutBuffers(Kernel::HLERequestContext& ctx) {
158 LOG_DEBUG(Service_Audio, "called {}", ctx.Description()); 132 auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64);
133 std::vector<u64> released_buffers(write_buffer_size, 0);
159 134
160 const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)}; 135 auto count = impl->GetReleasedBuffers(released_buffers);
161 const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
162 136
163 std::vector<u64> tags{released_buffers}; 137 [[maybe_unused]] std::string tags{};
164 tags.resize(max_count); 138 for (u32 i = 0; i < count; i++) {
165 ctx.WriteBuffer(tags); 139 tags += fmt::format("{:08X}, ", released_buffers[i]);
140 }
141 [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
142 LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
143 tags);
166 144
145 ctx.WriteBuffer(released_buffers);
167 IPC::ResponseBuilder rb{ctx, 3}; 146 IPC::ResponseBuilder rb{ctx, 3};
168 rb.Push(ResultSuccess); 147 rb.Push(ResultSuccess);
169 rb.Push<u32>(static_cast<u32>(released_buffers.size())); 148 rb.Push(count);
170 } 149 }
171 150
172 void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) { 151 void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) {
173 LOG_DEBUG(Service_Audio, "called");
174
175 IPC::RequestParser rp{ctx}; 152 IPC::RequestParser rp{ctx};
153
176 const u64 tag{rp.Pop<u64>()}; 154 const u64 tag{rp.Pop<u64>()};
155 const auto buffer_queued{impl->ContainsAudioBuffer(tag)};
156
157 LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued);
158
177 IPC::ResponseBuilder rb{ctx, 3}; 159 IPC::ResponseBuilder rb{ctx, 3};
178 rb.Push(ResultSuccess); 160 rb.Push(ResultSuccess);
179 rb.Push(stream->ContainsBuffer(tag)); 161 rb.Push(buffer_queued);
180 } 162 }
181 163
182 void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) { 164 void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) {
183 LOG_DEBUG(Service_Audio, "called"); 165 const auto buffer_count = impl->GetBufferCount();
166
167 LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count);
184 168
185 IPC::ResponseBuilder rb{ctx, 3}; 169 IPC::ResponseBuilder rb{ctx, 3};
170
186 rb.Push(ResultSuccess); 171 rb.Push(ResultSuccess);
187 rb.Push(static_cast<u32>(stream->GetQueueSize())); 172 rb.Push(buffer_count);
188 } 173 }
189 174
190 void GetAudioOutPlayedSampleCount(Kernel::HLERequestContext& ctx) { 175 void GetAudioOutPlayedSampleCount(Kernel::HLERequestContext& ctx) {
191 LOG_DEBUG(Service_Audio, "called"); 176 const auto samples_played = impl->GetPlayedSampleCount();
177
178 LOG_DEBUG(Service_Audio, "called. Played samples={}", samples_played);
192 179
193 IPC::ResponseBuilder rb{ctx, 4}; 180 IPC::ResponseBuilder rb{ctx, 4};
181
194 rb.Push(ResultSuccess); 182 rb.Push(ResultSuccess);
195 rb.Push(stream->GetPlayedSampleCount()); 183 rb.Push(samples_played);
196 } 184 }
197 185
198 void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) { 186 void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) {
199 LOG_DEBUG(Service_Audio, "called"); 187 bool flushed{impl->FlushAudioOutBuffers()};
188
189 LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed);
200 190
201 IPC::ResponseBuilder rb{ctx, 3}; 191 IPC::ResponseBuilder rb{ctx, 3};
202 rb.Push(ResultSuccess); 192 rb.Push(ResultSuccess);
203 rb.Push(stream->Flush()); 193 rb.Push(flushed);
204 } 194 }
205 195
206 void SetAudioOutVolume(Kernel::HLERequestContext& ctx) { 196 void SetAudioOutVolume(Kernel::HLERequestContext& ctx) {
207 IPC::RequestParser rp{ctx}; 197 IPC::RequestParser rp{ctx};
208 const float volume = rp.Pop<float>(); 198 const auto volume = rp.Pop<f32>();
209 LOG_DEBUG(Service_Audio, "called, volume={}", volume); 199
200 LOG_DEBUG(Service_Audio, "called. Volume={}", volume);
210 201
211 stream->SetVolume(volume); 202 impl->SetVolume(volume);
212 203
213 IPC::ResponseBuilder rb{ctx, 2}; 204 IPC::ResponseBuilder rb{ctx, 2};
214 rb.Push(ResultSuccess); 205 rb.Push(ResultSuccess);
215 } 206 }
216 207
217 void GetAudioOutVolume(Kernel::HLERequestContext& ctx) { 208 void GetAudioOutVolume(Kernel::HLERequestContext& ctx) {
218 LOG_DEBUG(Service_Audio, "called"); 209 const auto volume = impl->GetVolume();
210
211 LOG_DEBUG(Service_Audio, "called. Volume={}", volume);
219 212
220 IPC::ResponseBuilder rb{ctx, 3}; 213 IPC::ResponseBuilder rb{ctx, 3};
221 rb.Push(ResultSuccess); 214 rb.Push(ResultSuccess);
222 rb.Push(stream->GetVolume()); 215 rb.Push(volume);
223 } 216 }
224 217
225 AudioCore::AudioOut& audio_core;
226 AudioCore::StreamPtr stream;
227 std::string device_name;
228
229 [[maybe_unused]] AudoutParams audio_params{};
230
231 Core::Memory::Memory& main_memory;
232
233 KernelHelpers::ServiceContext service_context; 218 KernelHelpers::ServiceContext service_context;
234 219 Kernel::KEvent* event;
235 /// This is the event handle used to check if the audio buffer was released 220 std::shared_ptr<AudioCore::AudioOut::Out> impl;
236 Kernel::KEvent* buffer_event;
237}; 221};
238 222
239AudOutU::AudOutU(Core::System& system_) : ServiceFramework{system_, "audout:u"} { 223AudOutU::AudOutU(Core::System& system_)
224 : ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew},
225 service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>(
226 system_)} {
240 // clang-format off 227 // clang-format off
241 static const FunctionInfo functions[] = { 228 static const FunctionInfo functions[] = {
242 {0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"}, 229 {0, &AudOutU::ListAudioOuts, "ListAudioOuts"},
243 {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"}, 230 {1, &AudOutU::OpenAudioOut, "OpenAudioOut"},
244 {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"}, 231 {2, &AudOutU::ListAudioOuts, "ListAudioOutsAuto"},
245 {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}, 232 {3, &AudOutU::OpenAudioOut, "OpenAudioOutAuto"},
246 }; 233 };
247 // clang-format on 234 // clang-format on
248 235
249 RegisterHandlers(functions); 236 RegisterHandlers(functions);
250 audio_core = std::make_unique<AudioCore::AudioOut>();
251} 237}
252 238
253AudOutU::~AudOutU() = default; 239AudOutU::~AudOutU() = default;
254 240
255void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { 241void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
256 LOG_DEBUG(Service_Audio, "called"); 242 using namespace AudioCore::AudioRenderer;
257 243
258 ctx.WriteBuffer(DefaultDevice); 244 std::scoped_lock l{impl->mutex};
245
246 const auto write_count =
247 static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
248 std::vector<AudioDevice::AudioDeviceName> device_names{};
249 std::string print_names{};
250 if (write_count > 0) {
251 device_names.push_back(AudioDevice::AudioDeviceName("DeviceOut"));
252 LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut");
253 } else {
254 LOG_DEBUG(Service_Audio, "called. Empty buffer passed in.");
255 }
256
257 ctx.WriteBuffer(device_names);
259 258
260 IPC::ResponseBuilder rb{ctx, 3}; 259 IPC::ResponseBuilder rb{ctx, 3};
261 rb.Push(ResultSuccess); 260 rb.Push(ResultSuccess);
262 rb.Push<u32>(1); // Amount of audio devices 261 rb.Push<u32>(static_cast<u32>(device_names.size()));
263} 262}
264 263
265void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { 264void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
266 LOG_DEBUG(Service_Audio, "called"); 265 IPC::RequestParser rp{ctx};
267 266 auto in_params{rp.PopRaw<AudioOutParameter>()};
267 auto applet_resource_user_id{rp.PopRaw<u64>()};
268 const auto device_name_data{ctx.ReadBuffer()}; 268 const auto device_name_data{ctx.ReadBuffer()};
269 std::string device_name; 269 auto device_name = Common::StringFromBuffer(device_name_data);
270 if (device_name_data[0] != '\0') { 270 auto handle{ctx.GetCopyHandle(0)};
271 device_name.assign(device_name_data.begin(), device_name_data.end());
272 } else {
273 device_name.assign(DefaultDevice.begin(), DefaultDevice.end());
274 }
275 ctx.WriteBuffer(device_name);
276 271
277 IPC::RequestParser rp{ctx}; 272 auto link{impl->LinkToManager()};
278 auto params{rp.PopRaw<AudoutParams>()}; 273 if (link.IsError()) {
279 if (params.channel_count <= 2) { 274 LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager");
280 // Mono does not exist for audout 275 IPC::ResponseBuilder rb{ctx, 2};
281 params.channel_count = 2; 276 rb.Push(link);
282 } else { 277 return;
283 params.channel_count = 6;
284 } 278 }
285 if (!params.sample_rate) { 279
286 params.sample_rate = DefaultSampleRate; 280 size_t new_session_id{};
281 auto result{impl->AcquireSessionId(new_session_id)};
282 if (result.IsError()) {
283 IPC::ResponseBuilder rb{ctx, 2};
284 rb.Push(result);
285 return;
287 } 286 }
288 287
289 std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())}; 288 LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
290 auto audio_out_interface = std::make_shared<IAudioOut>( 289 impl->num_free_sessions);
291 system, params, *audio_core, std::move(device_name), std::move(unique_name)); 290
291 auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name,
292 in_params, handle, applet_resource_user_id);
293
294 impl->sessions[new_session_id] = audio_out->GetImpl();
295 impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
296
297 auto& out_system = impl->sessions[new_session_id]->GetSystem();
298 AudioOutParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
299 .channel_count = out_system.GetChannelCount(),
300 .sample_format =
301 static_cast<u32>(out_system.GetSampleFormat()),
302 .state = static_cast<u32>(out_system.GetState())};
292 303
293 IPC::ResponseBuilder rb{ctx, 6, 0, 1}; 304 IPC::ResponseBuilder rb{ctx, 6, 0, 1};
294 rb.Push(ResultSuccess);
295 rb.Push<u32>(DefaultSampleRate);
296 rb.Push<u32>(params.channel_count);
297 rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
298 rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
299 rb.PushIpcInterface<IAudioOut>(audio_out_interface);
300 305
301 audio_out_interfaces.push_back(std::move(audio_out_interface)); 306 ctx.WriteBuffer(out_system.GetName());
307
308 rb.Push(ResultSuccess);
309 rb.PushRaw<AudioOutParameterInternal>(out_params);
310 rb.PushIpcInterface<IAudioOut>(audio_out);
302} 311}
303 312
304} // namespace Service::Audio 313} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index d82004c2e..fdc0ee754 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -3,13 +3,11 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <vector> 6#include "audio_core/audio_out_manager.h"
7#include "audio_core/out/audio_out.h"
8#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
8 10
9namespace AudioCore {
10class AudioOut;
11}
12
13namespace Core { 11namespace Core {
14class System; 12class System;
15} 13}
@@ -18,6 +16,11 @@ namespace Kernel {
18class HLERequestContext; 16class HLERequestContext;
19} 17}
20 18
19namespace AudioCore::AudioOut {
20class Manager;
21class Out;
22} // namespace AudioCore::AudioOut
23
21namespace Service::Audio { 24namespace Service::Audio {
22 25
23class IAudioOut; 26class IAudioOut;
@@ -28,11 +31,11 @@ public:
28 ~AudOutU() override; 31 ~AudOutU() override;
29 32
30private: 33private:
31 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); 34 void ListAudioOuts(Kernel::HLERequestContext& ctx);
32 void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); 35 void OpenAudioOut(Kernel::HLERequestContext& ctx);
33 36
34 std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces; 37 KernelHelpers::ServiceContext service_context;
35 std::unique_ptr<AudioCore::AudioOut> audio_core; 38 std::unique_ptr<AudioCore::AudioOut::Manager> impl;
36}; 39};
37 40
38} // namespace Service::Audio 41} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 2ce63c004..381a66ba5 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -4,7 +4,12 @@
4#include <array> 4#include <array>
5#include <memory> 5#include <memory>
6 6
7#include "audio_core/audio_renderer.h" 7#include "audio_core/audio_core.h"
8#include "audio_core/common/audio_renderer_parameter.h"
9#include "audio_core/common/feature_support.h"
10#include "audio_core/renderer/audio_device.h"
11#include "audio_core/renderer/audio_renderer.h"
12#include "audio_core/renderer/voice/voice_info.h"
8#include "common/alignment.h" 13#include "common/alignment.h"
9#include "common/bit_util.h" 14#include "common/bit_util.h"
10#include "common/common_funcs.h" 15#include "common/common_funcs.h"
@@ -13,91 +18,112 @@
13#include "core/core.h" 18#include "core/core.h"
14#include "core/hle/ipc_helpers.h" 19#include "core/hle/ipc_helpers.h"
15#include "core/hle/kernel/k_event.h" 20#include "core/hle/kernel/k_event.h"
21#include "core/hle/kernel/k_process.h"
22#include "core/hle/kernel/k_transfer_memory.h"
16#include "core/hle/service/audio/audren_u.h" 23#include "core/hle/service/audio/audren_u.h"
17#include "core/hle/service/audio/errors.h" 24#include "core/hle/service/audio/errors.h"
25#include "core/memory.h"
26
27using namespace AudioCore::AudioRenderer;
18 28
19namespace Service::Audio { 29namespace Service::Audio {
20 30
21class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { 31class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
22public: 32public:
23 explicit IAudioRenderer(Core::System& system_, 33 explicit IAudioRenderer(Core::System& system_, Manager& manager_,
24 const AudioCommon::AudioRendererParameter& audren_params, 34 AudioCore::AudioRendererParameterInternal& params,
25 const std::size_t instance_number) 35 Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
36 u32 process_handle, u64 applet_resource_user_id, s32 session_id)
26 : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew}, 37 : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew},
27 service_context{system_, "IAudioRenderer"} { 38 service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent(
39 "IAudioRendererEvent")},
40 manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} {
28 // clang-format off 41 // clang-format off
29 static const FunctionInfo functions[] = { 42 static const FunctionInfo functions[] = {
30 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, 43 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
31 {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"}, 44 {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
32 {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"}, 45 {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
33 {3, &IAudioRenderer::GetState, "GetState"}, 46 {3, &IAudioRenderer::GetState, "GetState"},
34 {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"}, 47 {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
35 {5, &IAudioRenderer::Start, "Start"}, 48 {5, &IAudioRenderer::Start, "Start"},
36 {6, &IAudioRenderer::Stop, "Stop"}, 49 {6, &IAudioRenderer::Stop, "Stop"},
37 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, 50 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
38 {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"}, 51 {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
39 {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, 52 {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
40 {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"}, 53 {10, nullptr, "RequestUpdateAuto"},
41 {11, &IAudioRenderer::ExecuteAudioRendererRendering, "ExecuteAudioRendererRendering"}, 54 {11, nullptr, "ExecuteAudioRendererRendering"},
42 }; 55 };
43 // clang-format on 56 // clang-format on
44 RegisterHandlers(functions); 57 RegisterHandlers(functions);
45 58
46 system_event = service_context.CreateEvent("IAudioRenderer:SystemEvent"); 59 impl->Initialize(params, transfer_memory, transfer_memory_size, process_handle,
47 renderer = std::make_unique<AudioCore::AudioRenderer>( 60 applet_resource_user_id, session_id);
48 system.CoreTiming(), system.Memory(), audren_params,
49 [this]() {
50 const auto guard = LockService();
51 system_event->GetWritableEvent().Signal();
52 },
53 instance_number);
54 } 61 }
55 62
56 ~IAudioRenderer() override { 63 ~IAudioRenderer() override {
57 service_context.CloseEvent(system_event); 64 impl->Finalize();
65 service_context.CloseEvent(rendered_event);
58 } 66 }
59 67
60private: 68private:
61 void GetSampleRate(Kernel::HLERequestContext& ctx) { 69 void GetSampleRate(Kernel::HLERequestContext& ctx) {
62 LOG_DEBUG(Service_Audio, "called"); 70 const auto sample_rate{impl->GetSystem().GetSampleRate()};
71
72 LOG_DEBUG(Service_Audio, "called. Sample rate {}", sample_rate);
63 73
64 IPC::ResponseBuilder rb{ctx, 3}; 74 IPC::ResponseBuilder rb{ctx, 3};
65 rb.Push(ResultSuccess); 75 rb.Push(ResultSuccess);
66 rb.Push<u32>(renderer->GetSampleRate()); 76 rb.Push(sample_rate);
67 } 77 }
68 78
69 void GetSampleCount(Kernel::HLERequestContext& ctx) { 79 void GetSampleCount(Kernel::HLERequestContext& ctx) {
70 LOG_DEBUG(Service_Audio, "called"); 80 const auto sample_count{impl->GetSystem().GetSampleCount()};
81
82 LOG_DEBUG(Service_Audio, "called. Sample count {}", sample_count);
71 83
72 IPC::ResponseBuilder rb{ctx, 3}; 84 IPC::ResponseBuilder rb{ctx, 3};
73 rb.Push(ResultSuccess); 85 rb.Push(ResultSuccess);
74 rb.Push<u32>(renderer->GetSampleCount()); 86 rb.Push(sample_count);
75 } 87 }
76 88
77 void GetState(Kernel::HLERequestContext& ctx) { 89 void GetState(Kernel::HLERequestContext& ctx) {
78 LOG_DEBUG(Service_Audio, "called"); 90 const u32 state{!impl->GetSystem().IsActive()};
91
92 LOG_DEBUG(Service_Audio, "called, state {}", state);
79 93
80 IPC::ResponseBuilder rb{ctx, 3}; 94 IPC::ResponseBuilder rb{ctx, 3};
81 rb.Push(ResultSuccess); 95 rb.Push(ResultSuccess);
82 rb.Push<u32>(static_cast<u32>(renderer->GetStreamState())); 96 rb.Push(state);
83 } 97 }
84 98
85 void GetMixBufferCount(Kernel::HLERequestContext& ctx) { 99 void GetMixBufferCount(Kernel::HLERequestContext& ctx) {
86 LOG_DEBUG(Service_Audio, "called"); 100 LOG_DEBUG(Service_Audio, "called");
87 101
102 const auto buffer_count{impl->GetSystem().GetMixBufferCount()};
103
88 IPC::ResponseBuilder rb{ctx, 3}; 104 IPC::ResponseBuilder rb{ctx, 3};
89 rb.Push(ResultSuccess); 105 rb.Push(ResultSuccess);
90 rb.Push<u32>(renderer->GetMixBufferCount()); 106 rb.Push(buffer_count);
91 } 107 }
92 108
93 void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { 109 void RequestUpdate(Kernel::HLERequestContext& ctx) {
94 LOG_DEBUG(Service_Audio, "(STUBBED) called"); 110 LOG_TRACE(Service_Audio, "called");
95 111
96 std::vector<u8> output_params(ctx.GetWriteBufferSize(), 0); 112 std::vector<u8> input{ctx.ReadBuffer(0)};
97 auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params); 113
114 // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for
115 // checking size 0. Performance size is 0 for most games.
116 const auto buffers{ctx.BufferDescriptorB()};
117 std::vector<u8> output(buffers[0].Size(), 0);
118 std::vector<u8> performance(buffers[1].Size(), 0);
119
120 auto result = impl->RequestUpdate(input, performance, output);
98 121
99 if (result.IsSuccess()) { 122 if (result.IsSuccess()) {
100 ctx.WriteBuffer(output_params); 123 ctx.WriteBufferB(output.data(), output.size(), 0);
124 ctx.WriteBufferB(performance.data(), performance.size(), 1);
125 } else {
126 LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description);
101 } 127 }
102 128
103 IPC::ResponseBuilder rb{ctx, 2}; 129 IPC::ResponseBuilder rb{ctx, 2};
@@ -105,38 +131,45 @@ private:
105 } 131 }
106 132
107 void Start(Kernel::HLERequestContext& ctx) { 133 void Start(Kernel::HLERequestContext& ctx) {
108 LOG_WARNING(Service_Audio, "(STUBBED) called"); 134 LOG_DEBUG(Service_Audio, "called");
109 135
110 const auto result = renderer->Start(); 136 impl->Start();
111 137
112 IPC::ResponseBuilder rb{ctx, 2}; 138 IPC::ResponseBuilder rb{ctx, 2};
113 rb.Push(result); 139 rb.Push(ResultSuccess);
114 } 140 }
115 141
116 void Stop(Kernel::HLERequestContext& ctx) { 142 void Stop(Kernel::HLERequestContext& ctx) {
117 LOG_WARNING(Service_Audio, "(STUBBED) called"); 143 LOG_DEBUG(Service_Audio, "called");
118 144
119 const auto result = renderer->Stop(); 145 impl->Stop();
120 146
121 IPC::ResponseBuilder rb{ctx, 2}; 147 IPC::ResponseBuilder rb{ctx, 2};
122 rb.Push(result); 148 rb.Push(ResultSuccess);
123 } 149 }
124 150
125 void QuerySystemEvent(Kernel::HLERequestContext& ctx) { 151 void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
126 LOG_WARNING(Service_Audio, "(STUBBED) called"); 152 LOG_DEBUG(Service_Audio, "called");
153
154 if (impl->GetSystem().GetExecutionMode() == AudioCore::ExecutionMode::Manual) {
155 IPC::ResponseBuilder rb{ctx, 2};
156 rb.Push(ERR_NOT_SUPPORTED);
157 return;
158 }
127 159
128 IPC::ResponseBuilder rb{ctx, 2, 1}; 160 IPC::ResponseBuilder rb{ctx, 2, 1};
129 rb.Push(ResultSuccess); 161 rb.Push(ResultSuccess);
130 rb.PushCopyObjects(system_event->GetReadableEvent()); 162 rb.PushCopyObjects(rendered_event->GetReadableEvent());
131 } 163 }
132 164
133 void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { 165 void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
166 LOG_DEBUG(Service_Audio, "called");
167
134 IPC::RequestParser rp{ctx}; 168 IPC::RequestParser rp{ctx};
135 rendering_time_limit_percent = rp.Pop<u32>(); 169 auto limit = rp.PopRaw<u32>();
136 LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}",
137 rendering_time_limit_percent);
138 170
139 ASSERT(rendering_time_limit_percent <= 100); 171 auto& system_ = impl->GetSystem();
172 system_.SetRenderingTimeLimit(limit);
140 173
141 IPC::ResponseBuilder rb{ctx, 2}; 174 IPC::ResponseBuilder rb{ctx, 2};
142 rb.Push(ResultSuccess); 175 rb.Push(ResultSuccess);
@@ -145,34 +178,34 @@ private:
145 void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { 178 void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
146 LOG_DEBUG(Service_Audio, "called"); 179 LOG_DEBUG(Service_Audio, "called");
147 180
181 auto& system_ = impl->GetSystem();
182 auto time = system_.GetRenderingTimeLimit();
183
148 IPC::ResponseBuilder rb{ctx, 3}; 184 IPC::ResponseBuilder rb{ctx, 3};
149 rb.Push(ResultSuccess); 185 rb.Push(ResultSuccess);
150 rb.Push(rendering_time_limit_percent); 186 rb.Push(time);
151 } 187 }
152 188
153 void ExecuteAudioRendererRendering(Kernel::HLERequestContext& ctx) { 189 void ExecuteAudioRendererRendering(Kernel::HLERequestContext& ctx) {
154 LOG_DEBUG(Service_Audio, "called"); 190 LOG_DEBUG(Service_Audio, "called");
155
156 // This service command currently only reports an unsupported operation
157 // error code, or aborts. Given that, we just always return an error
158 // code in this case.
159
160 IPC::ResponseBuilder rb{ctx, 2};
161 rb.Push(ERR_NOT_SUPPORTED);
162 } 191 }
163 192
164 KernelHelpers::ServiceContext service_context; 193 KernelHelpers::ServiceContext service_context;
165 194 Kernel::KEvent* rendered_event;
166 Kernel::KEvent* system_event; 195 Manager& manager;
167 std::unique_ptr<AudioCore::AudioRenderer> renderer; 196 std::unique_ptr<Renderer> impl;
168 u32 rendering_time_limit_percent = 100;
169}; 197};
170 198
171class IAudioDevice final : public ServiceFramework<IAudioDevice> { 199class IAudioDevice final : public ServiceFramework<IAudioDevice> {
200
172public: 201public:
173 explicit IAudioDevice(Core::System& system_, Kernel::KEvent* buffer_event_, u32_le revision_) 202 explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision,
174 : ServiceFramework{system_, "IAudioDevice"}, buffer_event{buffer_event_}, revision{ 203 u32 device_num)
175 revision_} { 204 : ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew},
205 service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>(
206 system_, applet_resource_user_id,
207 revision)},
208 event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} {
176 static const FunctionInfo functions[] = { 209 static const FunctionInfo functions[] = {
177 {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, 210 {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
178 {1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"}, 211 {1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
@@ -186,54 +219,45 @@ public:
186 {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"}, 219 {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
187 {11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"}, 220 {11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"},
188 {12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"}, 221 {12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"},
189 {13, nullptr, "GetActiveAudioOutputDeviceName"}, 222 {13, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioOutputDeviceName"},
190 {14, nullptr, "ListAudioOutputDeviceName"}, 223 {14, &IAudioDevice::ListAudioOutputDeviceName, "ListAudioOutputDeviceName"},
191 }; 224 };
192 RegisterHandlers(functions); 225 RegisterHandlers(functions);
226
227 event->GetWritableEvent().Signal();
193 } 228 }
194 229
195private: 230 ~IAudioDevice() override {
196 using AudioDeviceName = std::array<char, 256>; 231 service_context.CloseEvent(event);
197 static constexpr std::array<std::string_view, 4> audio_device_names{{ 232 }
198 "AudioStereoJackOutput",
199 "AudioBuiltInSpeakerOutput",
200 "AudioTvOutput",
201 "AudioUsbDeviceOutput",
202 }};
203 enum class DeviceType {
204 AHUBHeadphones,
205 AHUBSpeakers,
206 HDA,
207 USBOutput,
208 };
209 233
234private:
210 void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { 235 void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
211 LOG_DEBUG(Service_Audio, "called"); 236 const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName);
212 237
213 const bool usb_output_supported = 238 std::vector<AudioDevice::AudioDeviceName> out_names{};
214 IsFeatureSupported(AudioFeatures::AudioUSBDeviceOutput, revision);
215 const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioDeviceName);
216 239
217 std::vector<AudioDeviceName> name_buffer; 240 u32 out_count = impl->ListAudioDeviceName(out_names, in_count);
218 name_buffer.reserve(audio_device_names.size());
219 241
220 for (std::size_t i = 0; i < count && i < audio_device_names.size(); i++) { 242 std::string out{};
221 const auto type = static_cast<DeviceType>(i); 243 for (u32 i = 0; i < out_count; i++) {
222 244 std::string a{};
223 if (!usb_output_supported && type == DeviceType::USBOutput) { 245 u32 j = 0;
224 continue; 246 while (out_names[i].name[j] != '\0') {
247 a += out_names[i].name[j];
248 j++;
225 } 249 }
226 250 out += "\n\t" + a;
227 const auto& device_name = audio_device_names[i];
228 auto& entry = name_buffer.emplace_back();
229 device_name.copy(entry.data(), device_name.size());
230 } 251 }
231 252
232 ctx.WriteBuffer(name_buffer); 253 LOG_DEBUG(Service_Audio, "called.\nNames={}", out);
233 254
234 IPC::ResponseBuilder rb{ctx, 3}; 255 IPC::ResponseBuilder rb{ctx, 3};
256
257 ctx.WriteBuffer(out_names);
258
235 rb.Push(ResultSuccess); 259 rb.Push(ResultSuccess);
236 rb.Push(static_cast<u32>(name_buffer.size())); 260 rb.Push(out_count);
237 } 261 }
238 262
239 void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { 263 void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
@@ -243,7 +267,11 @@ private:
243 const auto device_name_buffer = ctx.ReadBuffer(); 267 const auto device_name_buffer = ctx.ReadBuffer();
244 const std::string name = Common::StringFromBuffer(device_name_buffer); 268 const std::string name = Common::StringFromBuffer(device_name_buffer);
245 269
246 LOG_WARNING(Service_Audio, "(STUBBED) called. name={}, volume={}", name, volume); 270 LOG_DEBUG(Service_Audio, "called. name={}, volume={}", name, volume);
271
272 if (name == "AudioTvOutput") {
273 impl->SetDeviceVolumes(volume);
274 }
247 275
248 IPC::ResponseBuilder rb{ctx, 2}; 276 IPC::ResponseBuilder rb{ctx, 2};
249 rb.Push(ResultSuccess); 277 rb.Push(ResultSuccess);
@@ -253,53 +281,60 @@ private:
253 const auto device_name_buffer = ctx.ReadBuffer(); 281 const auto device_name_buffer = ctx.ReadBuffer();
254 const std::string name = Common::StringFromBuffer(device_name_buffer); 282 const std::string name = Common::StringFromBuffer(device_name_buffer);
255 283
256 LOG_WARNING(Service_Audio, "(STUBBED) called. name={}", name); 284 LOG_DEBUG(Service_Audio, "called. Name={}", name);
285
286 f32 volume{1.0f};
287 if (name == "AudioTvOutput") {
288 volume = impl->GetDeviceVolume(name);
289 }
257 290
258 IPC::ResponseBuilder rb{ctx, 3}; 291 IPC::ResponseBuilder rb{ctx, 3};
259 rb.Push(ResultSuccess); 292 rb.Push(ResultSuccess);
260 rb.Push(1.0f); 293 rb.Push(volume);
261 } 294 }
262 295
263 void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) { 296 void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
264 LOG_WARNING(Service_Audio, "(STUBBED) called"); 297 const auto write_size = ctx.GetWriteBufferSize() / sizeof(char);
298 std::string out_name{"AudioTvOutput"};
265 299
266 // Currently set to always be TV audio output. 300 LOG_DEBUG(Service_Audio, "(STUBBED) called. Name={}", out_name);
267 const auto& device_name = audio_device_names[2];
268 301
269 AudioDeviceName out_device_name{}; 302 out_name.resize(write_size);
270 device_name.copy(out_device_name.data(), device_name.size());
271 303
272 ctx.WriteBuffer(out_device_name); 304 ctx.WriteBuffer(out_name);
273 305
274 IPC::ResponseBuilder rb{ctx, 2}; 306 IPC::ResponseBuilder rb{ctx, 2};
275 rb.Push(ResultSuccess); 307 rb.Push(ResultSuccess);
276 } 308 }
277 309
278 void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { 310 void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
279 LOG_WARNING(Service_Audio, "(STUBBED) called"); 311 LOG_DEBUG(Service_Audio, "(STUBBED) called");
280 312
281 buffer_event->GetWritableEvent().Signal(); 313 event->GetWritableEvent().Signal();
282 314
283 IPC::ResponseBuilder rb{ctx, 2, 1}; 315 IPC::ResponseBuilder rb{ctx, 2, 1};
284 rb.Push(ResultSuccess); 316 rb.Push(ResultSuccess);
285 rb.PushCopyObjects(buffer_event->GetReadableEvent()); 317 rb.PushCopyObjects(event->GetReadableEvent());
286 } 318 }
287 319
288 void GetActiveChannelCount(Kernel::HLERequestContext& ctx) { 320 void GetActiveChannelCount(Kernel::HLERequestContext& ctx) {
289 LOG_WARNING(Service_Audio, "(STUBBED) called"); 321 const auto& sink{system.AudioCore().GetOutputSink()};
322 u32 channel_count{sink.GetDeviceChannels()};
323
324 LOG_DEBUG(Service_Audio, "(STUBBED) called. Channels={}", channel_count);
290 325
291 IPC::ResponseBuilder rb{ctx, 3}; 326 IPC::ResponseBuilder rb{ctx, 3};
327
292 rb.Push(ResultSuccess); 328 rb.Push(ResultSuccess);
293 rb.Push<u32>(2); 329 rb.Push<u32>(channel_count);
294 } 330 }
295 331
296 // Should be similar to QueryAudioDeviceOutputEvent
297 void QueryAudioDeviceInputEvent(Kernel::HLERequestContext& ctx) { 332 void QueryAudioDeviceInputEvent(Kernel::HLERequestContext& ctx) {
298 LOG_WARNING(Service_Audio, "(STUBBED) called"); 333 LOG_DEBUG(Service_Audio, "(STUBBED) called");
299 334
300 IPC::ResponseBuilder rb{ctx, 2, 1}; 335 IPC::ResponseBuilder rb{ctx, 2, 1};
301 rb.Push(ResultSuccess); 336 rb.Push(ResultSuccess);
302 rb.PushCopyObjects(buffer_event->GetReadableEvent()); 337 rb.PushCopyObjects(event->GetReadableEvent());
303 } 338 }
304 339
305 void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) { 340 void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) {
@@ -307,402 +342,167 @@ private:
307 342
308 IPC::ResponseBuilder rb{ctx, 2, 1}; 343 IPC::ResponseBuilder rb{ctx, 2, 1};
309 rb.Push(ResultSuccess); 344 rb.Push(ResultSuccess);
310 rb.PushCopyObjects(buffer_event->GetReadableEvent()); 345 rb.PushCopyObjects(event->GetReadableEvent());
311 } 346 }
312 347
313 Kernel::KEvent* buffer_event; 348 void ListAudioOutputDeviceName(Kernel::HLERequestContext& ctx) {
314 u32_le revision = 0; 349 const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName);
350
351 std::vector<AudioDevice::AudioDeviceName> out_names{};
352
353 u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count);
354
355 std::string out{};
356 for (u32 i = 0; i < out_count; i++) {
357 std::string a{};
358 u32 j = 0;
359 while (out_names[i].name[j] != '\0') {
360 a += out_names[i].name[j];
361 j++;
362 }
363 out += "\n\t" + a;
364 }
365
366 LOG_DEBUG(Service_Audio, "called.\nNames={}", out);
367
368 IPC::ResponseBuilder rb{ctx, 3};
369
370 ctx.WriteBuffer(out_names);
371
372 rb.Push(ResultSuccess);
373 rb.Push(out_count);
374 }
375
376 KernelHelpers::ServiceContext service_context;
377 std::unique_ptr<AudioDevice> impl;
378 Kernel::KEvent* event;
315}; 379};
316 380
317AudRenU::AudRenU(Core::System& system_) 381AudRenU::AudRenU(Core::System& system_)
318 : ServiceFramework{system_, "audren:u"}, service_context{system_, "audren:u"} { 382 : ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew},
383 service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
319 // clang-format off 384 // clang-format off
320 static const FunctionInfo functions[] = { 385 static const FunctionInfo functions[] = {
321 {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"}, 386 {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
322 {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetWorkBufferSize"}, 387 {1, &AudRenU::GetWorkBufferSize, "GetWorkBufferSize"},
323 {2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"}, 388 {2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"},
324 {3, &AudRenU::OpenAudioRendererForManualExecution, "OpenAudioRendererForManualExecution"}, 389 {3, nullptr, "OpenAudioRendererForManualExecution"},
325 {4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"}, 390 {4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"},
326 }; 391 };
327 // clang-format on 392 // clang-format on
328 393
329 RegisterHandlers(functions); 394 RegisterHandlers(functions);
330
331 buffer_event = service_context.CreateEvent("IAudioOutBufferReleasedEvent");
332} 395}
333 396
334AudRenU::~AudRenU() { 397AudRenU::~AudRenU() = default;
335 service_context.CloseEvent(buffer_event);
336}
337 398
338void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { 399void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
339 LOG_DEBUG(Service_Audio, "called"); 400 IPC::RequestParser rp{ctx};
340
341 OpenAudioRendererImpl(ctx);
342}
343
344static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) {
345 // +1 represents the final mix.
346 return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count +
347 1;
348}
349
350void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
351 LOG_DEBUG(Service_Audio, "called");
352
353 // Several calculations below align the sizes being calculated
354 // onto a 64 byte boundary.
355 static constexpr u64 buffer_alignment_size = 64;
356
357 // Some calculations that calculate portions of the buffer
358 // that will contain information, on the other hand, align
359 // the result of some of their calcularions on a 16 byte boundary.
360 static constexpr u64 info_field_alignment_size = 16;
361
362 // Maximum detail entries that may exist at one time for performance
363 // frame statistics.
364 static constexpr u64 max_perf_detail_entries = 100;
365
366 // Size of the data structure representing the bulk of the voice-related state.
367 static constexpr u64 voice_state_size_bytes = 0x100;
368
369 // Size of the upsampler manager data structure
370 constexpr u64 upsampler_manager_size = 0x48;
371
372 // Calculates the part of the size that relates to mix buffers.
373 const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) {
374 // As of 8.0.0 this is the maximum on voice channels.
375 constexpr u64 max_voice_channels = 6;
376
377 // The service expects the sample_count member of the parameters to either be
378 // a value of 160 or 240, so the maximum sample count is assumed in order
379 // to adequately handle all values at runtime.
380 constexpr u64 default_max_sample_count = 240;
381
382 const u64 total_mix_buffers = params.mix_buffer_count + max_voice_channels;
383
384 u64 size = 0;
385 size += total_mix_buffers * (sizeof(s32) * params.sample_count);
386 size += total_mix_buffers * (sizeof(s32) * default_max_sample_count);
387 size += u64{params.submix_count} + params.sink_count;
388 size = Common::AlignUp(size, buffer_alignment_size);
389 size += Common::AlignUp(params.unknown_30, buffer_alignment_size);
390 size += Common::AlignUp(sizeof(s32) * params.mix_buffer_count, buffer_alignment_size);
391 return size;
392 };
393
394 // Calculates the portion of the size related to the mix data (and the sorting thereof).
395 const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) {
396 // The size of the mixing info data structure.
397 constexpr u64 mix_info_size = 0x940;
398
399 // Consists of total submixes with the final mix included.
400 const u64 total_mix_count = u64{params.submix_count} + 1;
401
402 // The total number of effects that may be available to the audio renderer at any time.
403 constexpr u64 max_effects = 256;
404
405 // Calculates the part of the size related to the audio node state.
406 // This will only be used if the audio revision supports the splitter.
407 const auto calculate_node_state_size = [](std::size_t num_nodes) {
408 // Internally within a nodestate, it appears to use a data structure
409 // similar to a std::bitset<64> twice.
410 constexpr u64 bit_size = Common::BitSize<u64>();
411 constexpr u64 num_bitsets = 2;
412
413 // Node state instances have three states internally for performing
414 // depth-first searches of nodes. Initialized, Found, and Done Sorting.
415 constexpr u64 num_states = 3;
416
417 u64 size = 0;
418 size += (num_nodes * num_nodes) * sizeof(s32);
419 size += num_states * (num_nodes * sizeof(s32));
420 size += num_bitsets * (Common::AlignUp(num_nodes, bit_size) / Common::BitSize<u8>());
421 return size;
422 };
423
424 // Calculates the part of the size related to the adjacency (aka edge) matrix.
425 const auto calculate_edge_matrix_size = [](std::size_t num_nodes) {
426 return (num_nodes * num_nodes) * sizeof(s32);
427 };
428
429 u64 size = 0;
430 size += Common::AlignUp(sizeof(void*) * total_mix_count, info_field_alignment_size);
431 size += Common::AlignUp(mix_info_size * total_mix_count, info_field_alignment_size);
432 size += Common::AlignUp(sizeof(s32) * max_effects * params.submix_count,
433 info_field_alignment_size);
434
435 if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
436 size += Common::AlignUp(calculate_node_state_size(total_mix_count) +
437 calculate_edge_matrix_size(total_mix_count),
438 info_field_alignment_size);
439 }
440
441 return size;
442 };
443
444 // Calculates the part of the size related to voice channel info.
445 const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) {
446 constexpr u64 voice_info_size = 0x220;
447 constexpr u64 voice_resource_size = 0xD0;
448
449 u64 size = 0;
450 size += Common::AlignUp(sizeof(void*) * params.voice_count, info_field_alignment_size);
451 size += Common::AlignUp(voice_info_size * params.voice_count, info_field_alignment_size);
452 size +=
453 Common::AlignUp(voice_resource_size * params.voice_count, info_field_alignment_size);
454 size +=
455 Common::AlignUp(voice_state_size_bytes * params.voice_count, info_field_alignment_size);
456 return size;
457 };
458
459 // Calculates the part of the size related to memory pools.
460 const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) {
461 const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count);
462 const u64 memory_pool_info_size = 0x20;
463 return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size);
464 };
465
466 // Calculates the part of the size related to the splitter context.
467 const auto calculate_splitter_context_size =
468 [](const AudioCommon::AudioRendererParameter& params) -> u64 {
469 if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
470 return 0;
471 }
472
473 constexpr u64 splitter_info_size = 0x20;
474 constexpr u64 splitter_destination_data_size = 0xE0;
475
476 u64 size = 0;
477 size += params.num_splitter_send_channels;
478 size +=
479 Common::AlignUp(splitter_info_size * params.splitter_count, info_field_alignment_size);
480 size += Common::AlignUp(splitter_destination_data_size * params.num_splitter_send_channels,
481 info_field_alignment_size);
482
483 return size;
484 };
485
486 // Calculates the part of the size related to the upsampler info.
487 const auto calculate_upsampler_info_size =
488 [](const AudioCommon::AudioRendererParameter& params) {
489 constexpr u64 upsampler_info_size = 0x280;
490 // Yes, using the buffer size over info alignment size is intentional here.
491 return Common::AlignUp(upsampler_info_size *
492 (u64{params.submix_count} + params.sink_count),
493 buffer_alignment_size);
494 };
495
496 // Calculates the part of the size related to effect info.
497 const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) {
498 constexpr u64 effect_info_size = 0x2B0;
499 return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size);
500 };
501
502 // Calculates the part of the size related to audio sink info.
503 const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) {
504 const u64 sink_info_size = 0x170;
505 return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size);
506 };
507
508 // Calculates the part of the size related to voice state info.
509 const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) {
510 const u64 voice_state_size = 0x100;
511 const u64 additional_size = buffer_alignment_size - 1;
512 return Common::AlignUp(voice_state_size * params.voice_count + additional_size,
513 info_field_alignment_size);
514 };
515
516 // Calculates the part of the size related to performance statistics.
517 const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) {
518 // Extra size value appended to the end of the calculation.
519 constexpr u64 appended = 128;
520
521 // Whether or not we assume the newer version of performance metrics data structures.
522 const bool is_v2 =
523 IsFeatureSupported(AudioFeatures::PerformanceMetricsVersion2, params.revision);
524
525 // Data structure sizes
526 constexpr u64 perf_statistics_size = 0x0C;
527 const u64 header_size = is_v2 ? 0x30 : 0x18;
528 const u64 entry_size = is_v2 ? 0x18 : 0x10;
529 const u64 detail_size = is_v2 ? 0x18 : 0x10;
530
531 const u64 entry_count = CalculateNumPerformanceEntries(params);
532 const u64 size_per_frame =
533 header_size + (entry_size * entry_count) + (detail_size * max_perf_detail_entries);
534
535 u64 size = 0;
536 size += Common::AlignUp(size_per_frame * params.performance_frame_count + 1,
537 buffer_alignment_size);
538 size += Common::AlignUp(perf_statistics_size, buffer_alignment_size);
539 size += appended;
540 return size;
541 };
542
543 // Calculates the part of the size that relates to the audio command buffer.
544 const auto calculate_command_buffer_size =
545 [](const AudioCommon::AudioRendererParameter& params) {
546 constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
547
548 if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
549 constexpr u64 command_buffer_size = 0x18000;
550
551 return command_buffer_size + alignment;
552 }
553
554 // When the variadic command buffer is supported, this means
555 // the command generator for the audio renderer can issue commands
556 // that are (as one would expect), variable in size. So what we need to do
557 // is determine the maximum possible size for a few command data structures
558 // then multiply them by the amount of present commands indicated by the given
559 // respective audio parameters.
560
561 constexpr u64 max_biquad_filters = 2;
562 constexpr u64 max_mix_buffers = 24;
563
564 constexpr u64 biquad_filter_command_size = 0x2C;
565
566 constexpr u64 depop_mix_command_size = 0x24;
567 constexpr u64 depop_setup_command_size = 0x50;
568
569 constexpr u64 effect_command_max_size = 0x540;
570
571 constexpr u64 mix_command_size = 0x1C;
572 constexpr u64 mix_ramp_command_size = 0x24;
573 constexpr u64 mix_ramp_grouped_command_size = 0x13C;
574
575 constexpr u64 perf_command_size = 0x28;
576
577 constexpr u64 sink_command_size = 0x130;
578
579 constexpr u64 submix_command_max_size =
580 depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
581
582 constexpr u64 volume_command_size = 0x1C;
583 constexpr u64 volume_ramp_command_size = 0x20;
584
585 constexpr u64 voice_biquad_filter_command_size =
586 biquad_filter_command_size * max_biquad_filters;
587 constexpr u64 voice_data_command_size = 0x9C;
588 const u64 voice_command_max_size =
589 (params.splitter_count * depop_setup_command_size) +
590 (voice_data_command_size + voice_biquad_filter_command_size +
591 volume_ramp_command_size + mix_ramp_grouped_command_size);
592 401
593 // Now calculate the individual elements that comprise the size and add them together. 402 AudioCore::AudioRendererParameterInternal params;
594 const u64 effect_commands_size = params.effect_count * effect_command_max_size; 403 rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params);
404 auto transfer_memory_handle = ctx.GetCopyHandle(0);
405 auto process_handle = ctx.GetCopyHandle(1);
406 auto transfer_memory_size = rp.Pop<u64>();
407 auto applet_resource_user_id = rp.Pop<u64>();
595 408
596 const u64 final_mix_commands_size = 409 if (impl->GetSessionCount() + 1 > AudioCore::MaxRendererSessions) {
597 depop_mix_command_size + volume_command_size * max_mix_buffers; 410 LOG_ERROR(Service_Audio, "Too many AudioRenderer sessions open!");
411 IPC::ResponseBuilder rb{ctx, 2};
412 rb.Push(ERR_MAXIMUM_SESSIONS_REACHED);
413 return;
414 }
598 415
599 const u64 perf_commands_size = 416 const auto& handle_table{system.CurrentProcess()->GetHandleTable()};
600 perf_command_size * 417 auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
601 (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); 418 auto transfer_memory{
419 process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)};
602 420
603 const u64 sink_commands_size = params.sink_count * sink_command_size; 421 const auto session_id{impl->GetSessionId()};
422 if (session_id == -1) {
423 LOG_ERROR(Service_Audio, "Tried to open a session that's already in use!");
424 IPC::ResponseBuilder rb{ctx, 2};
425 rb.Push(ERR_MAXIMUM_SESSIONS_REACHED);
426 return;
427 }
604 428
605 const u64 splitter_commands_size = 429 LOG_DEBUG(Service_Audio, "Opened new AudioRenderer session {} sessions open {}", session_id,
606 params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; 430 impl->GetSessionCount());
607 431
608 const u64 submix_commands_size = params.submix_count * submix_command_max_size; 432 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
433 rb.Push(ResultSuccess);
434 rb.PushIpcInterface<IAudioRenderer>(system, *impl, params, transfer_memory.GetPointerUnsafe(),
435 transfer_memory_size, process_handle,
436 applet_resource_user_id, session_id);
437}
609 438
610 const u64 voice_commands_size = params.voice_count * voice_command_max_size; 439void AudRenU::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
611 440 AudioCore::AudioRendererParameterInternal params;
612 return effect_commands_size + final_mix_commands_size + perf_commands_size +
613 sink_commands_size + splitter_commands_size + submix_commands_size +
614 voice_commands_size + alignment;
615 };
616 441
617 IPC::RequestParser rp{ctx}; 442 IPC::RequestParser rp{ctx};
618 const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>(); 443 rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params);
619 444
620 u64 size = 0; 445 u64 size{0};
621 size += calculate_mix_buffer_sizes(params); 446 auto result = impl->GetWorkBufferSize(params, size);
622 size += calculate_mix_info_size(params); 447
623 size += calculate_voice_info_size(params); 448 std::string output_info{};
624 size += upsampler_manager_size; 449 output_info += fmt::format("\tRevision {}", AudioCore::GetRevisionNum(params.revision));
625 size += calculate_memory_pools_size(params); 450 output_info +=
626 size += calculate_splitter_context_size(params); 451 fmt::format("\n\tSample Rate {}, Sample Count {}", params.sample_rate, params.sample_count);
627 452 output_info += fmt::format("\n\tExecution Mode {}, Voice Drop Enabled {}",
628 size = Common::AlignUp(size, buffer_alignment_size); 453 static_cast<u32>(params.execution_mode), params.voice_drop_enabled);
629 454 output_info += fmt::format(
630 size += calculate_upsampler_info_size(params); 455 "\n\tSizes: Effects {:04X}, Mixes {:04X}, Sinks {:04X}, Submixes {:04X}, Splitter Infos "
631 size += calculate_effect_info_size(params); 456 "{:04X}, Splitter Destinations {:04X}, Voices {:04X}, Performance Frames {:04X} External "
632 size += calculate_sink_info_size(params); 457 "Context {:04X}",
633 size += calculate_voice_state_size(params); 458 params.effects, params.mixes, params.sinks, params.sub_mixes, params.splitter_infos,
634 size += calculate_perf_size(params); 459 params.splitter_destinations, params.voices, params.perf_frames,
635 size += calculate_command_buffer_size(params); 460 params.external_context_size);
636 461
637 // finally, 4KB page align the size, and we're done. 462 LOG_DEBUG(Service_Audio, "called.\nInput params:\n{}\nOutput params:\n\tWorkbuffer size {:08X}",
638 size = Common::AlignUp(size, 4096); 463 output_info, size);
639 464
640 IPC::ResponseBuilder rb{ctx, 4}; 465 IPC::ResponseBuilder rb{ctx, 4};
641 rb.Push(ResultSuccess); 466 rb.Push(result);
642 rb.Push<u64>(size); 467 rb.Push<u64>(size);
643
644 LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", size);
645} 468}
646 469
647void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) { 470void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
648 IPC::RequestParser rp{ctx}; 471 IPC::RequestParser rp{ctx};
649 const u64 aruid = rp.Pop<u64>();
650 472
651 LOG_DEBUG(Service_Audio, "called. aruid={:016X}", aruid); 473 const auto applet_resource_user_id = rp.Pop<u64>();
474
475 LOG_DEBUG(Service_Audio, "called. Applet resource id {}", applet_resource_user_id);
652 476
653 // Revisionless variant of GetAudioDeviceServiceWithRevisionInfo that
654 // always assumes the initial release revision (REV1).
655 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 477 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
478
656 rb.Push(ResultSuccess); 479 rb.Push(ResultSuccess);
657 rb.PushIpcInterface<IAudioDevice>(system, buffer_event, Common::MakeMagic('R', 'E', 'V', '1')); 480 rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id,
481 ::Common::MakeMagic('R', 'E', 'V', '1'), num_audio_devices++);
658} 482}
659 483
660void AudRenU::OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx) { 484void AudRenU::OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx) {
661 LOG_DEBUG(Service_Audio, "called"); 485 LOG_DEBUG(Service_Audio, "called");
662
663 OpenAudioRendererImpl(ctx);
664} 486}
665 487
666void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) { 488void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) {
667 struct Parameters { 489 struct Parameters {
668 u32 revision; 490 u32 revision;
669 u64 aruid; 491 u64 applet_resource_user_id;
670 }; 492 };
671 493
672 IPC::RequestParser rp{ctx}; 494 IPC::RequestParser rp{ctx};
673 const auto [revision, aruid] = rp.PopRaw<Parameters>();
674 495
675 LOG_DEBUG(Service_Audio, "called. revision={:08X}, aruid={:016X}", revision, aruid); 496 const auto [revision, applet_resource_user_id] = rp.PopRaw<Parameters>();
676 497
677 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 498 LOG_DEBUG(Service_Audio, "called. Revision {} Applet resource id {}",
678 rb.Push(ResultSuccess); 499 AudioCore::GetRevisionNum(revision), applet_resource_user_id);
679 rb.PushIpcInterface<IAudioDevice>(system, buffer_event, revision);
680}
681 500
682void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
683 IPC::RequestParser rp{ctx};
684 const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
685 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 501 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
686 502
687 rb.Push(ResultSuccess); 503 rb.Push(ResultSuccess);
688 rb.PushIpcInterface<IAudioRenderer>(system, params, audren_instance_count++); 504 rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id, revision,
689} 505 num_audio_devices++);
690
691bool IsFeatureSupported(AudioFeatures feature, u32_le revision) {
692 // Byte swap
693 const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0');
694
695 switch (feature) {
696 case AudioFeatures::AudioUSBDeviceOutput:
697 return version_num >= 4U;
698 case AudioFeatures::Splitter:
699 return version_num >= 2U;
700 case AudioFeatures::PerformanceMetricsVersion2:
701 case AudioFeatures::VariadicCommandBuffer:
702 return version_num >= 5U;
703 default:
704 return false;
705 }
706} 506}
707 507
708} // namespace Service::Audio 508} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index 869d39002..4384a9b3c 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "audio_core/audio_render_manager.h"
6#include "core/hle/service/kernel_helpers.h" 7#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
@@ -15,6 +16,7 @@ class HLERequestContext;
15} 16}
16 17
17namespace Service::Audio { 18namespace Service::Audio {
19class IAudioRenderer;
18 20
19class AudRenU final : public ServiceFramework<AudRenU> { 21class AudRenU final : public ServiceFramework<AudRenU> {
20public: 22public:
@@ -23,28 +25,14 @@ public:
23 25
24private: 26private:
25 void OpenAudioRenderer(Kernel::HLERequestContext& ctx); 27 void OpenAudioRenderer(Kernel::HLERequestContext& ctx);
26 void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx); 28 void GetWorkBufferSize(Kernel::HLERequestContext& ctx);
27 void GetAudioDeviceService(Kernel::HLERequestContext& ctx); 29 void GetAudioDeviceService(Kernel::HLERequestContext& ctx);
28 void OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx); 30 void OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx);
29 void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx); 31 void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx);
30 32
31 void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx);
32
33 KernelHelpers::ServiceContext service_context; 33 KernelHelpers::ServiceContext service_context;
34 34 std::unique_ptr<AudioCore::AudioRenderer::Manager> impl;
35 std::size_t audren_instance_count = 0; 35 u32 num_audio_devices{0};
36 Kernel::KEvent* buffer_event;
37}; 36};
38 37
39// Describes a particular audio feature that may be supported in a particular revision.
40enum class AudioFeatures : u32 {
41 AudioUSBDeviceOutput,
42 Splitter,
43 PerformanceMetricsVersion2,
44 VariadicCommandBuffer,
45};
46
47// Tests if a particular audio feature is supported with a given audio revision.
48bool IsFeatureSupported(AudioFeatures feature, u32_le revision);
49
50} // namespace Service::Audio 38} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h
index ac6c514af..d706978cb 100644
--- a/src/core/hle/service/audio/errors.h
+++ b/src/core/hle/service/audio/errors.h
@@ -7,8 +7,17 @@
7 7
8namespace Service::Audio { 8namespace Service::Audio {
9 9
10constexpr Result ERR_INVALID_DEVICE_NAME{ErrorModule::Audio, 1};
10constexpr Result ERR_OPERATION_FAILED{ErrorModule::Audio, 2}; 11constexpr Result ERR_OPERATION_FAILED{ErrorModule::Audio, 2};
12constexpr Result ERR_INVALID_SAMPLE_RATE{ErrorModule::Audio, 3};
13constexpr Result ERR_INSUFFICIENT_BUFFER_SIZE{ErrorModule::Audio, 4};
14constexpr Result ERR_MAXIMUM_SESSIONS_REACHED{ErrorModule::Audio, 5};
11constexpr Result ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8}; 15constexpr Result ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8};
16constexpr Result ERR_INVALID_CHANNEL_COUNT{ErrorModule::Audio, 10};
17constexpr Result ERR_INVALID_UPDATE_DATA{ErrorModule::Audio, 41};
18constexpr Result ERR_POOL_MAPPING_FAILED{ErrorModule::Audio, 42};
12constexpr Result ERR_NOT_SUPPORTED{ErrorModule::Audio, 513}; 19constexpr Result ERR_NOT_SUPPORTED{ErrorModule::Audio, 513};
20constexpr Result ERR_INVALID_PROCESS_HANDLE{ErrorModule::Audio, 1536};
21constexpr Result ERR_INVALID_REVISION{ErrorModule::Audio, 1537};
13 22
14} // namespace Service::Audio 23} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 75da659e5..4f2ed2d52 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -298,7 +298,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx) {
298 const auto sample_rate = rp.Pop<u32>(); 298 const auto sample_rate = rp.Pop<u32>();
299 const auto channel_count = rp.Pop<u32>(); 299 const auto channel_count = rp.Pop<u32>();
300 300
301 LOG_CRITICAL(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); 301 LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count);
302 302
303 ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || 303 ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
304 sample_rate == 12000 || sample_rate == 8000, 304 sample_rate == 12000 || sample_rate == 8000,
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 584808d50..635449fce 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -511,7 +511,7 @@ struct Memory::Impl {
511 511
512 [[nodiscard]] u8* GetPointerImpl(VAddr vaddr, auto on_unmapped, auto on_rasterizer) const { 512 [[nodiscard]] u8* GetPointerImpl(VAddr vaddr, auto on_unmapped, auto on_rasterizer) const {
513 // AARCH64 masks the upper 16 bit of all memory accesses 513 // AARCH64 masks the upper 16 bit of all memory accesses
514 vaddr &= 0xffffffffffffLL; 514 vaddr &= 0xffffffffffffULL;
515 515
516 if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { 516 if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
517 on_unmapped(); 517 on_unmapped();
@@ -776,6 +776,10 @@ void Memory::CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr s
776 impl->CopyBlock(process, dest_addr, src_addr, size); 776 impl->CopyBlock(process, dest_addr, src_addr, size);
777} 777}
778 778
779void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, const std::size_t size) {
780 impl->ZeroBlock(process, dest_addr, size);
781}
782
779void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { 783void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
780 impl->RasterizerMarkRegionCached(vaddr, size, cached); 784 impl->RasterizerMarkRegionCached(vaddr, size, cached);
781} 785}
diff --git a/src/core/memory.h b/src/core/memory.h
index f22c0a2d8..780c45385 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -437,6 +437,19 @@ public:
437 std::size_t size); 437 std::size_t size);
438 438
439 /** 439 /**
440 * Zeros a range of bytes within the current process' address space at the specified
441 * virtual address.
442 *
443 * @param process The process that will have data zeroed within its address space.
444 * @param dest_addr The destination virtual address to zero the data from.
445 * @param size The size of the range to zero out, in bytes.
446 *
447 * @post The range [dest_addr, size) within the process' address space contains the
448 * value 0.
449 */
450 void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
451
452 /**
440 * Marks each page within the specified address range as cached or uncached. 453 * Marks each page within the specified address range as cached or uncached.
441 * 454 *
442 * @param vaddr The virtual address indicating the start of the address range. 455 * @param vaddr The virtual address indicating the start of the address range.