Project Andio
This commit is contained in:
parent
6e36f4d230
commit
458da8a948
270 changed files with 33712 additions and 8445 deletions
|
@ -8,6 +8,7 @@
|
|||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
|
@ -140,6 +141,8 @@ struct System::Impl {
|
|||
core_timing.SyncPause(false);
|
||||
is_paused = false;
|
||||
|
||||
audio_core->PauseSinks(false);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -147,6 +150,8 @@ struct System::Impl {
|
|||
std::unique_lock<std::mutex> lk(suspend_guard);
|
||||
status = SystemResultStatus::Success;
|
||||
|
||||
audio_core->PauseSinks(true);
|
||||
|
||||
core_timing.SyncPause(true);
|
||||
kernel.Suspend(true);
|
||||
is_paused = true;
|
||||
|
@ -154,6 +159,11 @@ struct System::Impl {
|
|||
return status;
|
||||
}
|
||||
|
||||
bool IsPaused() const {
|
||||
std::unique_lock lk(suspend_guard);
|
||||
return is_paused;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> StallProcesses() {
|
||||
std::unique_lock<std::mutex> lk(suspend_guard);
|
||||
kernel.Suspend(true);
|
||||
|
@ -214,6 +224,8 @@ struct System::Impl {
|
|||
return SystemResultStatus::ErrorVideoCore;
|
||||
}
|
||||
|
||||
audio_core = std::make_unique<AudioCore::AudioCore>(system);
|
||||
|
||||
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
|
||||
services = std::make_unique<Service::Services>(service_manager, system);
|
||||
interrupt_manager = std::make_unique<Hardware::InterruptManager>(system);
|
||||
|
@ -290,7 +302,7 @@ struct System::Impl {
|
|||
if (Settings::values.gamecard_current_game) {
|
||||
fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath));
|
||||
} else if (!Settings::values.gamecard_path.GetValue().empty()) {
|
||||
const auto gamecard_path = Settings::values.gamecard_path.GetValue();
|
||||
const auto& gamecard_path = Settings::values.gamecard_path.GetValue();
|
||||
fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, gamecard_path));
|
||||
}
|
||||
}
|
||||
|
@ -308,6 +320,8 @@ struct System::Impl {
|
|||
}
|
||||
|
||||
void Shutdown() {
|
||||
SetShuttingDown(true);
|
||||
|
||||
// Log last frame performance stats if game was loded
|
||||
if (perf_stats) {
|
||||
const auto perf_results = GetAndResetPerfStats();
|
||||
|
@ -333,14 +347,15 @@ struct System::Impl {
|
|||
kernel.ShutdownCores();
|
||||
cpu_manager.Shutdown();
|
||||
debugger.reset();
|
||||
kernel.CloseServices();
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
cheat_engine.reset();
|
||||
telemetry_session.reset();
|
||||
cpu_manager.Shutdown();
|
||||
time_manager.Shutdown();
|
||||
core_timing.Shutdown();
|
||||
app_loader.reset();
|
||||
audio_core.reset();
|
||||
gpu_core.reset();
|
||||
perf_stats.reset();
|
||||
kernel.Shutdown();
|
||||
|
@ -350,6 +365,14 @@ struct System::Impl {
|
|||
LOG_DEBUG(Core, "Shutdown OK");
|
||||
}
|
||||
|
||||
bool IsShuttingDown() const {
|
||||
return is_shutting_down;
|
||||
}
|
||||
|
||||
void SetShuttingDown(bool shutting_down) {
|
||||
is_shutting_down = shutting_down;
|
||||
}
|
||||
|
||||
Loader::ResultStatus GetGameName(std::string& out) const {
|
||||
if (app_loader == nullptr)
|
||||
return Loader::ResultStatus::ErrorNotInitialized;
|
||||
|
@ -392,8 +415,9 @@ struct System::Impl {
|
|||
return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());
|
||||
}
|
||||
|
||||
std::mutex suspend_guard;
|
||||
mutable std::mutex suspend_guard;
|
||||
bool is_paused{};
|
||||
std::atomic<bool> is_shutting_down{};
|
||||
|
||||
Timing::CoreTiming core_timing;
|
||||
Kernel::KernelCore kernel;
|
||||
|
@ -407,6 +431,7 @@ struct System::Impl {
|
|||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
|
||||
std::unique_ptr<Core::DeviceMemory> device_memory;
|
||||
std::unique_ptr<AudioCore::AudioCore> audio_core;
|
||||
Core::Memory::Memory memory;
|
||||
Core::HID::HIDCore hid_core;
|
||||
CpuManager cpu_manager;
|
||||
|
@ -479,6 +504,10 @@ SystemResultStatus System::Pause() {
|
|||
return impl->Pause();
|
||||
}
|
||||
|
||||
bool System::IsPaused() const {
|
||||
return impl->IsPaused();
|
||||
}
|
||||
|
||||
void System::InvalidateCpuInstructionCaches() {
|
||||
impl->kernel.InvalidateAllInstructionCaches();
|
||||
}
|
||||
|
@ -491,6 +520,14 @@ void System::Shutdown() {
|
|||
impl->Shutdown();
|
||||
}
|
||||
|
||||
bool System::IsShuttingDown() const {
|
||||
return impl->IsShuttingDown();
|
||||
}
|
||||
|
||||
void System::SetShuttingDown(bool shutting_down) {
|
||||
impl->SetShuttingDown(shutting_down);
|
||||
}
|
||||
|
||||
void System::DetachDebugger() {
|
||||
if (impl->debugger) {
|
||||
impl->debugger->NotifyShutdown();
|
||||
|
@ -640,6 +677,14 @@ const HID::HIDCore& System::HIDCore() const {
|
|||
return impl->hid_core;
|
||||
}
|
||||
|
||||
AudioCore::AudioCore& System::AudioCore() {
|
||||
return *impl->audio_core;
|
||||
}
|
||||
|
||||
const AudioCore::AudioCore& System::AudioCore() const {
|
||||
return *impl->audio_core;
|
||||
}
|
||||
|
||||
Timing::CoreTiming& System::CoreTiming() {
|
||||
return impl->core_timing;
|
||||
}
|
||||
|
|
|
@ -81,6 +81,10 @@ namespace VideoCore {
|
|||
class RendererBase;
|
||||
} // namespace VideoCore
|
||||
|
||||
namespace AudioCore {
|
||||
class AudioCore;
|
||||
} // namespace AudioCore
|
||||
|
||||
namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
}
|
||||
|
@ -148,6 +152,9 @@ public:
|
|||
*/
|
||||
[[nodiscard]] SystemResultStatus Pause();
|
||||
|
||||
/// Check if the core is currently paused.
|
||||
[[nodiscard]] bool IsPaused() const;
|
||||
|
||||
/**
|
||||
* Invalidate the CPU instruction caches
|
||||
* This function should only be used by GDB Stub to support breakpoints, memory updates and
|
||||
|
@ -160,6 +167,12 @@ public:
|
|||
/// Shutdown the emulated system.
|
||||
void Shutdown();
|
||||
|
||||
/// Check if the core is shutting down.
|
||||
[[nodiscard]] bool IsShuttingDown() const;
|
||||
|
||||
/// Set the shutting down state.
|
||||
void SetShuttingDown(bool shutting_down);
|
||||
|
||||
/// Forcibly detach the debugger if it is running.
|
||||
void DetachDebugger();
|
||||
|
||||
|
@ -250,6 +263,12 @@ public:
|
|||
/// Gets an immutable reference to the renderer.
|
||||
[[nodiscard]] const VideoCore::RendererBase& Renderer() const;
|
||||
|
||||
/// Gets a mutable reference to the audio interface
|
||||
[[nodiscard]] AudioCore::AudioCore& AudioCore();
|
||||
|
||||
/// Gets an immutable reference to the audio interface.
|
||||
[[nodiscard]] const AudioCore::AudioCore& AudioCore() const;
|
||||
|
||||
/// Gets the global scheduler
|
||||
[[nodiscard]] Kernel::GlobalSchedulerContext& GlobalSchedulerContext();
|
||||
|
||||
|
|
|
@ -287,18 +287,52 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
|
|||
BufferDescriptorB().size() > buffer_index &&
|
||||
BufferDescriptorB()[buffer_index].Size() >= size,
|
||||
{ return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size);
|
||||
memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
|
||||
WriteBufferB(buffer, size, buffer_index);
|
||||
} else {
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
BufferDescriptorC().size() > buffer_index &&
|
||||
BufferDescriptorC()[buffer_index].Size() >= size,
|
||||
{ return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size);
|
||||
memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
|
||||
WriteBufferC(buffer, size, buffer_index);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
std::size_t HLERequestContext::WriteBufferB(const void* buffer, std::size_t size,
|
||||
std::size_t buffer_index) const {
|
||||
if (buffer_index >= BufferDescriptorB().size() || size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto buffer_size{BufferDescriptorB()[buffer_index].Size()};
|
||||
if (size > buffer_size) {
|
||||
LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
|
||||
buffer_size);
|
||||
size = buffer_size; // TODO(bunnei): This needs to be HW tested
|
||||
}
|
||||
|
||||
memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
std::size_t HLERequestContext::WriteBufferC(const void* buffer, std::size_t size,
|
||||
std::size_t buffer_index) const {
|
||||
if (buffer_index >= BufferDescriptorC().size() || size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto buffer_size{BufferDescriptorC()[buffer_index].Size()};
|
||||
if (size > buffer_size) {
|
||||
LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
|
||||
buffer_size);
|
||||
size = buffer_size; // TODO(bunnei): This needs to be HW tested
|
||||
}
|
||||
|
||||
memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const {
|
||||
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
|
||||
BufferDescriptorA()[buffer_index].Size()};
|
||||
|
|
|
@ -277,6 +277,14 @@ public:
|
|||
std::size_t WriteBuffer(const void* buffer, std::size_t size,
|
||||
std::size_t buffer_index = 0) const;
|
||||
|
||||
/// Helper function to write buffer B
|
||||
std::size_t WriteBufferB(const void* buffer, std::size_t size,
|
||||
std::size_t buffer_index = 0) const;
|
||||
|
||||
/// Helper function to write buffer C
|
||||
std::size_t WriteBufferC(const void* buffer, std::size_t size,
|
||||
std::size_t buffer_index = 0) const;
|
||||
|
||||
/* Helper function to write a buffer using the appropriate buffer descriptor
|
||||
*
|
||||
* @tparam T an arbitrary container that satisfies the
|
||||
|
|
|
@ -95,19 +95,7 @@ struct KernelCore::Impl {
|
|||
|
||||
process_list.clear();
|
||||
|
||||
// Close all open server sessions and ports.
|
||||
std::unordered_set<KAutoObject*> server_objects_;
|
||||
{
|
||||
std::scoped_lock lk(server_objects_lock);
|
||||
server_objects_ = server_objects;
|
||||
server_objects.clear();
|
||||
}
|
||||
for (auto* server_object : server_objects_) {
|
||||
server_object->Close();
|
||||
}
|
||||
|
||||
// Ensures all service threads gracefully shutdown.
|
||||
ClearServiceThreads();
|
||||
CloseServices();
|
||||
|
||||
next_object_id = 0;
|
||||
next_kernel_process_id = KProcess::InitialKIPIDMin;
|
||||
|
@ -191,6 +179,22 @@ struct KernelCore::Impl {
|
|||
global_object_list_container.reset();
|
||||
}
|
||||
|
||||
void CloseServices() {
|
||||
// Close all open server sessions and ports.
|
||||
std::unordered_set<KAutoObject*> server_objects_;
|
||||
{
|
||||
std::scoped_lock lk(server_objects_lock);
|
||||
server_objects_ = server_objects;
|
||||
server_objects.clear();
|
||||
}
|
||||
for (auto* server_object : server_objects_) {
|
||||
server_object->Close();
|
||||
}
|
||||
|
||||
// Ensures all service threads gracefully shutdown.
|
||||
ClearServiceThreads();
|
||||
}
|
||||
|
||||
void InitializePhysicalCores() {
|
||||
exclusive_monitor =
|
||||
Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES);
|
||||
|
@ -813,6 +817,10 @@ void KernelCore::Shutdown() {
|
|||
impl->Shutdown();
|
||||
}
|
||||
|
||||
void KernelCore::CloseServices() {
|
||||
impl->CloseServices();
|
||||
}
|
||||
|
||||
const KResourceLimit* KernelCore::GetSystemResourceLimit() const {
|
||||
return impl->system_resource_limit;
|
||||
}
|
||||
|
|
|
@ -109,6 +109,9 @@ public:
|
|||
/// Clears all resources in use by the kernel instance.
|
||||
void Shutdown();
|
||||
|
||||
/// Close all active services in use by the kernel instance.
|
||||
void CloseServices();
|
||||
|
||||
/// Retrieves a shared pointer to the system resource limit instance.
|
||||
const KResourceLimit* GetSystemResourceLimit() const;
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
|
@ -286,7 +285,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
|
|||
{62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
|
||||
{63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
|
||||
{64, nullptr, "SetInputDetectionSourceSet"},
|
||||
{65, nullptr, "ReportUserIsActive"},
|
||||
{65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"},
|
||||
{66, nullptr, "GetCurrentIlluminance"},
|
||||
{67, nullptr, "IsIlluminanceAvailable"},
|
||||
{68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"},
|
||||
|
@ -518,6 +517,13 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
|
|||
rb.Push<u32>(idle_time_detection_extension);
|
||||
}
|
||||
|
||||
void ISelfController::ReportUserIsActive(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void ISelfController::SetAutoSleepDisabled(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
is_auto_sleep_disabled = rp.Pop<bool>();
|
||||
|
|
|
@ -175,6 +175,7 @@ private:
|
|||
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
|
||||
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
|
||||
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
|
||||
void ReportUserIsActive(Kernel::HLERequestContext& ctx);
|
||||
void SetAutoSleepDisabled(Kernel::HLERequestContext& ctx);
|
||||
void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx);
|
||||
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
|
||||
|
|
|
@ -1,68 +1,211 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/in/audio_in_system.h"
|
||||
#include "audio_core/renderer/audio_device.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/audio/audin_u.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
using namespace AudioCore::AudioIn;
|
||||
|
||||
IAudioIn::IAudioIn(Core::System& system_)
|
||||
: ServiceFramework{system_, "IAudioIn"}, service_context{system_, "IAudioIn"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetAudioInState"},
|
||||
{1, &IAudioIn::Start, "Start"},
|
||||
{2, nullptr, "Stop"},
|
||||
{3, nullptr, "AppendAudioInBuffer"},
|
||||
{4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"},
|
||||
{5, nullptr, "GetReleasedAudioInBuffer"},
|
||||
{6, nullptr, "ContainsAudioInBuffer"},
|
||||
{7, nullptr, "AppendUacInBuffer"},
|
||||
{8, &IAudioIn::AppendAudioInBufferAuto, "AppendAudioInBufferAuto"},
|
||||
{9, nullptr, "GetReleasedAudioInBuffersAuto"},
|
||||
{10, nullptr, "AppendUacInBufferAuto"},
|
||||
{11, nullptr, "GetAudioInBufferCount"},
|
||||
{12, nullptr, "SetDeviceGain"},
|
||||
{13, nullptr, "GetDeviceGain"},
|
||||
{14, nullptr, "FlushAudioInBuffers"},
|
||||
};
|
||||
// clang-format on
|
||||
class IAudioIn final : public ServiceFramework<IAudioIn> {
|
||||
public:
|
||||
explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
|
||||
std::string& device_name, const AudioInParameter& in_params, u32 handle,
|
||||
u64 applet_resource_user_id)
|
||||
: ServiceFramework{system_, "IAudioIn"},
|
||||
service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")},
|
||||
impl{std::make_shared<In>(system_, manager, event, session_id)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioIn::GetAudioInState, "GetAudioInState"},
|
||||
{1, &IAudioIn::Start, "Start"},
|
||||
{2, &IAudioIn::Stop, "Stop"},
|
||||
{3, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBuffer"},
|
||||
{4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"},
|
||||
{5, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffer"},
|
||||
{6, &IAudioIn::ContainsAudioInBuffer, "ContainsAudioInBuffer"},
|
||||
{7, &IAudioIn::AppendAudioInBuffer, "AppendUacInBuffer"},
|
||||
{8, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBufferAuto"},
|
||||
{9, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffersAuto"},
|
||||
{10, &IAudioIn::AppendAudioInBuffer, "AppendUacInBufferAuto"},
|
||||
{11, &IAudioIn::GetAudioInBufferCount, "GetAudioInBufferCount"},
|
||||
{12, &IAudioIn::SetDeviceGain, "SetDeviceGain"},
|
||||
{13, &IAudioIn::GetDeviceGain, "GetDeviceGain"},
|
||||
{14, &IAudioIn::FlushAudioInBuffers, "FlushAudioInBuffers"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
RegisterHandlers(functions);
|
||||
|
||||
buffer_event = service_context.CreateEvent("IAudioIn:BufferEvent");
|
||||
}
|
||||
if (impl->GetSystem()
|
||||
.Initialize(device_name, in_params, handle, applet_resource_user_id)
|
||||
.IsError()) {
|
||||
LOG_ERROR(Service_Audio, "Failed to initialize the AudioIn System!");
|
||||
}
|
||||
}
|
||||
|
||||
IAudioIn::~IAudioIn() {
|
||||
service_context.CloseEvent(buffer_event);
|
||||
}
|
||||
~IAudioIn() override {
|
||||
impl->Free();
|
||||
service_context.CloseEvent(event);
|
||||
}
|
||||
|
||||
void IAudioIn::Start(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
[[nodiscard]] std::shared_ptr<In> GetImpl() {
|
||||
return impl;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
private:
|
||||
void GetAudioInState(Kernel::HLERequestContext& ctx) {
|
||||
const auto state = static_cast<u32>(impl->GetState());
|
||||
|
||||
void IAudioIn::RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_Audio, "called. State={}", state);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(buffer_event->GetReadableEvent());
|
||||
}
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(state);
|
||||
}
|
||||
|
||||
void IAudioIn::AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
void Start(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
auto result = impl->StartSystem();
|
||||
|
||||
AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Stop(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
auto result = impl->StopSystem();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void AppendAudioInBuffer(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 tag = rp.PopRaw<u64>();
|
||||
|
||||
const auto in_buffer_size{ctx.GetReadBufferSize()};
|
||||
if (in_buffer_size < sizeof(AudioInBuffer)) {
|
||||
LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!");
|
||||
}
|
||||
|
||||
const auto& in_buffer = ctx.ReadBuffer();
|
||||
AudioInBuffer buffer{};
|
||||
std::memcpy(&buffer, in_buffer.data(), sizeof(AudioInBuffer));
|
||||
|
||||
[[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
|
||||
LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag);
|
||||
|
||||
auto result = impl->AppendBuffer(buffer, tag);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
auto& buffer_event = impl->GetBufferEvent();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(buffer_event);
|
||||
}
|
||||
|
||||
void GetReleasedAudioInBuffer(Kernel::HLERequestContext& ctx) {
|
||||
auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64);
|
||||
std::vector<u64> released_buffers(write_buffer_size, 0);
|
||||
|
||||
auto count = impl->GetReleasedBuffers(released_buffers);
|
||||
|
||||
[[maybe_unused]] std::string tags{};
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
tags += fmt::format("{:08X}, ", released_buffers[i]);
|
||||
}
|
||||
[[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
|
||||
LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
|
||||
tags);
|
||||
|
||||
ctx.WriteBuffer(released_buffers);
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(count);
|
||||
}
|
||||
|
||||
void ContainsAudioInBuffer(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 tag{rp.Pop<u64>()};
|
||||
const auto buffer_queued{impl->ContainsAudioBuffer(tag)};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(buffer_queued);
|
||||
}
|
||||
|
||||
void GetAudioInBufferCount(Kernel::HLERequestContext& ctx) {
|
||||
const auto buffer_count = impl->GetBufferCount();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(buffer_count);
|
||||
}
|
||||
|
||||
void SetDeviceGain(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto volume{rp.Pop<f32>()};
|
||||
LOG_DEBUG(Service_Audio, "called. Gain {}", volume);
|
||||
|
||||
impl->SetVolume(volume);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void GetDeviceGain(Kernel::HLERequestContext& ctx) {
|
||||
auto volume{impl->GetVolume()};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Gain {}", volume);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(volume);
|
||||
}
|
||||
|
||||
void FlushAudioInBuffers(Kernel::HLERequestContext& ctx) {
|
||||
bool flushed{impl->FlushAudioInBuffers()};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(flushed);
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* event;
|
||||
std::shared_ptr<AudioCore::AudioIn::In> impl;
|
||||
};
|
||||
|
||||
AudInU::AudInU(Core::System& system_)
|
||||
: ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew},
|
||||
service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>(
|
||||
system_)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AudInU::ListAudioIns, "ListAudioIns"},
|
||||
|
@ -80,59 +223,152 @@ AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} {
|
|||
AudInU::~AudInU() = default;
|
||||
|
||||
void AudInU::ListAudioIns(Kernel::HLERequestContext& ctx) {
|
||||
using namespace AudioCore::AudioRenderer;
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioInDeviceName);
|
||||
|
||||
const std::size_t device_count = std::min(count, audio_device_names.size());
|
||||
std::vector<AudioInDeviceName> device_names;
|
||||
device_names.reserve(device_count);
|
||||
const auto write_count =
|
||||
static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
|
||||
std::vector<AudioDevice::AudioDeviceName> device_names{};
|
||||
|
||||
for (std::size_t i = 0; i < device_count; i++) {
|
||||
const auto& device_name = audio_device_names[i];
|
||||
auto& entry = device_names.emplace_back();
|
||||
device_name.copy(entry.data(), device_name.size());
|
||||
u32 out_count{0};
|
||||
if (write_count > 0) {
|
||||
out_count = impl->GetDeviceNames(device_names, write_count, false);
|
||||
ctx.WriteBuffer(device_names);
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(device_names);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(static_cast<u32>(device_names.size()));
|
||||
rb.Push(out_count);
|
||||
}
|
||||
|
||||
void AudInU::ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
constexpr u32 device_count = 0;
|
||||
using namespace AudioCore::AudioRenderer;
|
||||
|
||||
// Since we don't actually use any other audio input devices, we return 0 devices. Filtered
|
||||
// device listing just omits the default input device
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
const auto write_count =
|
||||
static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
|
||||
std::vector<AudioDevice::AudioDeviceName> device_names{};
|
||||
|
||||
u32 out_count{0};
|
||||
if (write_count > 0) {
|
||||
out_count = impl->GetDeviceNames(device_names, write_count, true);
|
||||
ctx.WriteBuffer(device_names);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(static_cast<u32>(device_count));
|
||||
}
|
||||
|
||||
void AudInU::OpenInOutImpl(Kernel::HLERequestContext& ctx) {
|
||||
AudInOutParams params{};
|
||||
params.channel_count = 2;
|
||||
params.sample_format = SampleFormat::PCM16;
|
||||
params.sample_rate = 48000;
|
||||
params.state = State::Started;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<AudInOutParams>(params);
|
||||
rb.PushIpcInterface<IAudioIn>(system);
|
||||
rb.Push(out_count);
|
||||
}
|
||||
|
||||
void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
OpenInOutImpl(ctx);
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto in_params{rp.PopRaw<AudioInParameter>()};
|
||||
auto applet_resource_user_id{rp.PopRaw<u64>()};
|
||||
const auto device_name_data{ctx.ReadBuffer()};
|
||||
auto device_name = Common::StringFromBuffer(device_name_data);
|
||||
auto handle{ctx.GetCopyHandle(0)};
|
||||
|
||||
std::scoped_lock l{impl->mutex};
|
||||
auto link{impl->LinkToManager()};
|
||||
if (link.IsError()) {
|
||||
LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(link);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t new_session_id{};
|
||||
auto result{impl->AcquireSessionId(new_session_id)};
|
||||
if (result.IsError()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
|
||||
impl->num_free_sessions);
|
||||
|
||||
auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
|
||||
in_params, handle, applet_resource_user_id);
|
||||
impl->sessions[new_session_id] = audio_in->GetImpl();
|
||||
impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
|
||||
|
||||
auto& out_system = impl->sessions[new_session_id]->GetSystem();
|
||||
AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
|
||||
.channel_count = out_system.GetChannelCount(),
|
||||
.sample_format =
|
||||
static_cast<u32>(out_system.GetSampleFormat()),
|
||||
.state = static_cast<u32>(out_system.GetState())};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
|
||||
|
||||
std::string out_name{out_system.GetName()};
|
||||
ctx.WriteBuffer(out_name);
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<AudioInParameterInternal>(out_params);
|
||||
rb.PushIpcInterface<IAudioIn>(audio_in);
|
||||
}
|
||||
|
||||
void AudInU::OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
OpenInOutImpl(ctx);
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto protocol_specified{rp.PopRaw<u64>()};
|
||||
auto in_params{rp.PopRaw<AudioInParameter>()};
|
||||
auto applet_resource_user_id{rp.PopRaw<u64>()};
|
||||
const auto device_name_data{ctx.ReadBuffer()};
|
||||
auto device_name = Common::StringFromBuffer(device_name_data);
|
||||
auto handle{ctx.GetCopyHandle(0)};
|
||||
|
||||
std::scoped_lock l{impl->mutex};
|
||||
auto link{impl->LinkToManager()};
|
||||
if (link.IsError()) {
|
||||
LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(link);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t new_session_id{};
|
||||
auto result{impl->AcquireSessionId(new_session_id)};
|
||||
if (result.IsError()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
|
||||
impl->num_free_sessions);
|
||||
|
||||
auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
|
||||
in_params, handle, applet_resource_user_id);
|
||||
impl->sessions[new_session_id] = audio_in->GetImpl();
|
||||
impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
|
||||
|
||||
auto& out_system = impl->sessions[new_session_id]->GetSystem();
|
||||
AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
|
||||
.channel_count = out_system.GetChannelCount(),
|
||||
.sample_format =
|
||||
static_cast<u32>(out_system.GetSampleFormat()),
|
||||
.state = static_cast<u32>(out_system.GetState())};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
|
||||
|
||||
std::string out_name{out_system.GetName()};
|
||||
if (protocol_specified == 0) {
|
||||
if (out_system.IsUac()) {
|
||||
out_name = "UacIn";
|
||||
} else {
|
||||
out_name = "DeviceIn";
|
||||
}
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(out_name);
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<AudioInParameterInternal>(out_params);
|
||||
rb.PushIpcInterface<IAudioIn>(audio_in);
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/audio_in_manager.h"
|
||||
#include "audio_core/in/audio_in.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
|
@ -14,56 +16,27 @@ namespace Kernel {
|
|||
class HLERequestContext;
|
||||
}
|
||||
|
||||
namespace AudioCore::AudioOut {
|
||||
class Manager;
|
||||
class In;
|
||||
} // namespace AudioCore::AudioOut
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
class IAudioIn final : public ServiceFramework<IAudioIn> {
|
||||
public:
|
||||
explicit IAudioIn(Core::System& system_);
|
||||
~IAudioIn() override;
|
||||
|
||||
private:
|
||||
void Start(Kernel::HLERequestContext& ctx);
|
||||
void RegisterBufferEvent(Kernel::HLERequestContext& ctx);
|
||||
void AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx);
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
Kernel::KEvent* buffer_event;
|
||||
};
|
||||
|
||||
class AudInU final : public ServiceFramework<AudInU> {
|
||||
public:
|
||||
explicit AudInU(Core::System& system_);
|
||||
~AudInU() override;
|
||||
|
||||
private:
|
||||
enum class SampleFormat : u32_le {
|
||||
PCM16 = 2,
|
||||
};
|
||||
|
||||
enum class State : u32_le {
|
||||
Started = 0,
|
||||
Stopped = 1,
|
||||
};
|
||||
|
||||
struct AudInOutParams {
|
||||
u32_le sample_rate{};
|
||||
u32_le channel_count{};
|
||||
SampleFormat sample_format{};
|
||||
State state{};
|
||||
};
|
||||
static_assert(sizeof(AudInOutParams) == 0x10, "AudInOutParams is an invalid size");
|
||||
|
||||
using AudioInDeviceName = std::array<char, 256>;
|
||||
static constexpr std::array<std::string_view, 1> audio_device_names{{
|
||||
"BuiltInHeadset",
|
||||
}};
|
||||
|
||||
void ListAudioIns(Kernel::HLERequestContext& ctx);
|
||||
void ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx);
|
||||
void OpenInOutImpl(Kernel::HLERequestContext& ctx);
|
||||
void OpenAudioIn(Kernel::HLERequestContext& ctx);
|
||||
void OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx);
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
std::unique_ptr<AudioCore::AudioIn::Manager> impl;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
|
|
@ -5,56 +5,43 @@
|
|||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/audio_out.h"
|
||||
#include "audio_core/codec.h"
|
||||
#include "audio_core/out/audio_out_system.h"
|
||||
#include "audio_core/renderer/audio_device.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/audio/audout_u.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}};
|
||||
constexpr int DefaultSampleRate{48000};
|
||||
|
||||
struct AudoutParams {
|
||||
s32_le sample_rate;
|
||||
u16_le channel_count;
|
||||
INSERT_PADDING_BYTES_NOINIT(2);
|
||||
};
|
||||
static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size");
|
||||
|
||||
enum class AudioState : u32 {
|
||||
Started,
|
||||
Stopped,
|
||||
};
|
||||
using namespace AudioCore::AudioOut;
|
||||
|
||||
class IAudioOut final : public ServiceFramework<IAudioOut> {
|
||||
public:
|
||||
explicit IAudioOut(Core::System& system_, AudoutParams audio_params_,
|
||||
AudioCore::AudioOut& audio_core_, std::string&& device_name_,
|
||||
std::string&& unique_name)
|
||||
explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
|
||||
size_t session_id, std::string& device_name,
|
||||
const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id)
|
||||
: ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
|
||||
audio_core{audio_core_}, device_name{std::move(device_name_)},
|
||||
audio_params{audio_params_}, main_memory{system.Memory()}, service_context{system_,
|
||||
"IAudioOut"} {
|
||||
service_context{system_, "IAudioOut"}, event{service_context.CreateEvent(
|
||||
"AudioOutEvent")},
|
||||
impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} {
|
||||
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
|
||||
{1, &IAudioOut::StartAudioOut, "Start"},
|
||||
{2, &IAudioOut::StopAudioOut, "Stop"},
|
||||
{3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"},
|
||||
{1, &IAudioOut::Start, "Start"},
|
||||
{2, &IAudioOut::Stop, "Stop"},
|
||||
{3, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBuffer"},
|
||||
{4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
|
||||
{5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffers"},
|
||||
{5, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffers"},
|
||||
{6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"},
|
||||
{7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
|
||||
{8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
|
||||
{7, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBufferAuto"},
|
||||
{8, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffersAuto"},
|
||||
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
|
||||
{10, &IAudioOut::GetAudioOutPlayedSampleCount, "GetAudioOutPlayedSampleCount"},
|
||||
{11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"},
|
||||
|
@ -64,241 +51,263 @@ public:
|
|||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
// This is the event handle used to check if the audio buffer was released
|
||||
buffer_event = service_context.CreateEvent("IAudioOutBufferReleased");
|
||||
|
||||
stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
|
||||
audio_params.channel_count, std::move(unique_name), [this] {
|
||||
const auto guard = LockService();
|
||||
buffer_event->GetWritableEvent().Signal();
|
||||
});
|
||||
if (impl->GetSystem()
|
||||
.Initialize(device_name, in_params, handle, applet_resource_user_id)
|
||||
.IsError()) {
|
||||
LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!");
|
||||
}
|
||||
}
|
||||
|
||||
~IAudioOut() override {
|
||||
service_context.CloseEvent(buffer_event);
|
||||
impl->Free();
|
||||
service_context.CloseEvent(event);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() {
|
||||
return impl;
|
||||
}
|
||||
|
||||
private:
|
||||
struct AudioBuffer {
|
||||
u64_le next;
|
||||
u64_le buffer;
|
||||
u64_le buffer_capacity;
|
||||
u64_le buffer_size;
|
||||
u64_le offset;
|
||||
};
|
||||
static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size");
|
||||
|
||||
void GetAudioOutState(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
const auto state = static_cast<u32>(impl->GetState());
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. State={}", state);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped));
|
||||
rb.Push(state);
|
||||
}
|
||||
|
||||
void StartAudioOut(Kernel::HLERequestContext& ctx) {
|
||||
void Start(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
if (stream->IsPlaying()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_OPERATION_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
audio_core.StartStream(stream);
|
||||
auto result = impl->StartSystem();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void StopAudioOut(Kernel::HLERequestContext& ctx) {
|
||||
void Stop(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
if (stream->IsPlaying()) {
|
||||
audio_core.StopStream(stream);
|
||||
}
|
||||
auto result = impl->StopSystem();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 tag = rp.PopRaw<u64>();
|
||||
|
||||
const auto in_buffer_size{ctx.GetReadBufferSize()};
|
||||
if (in_buffer_size < sizeof(AudioOutBuffer)) {
|
||||
LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!");
|
||||
}
|
||||
|
||||
const auto& in_buffer = ctx.ReadBuffer();
|
||||
AudioOutBuffer buffer{};
|
||||
std::memcpy(&buffer, in_buffer.data(), sizeof(AudioOutBuffer));
|
||||
|
||||
[[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
|
||||
LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag);
|
||||
|
||||
auto result = impl->AppendBuffer(buffer, tag);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
auto& buffer_event = impl->GetBufferEvent();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(buffer_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(buffer_event);
|
||||
}
|
||||
|
||||
void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description());
|
||||
IPC::RequestParser rp{ctx};
|
||||
void GetReleasedAudioOutBuffers(Kernel::HLERequestContext& ctx) {
|
||||
auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64);
|
||||
std::vector<u64> released_buffers(write_buffer_size, 0);
|
||||
|
||||
const auto& input_buffer{ctx.ReadBuffer()};
|
||||
ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer),
|
||||
"AudioBuffer input is an invalid size!");
|
||||
AudioBuffer audio_buffer{};
|
||||
std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
|
||||
const u64 tag{rp.Pop<u64>()};
|
||||
auto count = impl->GetReleasedBuffers(released_buffers);
|
||||
|
||||
std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
|
||||
main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
|
||||
|
||||
if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_BUFFER_COUNT_EXCEEDED);
|
||||
return;
|
||||
[[maybe_unused]] std::string tags{};
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
tags += fmt::format("{:08X}, ", released_buffers[i]);
|
||||
}
|
||||
[[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
|
||||
LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
|
||||
tags);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called {}", ctx.Description());
|
||||
|
||||
const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)};
|
||||
const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
|
||||
|
||||
std::vector<u64> tags{released_buffers};
|
||||
tags.resize(max_count);
|
||||
ctx.WriteBuffer(tags);
|
||||
|
||||
ctx.WriteBuffer(released_buffers);
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(static_cast<u32>(released_buffers.size()));
|
||||
rb.Push(count);
|
||||
}
|
||||
|
||||
void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 tag{rp.Pop<u64>()};
|
||||
const auto buffer_queued{impl->ContainsAudioBuffer(tag)};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(stream->ContainsBuffer(tag));
|
||||
rb.Push(buffer_queued);
|
||||
}
|
||||
|
||||
void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
const auto buffer_count = impl->GetBufferCount();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(static_cast<u32>(stream->GetQueueSize()));
|
||||
rb.Push(buffer_count);
|
||||
}
|
||||
|
||||
void GetAudioOutPlayedSampleCount(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
const auto samples_played = impl->GetPlayedSampleCount();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Played samples={}", samples_played);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(stream->GetPlayedSampleCount());
|
||||
rb.Push(samples_played);
|
||||
}
|
||||
|
||||
void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
bool flushed{impl->FlushAudioOutBuffers()};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(stream->Flush());
|
||||
rb.Push(flushed);
|
||||
}
|
||||
|
||||
void SetAudioOutVolume(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const float volume = rp.Pop<float>();
|
||||
LOG_DEBUG(Service_Audio, "called, volume={}", volume);
|
||||
const auto volume = rp.Pop<f32>();
|
||||
|
||||
stream->SetVolume(volume);
|
||||
LOG_DEBUG(Service_Audio, "called. Volume={}", volume);
|
||||
|
||||
impl->SetVolume(volume);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void GetAudioOutVolume(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
const auto volume = impl->GetVolume();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Volume={}", volume);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(stream->GetVolume());
|
||||
rb.Push(volume);
|
||||
}
|
||||
|
||||
AudioCore::AudioOut& audio_core;
|
||||
AudioCore::StreamPtr stream;
|
||||
std::string device_name;
|
||||
|
||||
[[maybe_unused]] AudoutParams audio_params{};
|
||||
|
||||
Core::Memory::Memory& main_memory;
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
/// This is the event handle used to check if the audio buffer was released
|
||||
Kernel::KEvent* buffer_event;
|
||||
Kernel::KEvent* event;
|
||||
std::shared_ptr<AudioCore::AudioOut::Out> impl;
|
||||
};
|
||||
|
||||
AudOutU::AudOutU(Core::System& system_) : ServiceFramework{system_, "audout:u"} {
|
||||
AudOutU::AudOutU(Core::System& system_)
|
||||
: ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew},
|
||||
service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>(
|
||||
system_)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"},
|
||||
{1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"},
|
||||
{2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
|
||||
{3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"},
|
||||
{0, &AudOutU::ListAudioOuts, "ListAudioOuts"},
|
||||
{1, &AudOutU::OpenAudioOut, "OpenAudioOut"},
|
||||
{2, &AudOutU::ListAudioOuts, "ListAudioOutsAuto"},
|
||||
{3, &AudOutU::OpenAudioOut, "OpenAudioOutAuto"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
audio_core = std::make_unique<AudioCore::AudioOut>();
|
||||
}
|
||||
|
||||
AudOutU::~AudOutU() = default;
|
||||
|
||||
void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
|
||||
using namespace AudioCore::AudioRenderer;
|
||||
|
||||
ctx.WriteBuffer(DefaultDevice);
|
||||
std::scoped_lock l{impl->mutex};
|
||||
|
||||
const auto write_count =
|
||||
static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
|
||||
std::vector<AudioDevice::AudioDeviceName> device_names{};
|
||||
std::string print_names{};
|
||||
if (write_count > 0) {
|
||||
device_names.push_back(AudioDevice::AudioDeviceName("DeviceOut"));
|
||||
LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut");
|
||||
} else {
|
||||
LOG_DEBUG(Service_Audio, "called. Empty buffer passed in.");
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(device_names);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(1); // Amount of audio devices
|
||||
rb.Push<u32>(static_cast<u32>(device_names.size()));
|
||||
}
|
||||
|
||||
void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
const auto device_name_data{ctx.ReadBuffer()};
|
||||
std::string device_name;
|
||||
if (device_name_data[0] != '\0') {
|
||||
device_name.assign(device_name_data.begin(), device_name_data.end());
|
||||
} else {
|
||||
device_name.assign(DefaultDevice.begin(), DefaultDevice.end());
|
||||
}
|
||||
ctx.WriteBuffer(device_name);
|
||||
|
||||
void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params{rp.PopRaw<AudoutParams>()};
|
||||
if (params.channel_count <= 2) {
|
||||
// Mono does not exist for audout
|
||||
params.channel_count = 2;
|
||||
} else {
|
||||
params.channel_count = 6;
|
||||
}
|
||||
if (!params.sample_rate) {
|
||||
params.sample_rate = DefaultSampleRate;
|
||||
auto in_params{rp.PopRaw<AudioOutParameter>()};
|
||||
auto applet_resource_user_id{rp.PopRaw<u64>()};
|
||||
const auto device_name_data{ctx.ReadBuffer()};
|
||||
auto device_name = Common::StringFromBuffer(device_name_data);
|
||||
auto handle{ctx.GetCopyHandle(0)};
|
||||
|
||||
auto link{impl->LinkToManager()};
|
||||
if (link.IsError()) {
|
||||
LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(link);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())};
|
||||
auto audio_out_interface = std::make_shared<IAudioOut>(
|
||||
system, params, *audio_core, std::move(device_name), std::move(unique_name));
|
||||
size_t new_session_id{};
|
||||
auto result{impl->AcquireSessionId(new_session_id)};
|
||||
if (result.IsError()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
|
||||
impl->num_free_sessions);
|
||||
|
||||
auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name,
|
||||
in_params, handle, applet_resource_user_id);
|
||||
|
||||
impl->sessions[new_session_id] = audio_out->GetImpl();
|
||||
impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
|
||||
|
||||
auto& out_system = impl->sessions[new_session_id]->GetSystem();
|
||||
AudioOutParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
|
||||
.channel_count = out_system.GetChannelCount(),
|
||||
.sample_format =
|
||||
static_cast<u32>(out_system.GetSampleFormat()),
|
||||
.state = static_cast<u32>(out_system.GetState())};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(DefaultSampleRate);
|
||||
rb.Push<u32>(params.channel_count);
|
||||
rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
|
||||
rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
|
||||
rb.PushIpcInterface<IAudioOut>(audio_out_interface);
|
||||
|
||||
audio_out_interfaces.push_back(std::move(audio_out_interface));
|
||||
ctx.WriteBuffer(out_system.GetName());
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<AudioOutParameterInternal>(out_params);
|
||||
rb.PushIpcInterface<IAudioOut>(audio_out);
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
|
|
@ -3,13 +3,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "audio_core/audio_out_manager.h"
|
||||
#include "audio_core/out/audio_out.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace AudioCore {
|
||||
class AudioOut;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
@ -18,6 +16,11 @@ namespace Kernel {
|
|||
class HLERequestContext;
|
||||
}
|
||||
|
||||
namespace AudioCore::AudioOut {
|
||||
class Manager;
|
||||
class Out;
|
||||
} // namespace AudioCore::AudioOut
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
class IAudioOut;
|
||||
|
@ -28,11 +31,11 @@ public:
|
|||
~AudOutU() override;
|
||||
|
||||
private:
|
||||
void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
|
||||
void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
|
||||
void ListAudioOuts(Kernel::HLERequestContext& ctx);
|
||||
void OpenAudioOut(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces;
|
||||
std::unique_ptr<AudioCore::AudioOut> audio_core;
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
std::unique_ptr<AudioCore::AudioOut::Manager> impl;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
|
|
@ -4,7 +4,12 @@
|
|||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "audio_core/common/audio_renderer_parameter.h"
|
||||
#include "audio_core/common/feature_support.h"
|
||||
#include "audio_core/renderer/audio_device.h"
|
||||
#include "audio_core/renderer/audio_renderer.h"
|
||||
#include "audio_core/renderer/voice/voice_info.h"
|
||||
#include "common/alignment.h"
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_funcs.h"
|
||||
|
@ -13,91 +18,112 @@
|
|||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
#include "core/hle/service/audio/audren_u.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
using namespace AudioCore::AudioRenderer;
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
|
||||
public:
|
||||
explicit IAudioRenderer(Core::System& system_,
|
||||
const AudioCommon::AudioRendererParameter& audren_params,
|
||||
const std::size_t instance_number)
|
||||
explicit IAudioRenderer(Core::System& system_, Manager& manager_,
|
||||
AudioCore::AudioRendererParameterInternal& params,
|
||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
||||
u32 process_handle, u64 applet_resource_user_id, s32 session_id)
|
||||
: ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew},
|
||||
service_context{system_, "IAudioRenderer"} {
|
||||
service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent(
|
||||
"IAudioRendererEvent")},
|
||||
manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
|
||||
{1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
|
||||
{2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
|
||||
{3, &IAudioRenderer::GetState, "GetState"},
|
||||
{4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"},
|
||||
{4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
|
||||
{5, &IAudioRenderer::Start, "Start"},
|
||||
{6, &IAudioRenderer::Stop, "Stop"},
|
||||
{7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
|
||||
{8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
|
||||
{9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
|
||||
{10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"},
|
||||
{11, &IAudioRenderer::ExecuteAudioRendererRendering, "ExecuteAudioRendererRendering"},
|
||||
{10, nullptr, "RequestUpdateAuto"},
|
||||
{11, nullptr, "ExecuteAudioRendererRendering"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
system_event = service_context.CreateEvent("IAudioRenderer:SystemEvent");
|
||||
renderer = std::make_unique<AudioCore::AudioRenderer>(
|
||||
system.CoreTiming(), system.Memory(), audren_params,
|
||||
[this]() {
|
||||
const auto guard = LockService();
|
||||
system_event->GetWritableEvent().Signal();
|
||||
},
|
||||
instance_number);
|
||||
impl->Initialize(params, transfer_memory, transfer_memory_size, process_handle,
|
||||
applet_resource_user_id, session_id);
|
||||
}
|
||||
|
||||
~IAudioRenderer() override {
|
||||
service_context.CloseEvent(system_event);
|
||||
impl->Finalize();
|
||||
service_context.CloseEvent(rendered_event);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetSampleRate(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
const auto sample_rate{impl->GetSystem().GetSampleRate()};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Sample rate {}", sample_rate);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(renderer->GetSampleRate());
|
||||
rb.Push(sample_rate);
|
||||
}
|
||||
|
||||
void GetSampleCount(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
const auto sample_count{impl->GetSystem().GetSampleCount()};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Sample count {}", sample_count);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(renderer->GetSampleCount());
|
||||
rb.Push(sample_count);
|
||||
}
|
||||
|
||||
void GetState(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
const u32 state{!impl->GetSystem().IsActive()};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called, state {}", state);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(static_cast<u32>(renderer->GetStreamState()));
|
||||
rb.Push(state);
|
||||
}
|
||||
|
||||
void GetMixBufferCount(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
const auto buffer_count{impl->GetSystem().GetMixBufferCount()};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(renderer->GetMixBufferCount());
|
||||
rb.Push(buffer_count);
|
||||
}
|
||||
|
||||
void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "(STUBBED) called");
|
||||
void RequestUpdate(Kernel::HLERequestContext& ctx) {
|
||||
LOG_TRACE(Service_Audio, "called");
|
||||
|
||||
std::vector<u8> output_params(ctx.GetWriteBufferSize(), 0);
|
||||
auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params);
|
||||
std::vector<u8> input{ctx.ReadBuffer(0)};
|
||||
|
||||
// These buffers are written manually to avoid an issue with WriteBuffer throwing errors for
|
||||
// checking size 0. Performance size is 0 for most games.
|
||||
const auto buffers{ctx.BufferDescriptorB()};
|
||||
std::vector<u8> output(buffers[0].Size(), 0);
|
||||
std::vector<u8> performance(buffers[1].Size(), 0);
|
||||
|
||||
auto result = impl->RequestUpdate(input, performance, output);
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
ctx.WriteBuffer(output_params);
|
||||
ctx.WriteBufferB(output.data(), output.size(), 0);
|
||||
ctx.WriteBufferB(performance.data(), performance.size(), 1);
|
||||
} else {
|
||||
LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
@ -105,38 +131,45 @@ private:
|
|||
}
|
||||
|
||||
void Start(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
const auto result = renderer->Start();
|
||||
impl->Start();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Stop(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
const auto result = renderer->Stop();
|
||||
impl->Stop();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
if (impl->GetSystem().GetExecutionMode() == AudioCore::ExecutionMode::Manual) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NOT_SUPPORTED);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(system_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(rendered_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
rendering_time_limit_percent = rp.Pop<u32>();
|
||||
LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}",
|
||||
rendering_time_limit_percent);
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
ASSERT(rendering_time_limit_percent <= 100);
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto limit = rp.PopRaw<u32>();
|
||||
|
||||
auto& system_ = impl->GetSystem();
|
||||
system_.SetRenderingTimeLimit(limit);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
|
@ -145,34 +178,34 @@ private:
|
|||
void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
auto& system_ = impl->GetSystem();
|
||||
auto time = system_.GetRenderingTimeLimit();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(rendering_time_limit_percent);
|
||||
rb.Push(time);
|
||||
}
|
||||
|
||||
void ExecuteAudioRendererRendering(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
// This service command currently only reports an unsupported operation
|
||||
// error code, or aborts. Given that, we just always return an error
|
||||
// code in this case.
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
Kernel::KEvent* system_event;
|
||||
std::unique_ptr<AudioCore::AudioRenderer> renderer;
|
||||
u32 rendering_time_limit_percent = 100;
|
||||
Kernel::KEvent* rendered_event;
|
||||
Manager& manager;
|
||||
std::unique_ptr<Renderer> impl;
|
||||
};
|
||||
|
||||
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
|
||||
|
||||
public:
|
||||
explicit IAudioDevice(Core::System& system_, Kernel::KEvent* buffer_event_, u32_le revision_)
|
||||
: ServiceFramework{system_, "IAudioDevice"}, buffer_event{buffer_event_}, revision{
|
||||
revision_} {
|
||||
explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision,
|
||||
u32 device_num)
|
||||
: ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew},
|
||||
service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>(
|
||||
system_, applet_resource_user_id,
|
||||
revision)},
|
||||
event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
|
||||
{1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
|
||||
|
@ -186,54 +219,45 @@ public:
|
|||
{10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
|
||||
{11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"},
|
||||
{12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"},
|
||||
{13, nullptr, "GetActiveAudioOutputDeviceName"},
|
||||
{14, nullptr, "ListAudioOutputDeviceName"},
|
||||
{13, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioOutputDeviceName"},
|
||||
{14, &IAudioDevice::ListAudioOutputDeviceName, "ListAudioOutputDeviceName"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
event->GetWritableEvent().Signal();
|
||||
}
|
||||
|
||||
~IAudioDevice() override {
|
||||
service_context.CloseEvent(event);
|
||||
}
|
||||
|
||||
private:
|
||||
using AudioDeviceName = std::array<char, 256>;
|
||||
static constexpr std::array<std::string_view, 4> audio_device_names{{
|
||||
"AudioStereoJackOutput",
|
||||
"AudioBuiltInSpeakerOutput",
|
||||
"AudioTvOutput",
|
||||
"AudioUsbDeviceOutput",
|
||||
}};
|
||||
enum class DeviceType {
|
||||
AHUBHeadphones,
|
||||
AHUBSpeakers,
|
||||
HDA,
|
||||
USBOutput,
|
||||
};
|
||||
|
||||
void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName);
|
||||
|
||||
const bool usb_output_supported =
|
||||
IsFeatureSupported(AudioFeatures::AudioUSBDeviceOutput, revision);
|
||||
const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioDeviceName);
|
||||
std::vector<AudioDevice::AudioDeviceName> out_names{};
|
||||
|
||||
std::vector<AudioDeviceName> name_buffer;
|
||||
name_buffer.reserve(audio_device_names.size());
|
||||
u32 out_count = impl->ListAudioDeviceName(out_names, in_count);
|
||||
|
||||
for (std::size_t i = 0; i < count && i < audio_device_names.size(); i++) {
|
||||
const auto type = static_cast<DeviceType>(i);
|
||||
|
||||
if (!usb_output_supported && type == DeviceType::USBOutput) {
|
||||
continue;
|
||||
std::string out{};
|
||||
for (u32 i = 0; i < out_count; i++) {
|
||||
std::string a{};
|
||||
u32 j = 0;
|
||||
while (out_names[i].name[j] != '\0') {
|
||||
a += out_names[i].name[j];
|
||||
j++;
|
||||
}
|
||||
|
||||
const auto& device_name = audio_device_names[i];
|
||||
auto& entry = name_buffer.emplace_back();
|
||||
device_name.copy(entry.data(), device_name.size());
|
||||
out += "\n\t" + a;
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(name_buffer);
|
||||
LOG_DEBUG(Service_Audio, "called.\nNames={}", out);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
ctx.WriteBuffer(out_names);
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(static_cast<u32>(name_buffer.size()));
|
||||
rb.Push(out_count);
|
||||
}
|
||||
|
||||
void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
|
||||
|
@ -243,7 +267,11 @@ private:
|
|||
const auto device_name_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(device_name_buffer);
|
||||
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called. name={}, volume={}", name, volume);
|
||||
LOG_DEBUG(Service_Audio, "called. name={}, volume={}", name, volume);
|
||||
|
||||
if (name == "AudioTvOutput") {
|
||||
impl->SetDeviceVolumes(volume);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
|
@ -253,53 +281,60 @@ private:
|
|||
const auto device_name_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(device_name_buffer);
|
||||
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called. name={}", name);
|
||||
LOG_DEBUG(Service_Audio, "called. Name={}", name);
|
||||
|
||||
f32 volume{1.0f};
|
||||
if (name == "AudioTvOutput") {
|
||||
volume = impl->GetDeviceVolume(name);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(1.0f);
|
||||
rb.Push(volume);
|
||||
}
|
||||
|
||||
void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
const auto write_size = ctx.GetWriteBufferSize() / sizeof(char);
|
||||
std::string out_name{"AudioTvOutput"};
|
||||
|
||||
// Currently set to always be TV audio output.
|
||||
const auto& device_name = audio_device_names[2];
|
||||
LOG_DEBUG(Service_Audio, "(STUBBED) called. Name={}", out_name);
|
||||
|
||||
AudioDeviceName out_device_name{};
|
||||
device_name.copy(out_device_name.data(), device_name.size());
|
||||
out_name.resize(write_size);
|
||||
|
||||
ctx.WriteBuffer(out_device_name);
|
||||
ctx.WriteBuffer(out_name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_Audio, "(STUBBED) called");
|
||||
|
||||
buffer_event->GetWritableEvent().Signal();
|
||||
event->GetWritableEvent().Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(buffer_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void GetActiveChannelCount(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
const auto& sink{system.AudioCore().GetOutputSink()};
|
||||
u32 channel_count{sink.GetDeviceChannels()};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "(STUBBED) called. Channels={}", channel_count);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(2);
|
||||
rb.Push<u32>(channel_count);
|
||||
}
|
||||
|
||||
// Should be similar to QueryAudioDeviceOutputEvent
|
||||
void QueryAudioDeviceInputEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_Audio, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(buffer_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) {
|
||||
|
@ -307,402 +342,167 @@ private:
|
|||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(buffer_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(event->GetReadableEvent());
|
||||
}
|
||||
|
||||
Kernel::KEvent* buffer_event;
|
||||
u32_le revision = 0;
|
||||
void ListAudioOutputDeviceName(Kernel::HLERequestContext& ctx) {
|
||||
const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName);
|
||||
|
||||
std::vector<AudioDevice::AudioDeviceName> out_names{};
|
||||
|
||||
u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count);
|
||||
|
||||
std::string out{};
|
||||
for (u32 i = 0; i < out_count; i++) {
|
||||
std::string a{};
|
||||
u32 j = 0;
|
||||
while (out_names[i].name[j] != '\0') {
|
||||
a += out_names[i].name[j];
|
||||
j++;
|
||||
}
|
||||
out += "\n\t" + a;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called.\nNames={}", out);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
ctx.WriteBuffer(out_names);
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(out_count);
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
std::unique_ptr<AudioDevice> impl;
|
||||
Kernel::KEvent* event;
|
||||
};
|
||||
|
||||
AudRenU::AudRenU(Core::System& system_)
|
||||
: ServiceFramework{system_, "audren:u"}, service_context{system_, "audren:u"} {
|
||||
: ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew},
|
||||
service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
|
||||
{1, &AudRenU::GetAudioRendererWorkBufferSize, "GetWorkBufferSize"},
|
||||
{1, &AudRenU::GetWorkBufferSize, "GetWorkBufferSize"},
|
||||
{2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"},
|
||||
{3, &AudRenU::OpenAudioRendererForManualExecution, "OpenAudioRendererForManualExecution"},
|
||||
{3, nullptr, "OpenAudioRendererForManualExecution"},
|
||||
{4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
buffer_event = service_context.CreateEvent("IAudioOutBufferReleasedEvent");
|
||||
}
|
||||
|
||||
AudRenU::~AudRenU() {
|
||||
service_context.CloseEvent(buffer_event);
|
||||
}
|
||||
AudRenU::~AudRenU() = default;
|
||||
|
||||
void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
OpenAudioRendererImpl(ctx);
|
||||
AudioCore::AudioRendererParameterInternal params;
|
||||
rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params);
|
||||
auto transfer_memory_handle = ctx.GetCopyHandle(0);
|
||||
auto process_handle = ctx.GetCopyHandle(1);
|
||||
auto transfer_memory_size = rp.Pop<u64>();
|
||||
auto applet_resource_user_id = rp.Pop<u64>();
|
||||
|
||||
if (impl->GetSessionCount() + 1 > AudioCore::MaxRendererSessions) {
|
||||
LOG_ERROR(Service_Audio, "Too many AudioRenderer sessions open!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_MAXIMUM_SESSIONS_REACHED);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& handle_table{system.CurrentProcess()->GetHandleTable()};
|
||||
auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
|
||||
auto transfer_memory{
|
||||
process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||
|
||||
const auto session_id{impl->GetSessionId()};
|
||||
if (session_id == -1) {
|
||||
LOG_ERROR(Service_Audio, "Tried to open a session that's already in use!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_MAXIMUM_SESSIONS_REACHED);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Audio, "Opened new AudioRenderer session {} sessions open {}", session_id,
|
||||
impl->GetSessionCount());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAudioRenderer>(system, *impl, params, transfer_memory.GetPointerUnsafe(),
|
||||
transfer_memory_size, process_handle,
|
||||
applet_resource_user_id, session_id);
|
||||
}
|
||||
|
||||
static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) {
|
||||
// +1 represents the final mix.
|
||||
return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count +
|
||||
1;
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
// Several calculations below align the sizes being calculated
|
||||
// onto a 64 byte boundary.
|
||||
static constexpr u64 buffer_alignment_size = 64;
|
||||
|
||||
// Some calculations that calculate portions of the buffer
|
||||
// that will contain information, on the other hand, align
|
||||
// the result of some of their calcularions on a 16 byte boundary.
|
||||
static constexpr u64 info_field_alignment_size = 16;
|
||||
|
||||
// Maximum detail entries that may exist at one time for performance
|
||||
// frame statistics.
|
||||
static constexpr u64 max_perf_detail_entries = 100;
|
||||
|
||||
// Size of the data structure representing the bulk of the voice-related state.
|
||||
static constexpr u64 voice_state_size_bytes = 0x100;
|
||||
|
||||
// Size of the upsampler manager data structure
|
||||
constexpr u64 upsampler_manager_size = 0x48;
|
||||
|
||||
// Calculates the part of the size that relates to mix buffers.
|
||||
const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) {
|
||||
// As of 8.0.0 this is the maximum on voice channels.
|
||||
constexpr u64 max_voice_channels = 6;
|
||||
|
||||
// The service expects the sample_count member of the parameters to either be
|
||||
// a value of 160 or 240, so the maximum sample count is assumed in order
|
||||
// to adequately handle all values at runtime.
|
||||
constexpr u64 default_max_sample_count = 240;
|
||||
|
||||
const u64 total_mix_buffers = params.mix_buffer_count + max_voice_channels;
|
||||
|
||||
u64 size = 0;
|
||||
size += total_mix_buffers * (sizeof(s32) * params.sample_count);
|
||||
size += total_mix_buffers * (sizeof(s32) * default_max_sample_count);
|
||||
size += u64{params.submix_count} + params.sink_count;
|
||||
size = Common::AlignUp(size, buffer_alignment_size);
|
||||
size += Common::AlignUp(params.unknown_30, buffer_alignment_size);
|
||||
size += Common::AlignUp(sizeof(s32) * params.mix_buffer_count, buffer_alignment_size);
|
||||
return size;
|
||||
};
|
||||
|
||||
// Calculates the portion of the size related to the mix data (and the sorting thereof).
|
||||
const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) {
|
||||
// The size of the mixing info data structure.
|
||||
constexpr u64 mix_info_size = 0x940;
|
||||
|
||||
// Consists of total submixes with the final mix included.
|
||||
const u64 total_mix_count = u64{params.submix_count} + 1;
|
||||
|
||||
// The total number of effects that may be available to the audio renderer at any time.
|
||||
constexpr u64 max_effects = 256;
|
||||
|
||||
// Calculates the part of the size related to the audio node state.
|
||||
// This will only be used if the audio revision supports the splitter.
|
||||
const auto calculate_node_state_size = [](std::size_t num_nodes) {
|
||||
// Internally within a nodestate, it appears to use a data structure
|
||||
// similar to a std::bitset<64> twice.
|
||||
constexpr u64 bit_size = Common::BitSize<u64>();
|
||||
constexpr u64 num_bitsets = 2;
|
||||
|
||||
// Node state instances have three states internally for performing
|
||||
// depth-first searches of nodes. Initialized, Found, and Done Sorting.
|
||||
constexpr u64 num_states = 3;
|
||||
|
||||
u64 size = 0;
|
||||
size += (num_nodes * num_nodes) * sizeof(s32);
|
||||
size += num_states * (num_nodes * sizeof(s32));
|
||||
size += num_bitsets * (Common::AlignUp(num_nodes, bit_size) / Common::BitSize<u8>());
|
||||
return size;
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to the adjacency (aka edge) matrix.
|
||||
const auto calculate_edge_matrix_size = [](std::size_t num_nodes) {
|
||||
return (num_nodes * num_nodes) * sizeof(s32);
|
||||
};
|
||||
|
||||
u64 size = 0;
|
||||
size += Common::AlignUp(sizeof(void*) * total_mix_count, info_field_alignment_size);
|
||||
size += Common::AlignUp(mix_info_size * total_mix_count, info_field_alignment_size);
|
||||
size += Common::AlignUp(sizeof(s32) * max_effects * params.submix_count,
|
||||
info_field_alignment_size);
|
||||
|
||||
if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
|
||||
size += Common::AlignUp(calculate_node_state_size(total_mix_count) +
|
||||
calculate_edge_matrix_size(total_mix_count),
|
||||
info_field_alignment_size);
|
||||
}
|
||||
|
||||
return size;
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to voice channel info.
|
||||
const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) {
|
||||
constexpr u64 voice_info_size = 0x220;
|
||||
constexpr u64 voice_resource_size = 0xD0;
|
||||
|
||||
u64 size = 0;
|
||||
size += Common::AlignUp(sizeof(void*) * params.voice_count, info_field_alignment_size);
|
||||
size += Common::AlignUp(voice_info_size * params.voice_count, info_field_alignment_size);
|
||||
size +=
|
||||
Common::AlignUp(voice_resource_size * params.voice_count, info_field_alignment_size);
|
||||
size +=
|
||||
Common::AlignUp(voice_state_size_bytes * params.voice_count, info_field_alignment_size);
|
||||
return size;
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to memory pools.
|
||||
const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) {
|
||||
const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count);
|
||||
const u64 memory_pool_info_size = 0x20;
|
||||
return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size);
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to the splitter context.
|
||||
const auto calculate_splitter_context_size =
|
||||
[](const AudioCommon::AudioRendererParameter& params) -> u64 {
|
||||
if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr u64 splitter_info_size = 0x20;
|
||||
constexpr u64 splitter_destination_data_size = 0xE0;
|
||||
|
||||
u64 size = 0;
|
||||
size += params.num_splitter_send_channels;
|
||||
size +=
|
||||
Common::AlignUp(splitter_info_size * params.splitter_count, info_field_alignment_size);
|
||||
size += Common::AlignUp(splitter_destination_data_size * params.num_splitter_send_channels,
|
||||
info_field_alignment_size);
|
||||
|
||||
return size;
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to the upsampler info.
|
||||
const auto calculate_upsampler_info_size =
|
||||
[](const AudioCommon::AudioRendererParameter& params) {
|
||||
constexpr u64 upsampler_info_size = 0x280;
|
||||
// Yes, using the buffer size over info alignment size is intentional here.
|
||||
return Common::AlignUp(upsampler_info_size *
|
||||
(u64{params.submix_count} + params.sink_count),
|
||||
buffer_alignment_size);
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to effect info.
|
||||
const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) {
|
||||
constexpr u64 effect_info_size = 0x2B0;
|
||||
return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size);
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to audio sink info.
|
||||
const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) {
|
||||
const u64 sink_info_size = 0x170;
|
||||
return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size);
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to voice state info.
|
||||
const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) {
|
||||
const u64 voice_state_size = 0x100;
|
||||
const u64 additional_size = buffer_alignment_size - 1;
|
||||
return Common::AlignUp(voice_state_size * params.voice_count + additional_size,
|
||||
info_field_alignment_size);
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to performance statistics.
|
||||
const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) {
|
||||
// Extra size value appended to the end of the calculation.
|
||||
constexpr u64 appended = 128;
|
||||
|
||||
// Whether or not we assume the newer version of performance metrics data structures.
|
||||
const bool is_v2 =
|
||||
IsFeatureSupported(AudioFeatures::PerformanceMetricsVersion2, params.revision);
|
||||
|
||||
// Data structure sizes
|
||||
constexpr u64 perf_statistics_size = 0x0C;
|
||||
const u64 header_size = is_v2 ? 0x30 : 0x18;
|
||||
const u64 entry_size = is_v2 ? 0x18 : 0x10;
|
||||
const u64 detail_size = is_v2 ? 0x18 : 0x10;
|
||||
|
||||
const u64 entry_count = CalculateNumPerformanceEntries(params);
|
||||
const u64 size_per_frame =
|
||||
header_size + (entry_size * entry_count) + (detail_size * max_perf_detail_entries);
|
||||
|
||||
u64 size = 0;
|
||||
size += Common::AlignUp(size_per_frame * params.performance_frame_count + 1,
|
||||
buffer_alignment_size);
|
||||
size += Common::AlignUp(perf_statistics_size, buffer_alignment_size);
|
||||
size += appended;
|
||||
return size;
|
||||
};
|
||||
|
||||
// Calculates the part of the size that relates to the audio command buffer.
|
||||
const auto calculate_command_buffer_size =
|
||||
[](const AudioCommon::AudioRendererParameter& params) {
|
||||
constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
|
||||
|
||||
if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
|
||||
constexpr u64 command_buffer_size = 0x18000;
|
||||
|
||||
return command_buffer_size + alignment;
|
||||
}
|
||||
|
||||
// When the variadic command buffer is supported, this means
|
||||
// the command generator for the audio renderer can issue commands
|
||||
// that are (as one would expect), variable in size. So what we need to do
|
||||
// is determine the maximum possible size for a few command data structures
|
||||
// then multiply them by the amount of present commands indicated by the given
|
||||
// respective audio parameters.
|
||||
|
||||
constexpr u64 max_biquad_filters = 2;
|
||||
constexpr u64 max_mix_buffers = 24;
|
||||
|
||||
constexpr u64 biquad_filter_command_size = 0x2C;
|
||||
|
||||
constexpr u64 depop_mix_command_size = 0x24;
|
||||
constexpr u64 depop_setup_command_size = 0x50;
|
||||
|
||||
constexpr u64 effect_command_max_size = 0x540;
|
||||
|
||||
constexpr u64 mix_command_size = 0x1C;
|
||||
constexpr u64 mix_ramp_command_size = 0x24;
|
||||
constexpr u64 mix_ramp_grouped_command_size = 0x13C;
|
||||
|
||||
constexpr u64 perf_command_size = 0x28;
|
||||
|
||||
constexpr u64 sink_command_size = 0x130;
|
||||
|
||||
constexpr u64 submix_command_max_size =
|
||||
depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
|
||||
|
||||
constexpr u64 volume_command_size = 0x1C;
|
||||
constexpr u64 volume_ramp_command_size = 0x20;
|
||||
|
||||
constexpr u64 voice_biquad_filter_command_size =
|
||||
biquad_filter_command_size * max_biquad_filters;
|
||||
constexpr u64 voice_data_command_size = 0x9C;
|
||||
const u64 voice_command_max_size =
|
||||
(params.splitter_count * depop_setup_command_size) +
|
||||
(voice_data_command_size + voice_biquad_filter_command_size +
|
||||
volume_ramp_command_size + mix_ramp_grouped_command_size);
|
||||
|
||||
// Now calculate the individual elements that comprise the size and add them together.
|
||||
const u64 effect_commands_size = params.effect_count * effect_command_max_size;
|
||||
|
||||
const u64 final_mix_commands_size =
|
||||
depop_mix_command_size + volume_command_size * max_mix_buffers;
|
||||
|
||||
const u64 perf_commands_size =
|
||||
perf_command_size *
|
||||
(CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
|
||||
|
||||
const u64 sink_commands_size = params.sink_count * sink_command_size;
|
||||
|
||||
const u64 splitter_commands_size =
|
||||
params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
|
||||
|
||||
const u64 submix_commands_size = params.submix_count * submix_command_max_size;
|
||||
|
||||
const u64 voice_commands_size = params.voice_count * voice_command_max_size;
|
||||
|
||||
return effect_commands_size + final_mix_commands_size + perf_commands_size +
|
||||
sink_commands_size + splitter_commands_size + submix_commands_size +
|
||||
voice_commands_size + alignment;
|
||||
};
|
||||
void AudRenU::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
AudioCore::AudioRendererParameterInternal params;
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
|
||||
rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params);
|
||||
|
||||
u64 size = 0;
|
||||
size += calculate_mix_buffer_sizes(params);
|
||||
size += calculate_mix_info_size(params);
|
||||
size += calculate_voice_info_size(params);
|
||||
size += upsampler_manager_size;
|
||||
size += calculate_memory_pools_size(params);
|
||||
size += calculate_splitter_context_size(params);
|
||||
u64 size{0};
|
||||
auto result = impl->GetWorkBufferSize(params, size);
|
||||
|
||||
size = Common::AlignUp(size, buffer_alignment_size);
|
||||
std::string output_info{};
|
||||
output_info += fmt::format("\tRevision {}", AudioCore::GetRevisionNum(params.revision));
|
||||
output_info +=
|
||||
fmt::format("\n\tSample Rate {}, Sample Count {}", params.sample_rate, params.sample_count);
|
||||
output_info += fmt::format("\n\tExecution Mode {}, Voice Drop Enabled {}",
|
||||
static_cast<u32>(params.execution_mode), params.voice_drop_enabled);
|
||||
output_info += fmt::format(
|
||||
"\n\tSizes: Effects {:04X}, Mixes {:04X}, Sinks {:04X}, Submixes {:04X}, Splitter Infos "
|
||||
"{:04X}, Splitter Destinations {:04X}, Voices {:04X}, Performance Frames {:04X} External "
|
||||
"Context {:04X}",
|
||||
params.effects, params.mixes, params.sinks, params.sub_mixes, params.splitter_infos,
|
||||
params.splitter_destinations, params.voices, params.perf_frames,
|
||||
params.external_context_size);
|
||||
|
||||
size += calculate_upsampler_info_size(params);
|
||||
size += calculate_effect_info_size(params);
|
||||
size += calculate_sink_info_size(params);
|
||||
size += calculate_voice_state_size(params);
|
||||
size += calculate_perf_size(params);
|
||||
size += calculate_command_buffer_size(params);
|
||||
|
||||
// finally, 4KB page align the size, and we're done.
|
||||
size = Common::AlignUp(size, 4096);
|
||||
LOG_DEBUG(Service_Audio, "called.\nInput params:\n{}\nOutput params:\n\tWorkbuffer size {:08X}",
|
||||
output_info, size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(result);
|
||||
rb.Push<u64>(size);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", size);
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 aruid = rp.Pop<u64>();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. aruid={:016X}", aruid);
|
||||
const auto applet_resource_user_id = rp.Pop<u64>();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Applet resource id {}", applet_resource_user_id);
|
||||
|
||||
// Revisionless variant of GetAudioDeviceServiceWithRevisionInfo that
|
||||
// always assumes the initial release revision (REV1).
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAudioDevice>(system, buffer_event, Common::MakeMagic('R', 'E', 'V', '1'));
|
||||
rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id,
|
||||
::Common::MakeMagic('R', 'E', 'V', '1'), num_audio_devices++);
|
||||
}
|
||||
|
||||
void AudRenU::OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
OpenAudioRendererImpl(ctx);
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
u32 revision;
|
||||
u64 aruid;
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto [revision, aruid] = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. revision={:08X}, aruid={:016X}", revision, aruid);
|
||||
const auto [revision, applet_resource_user_id] = rp.PopRaw<Parameters>();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAudioDevice>(system, buffer_event, revision);
|
||||
}
|
||||
LOG_DEBUG(Service_Audio, "called. Revision {} Applet resource id {}",
|
||||
AudioCore::GetRevisionNum(revision), applet_resource_user_id);
|
||||
|
||||
void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAudioRenderer>(system, params, audren_instance_count++);
|
||||
}
|
||||
|
||||
bool IsFeatureSupported(AudioFeatures feature, u32_le revision) {
|
||||
// Byte swap
|
||||
const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0');
|
||||
|
||||
switch (feature) {
|
||||
case AudioFeatures::AudioUSBDeviceOutput:
|
||||
return version_num >= 4U;
|
||||
case AudioFeatures::Splitter:
|
||||
return version_num >= 2U;
|
||||
case AudioFeatures::PerformanceMetricsVersion2:
|
||||
case AudioFeatures::VariadicCommandBuffer:
|
||||
return version_num >= 5U;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id, revision,
|
||||
num_audio_devices++);
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/audio_render_manager.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
|
@ -15,6 +16,7 @@ class HLERequestContext;
|
|||
}
|
||||
|
||||
namespace Service::Audio {
|
||||
class IAudioRenderer;
|
||||
|
||||
class AudRenU final : public ServiceFramework<AudRenU> {
|
||||
public:
|
||||
|
@ -23,28 +25,14 @@ public:
|
|||
|
||||
private:
|
||||
void OpenAudioRenderer(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
|
||||
void GetWorkBufferSize(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioDeviceService(Kernel::HLERequestContext& ctx);
|
||||
void OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx);
|
||||
|
||||
void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx);
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
std::size_t audren_instance_count = 0;
|
||||
Kernel::KEvent* buffer_event;
|
||||
std::unique_ptr<AudioCore::AudioRenderer::Manager> impl;
|
||||
u32 num_audio_devices{0};
|
||||
};
|
||||
|
||||
// Describes a particular audio feature that may be supported in a particular revision.
|
||||
enum class AudioFeatures : u32 {
|
||||
AudioUSBDeviceOutput,
|
||||
Splitter,
|
||||
PerformanceMetricsVersion2,
|
||||
VariadicCommandBuffer,
|
||||
};
|
||||
|
||||
// Tests if a particular audio feature is supported with a given audio revision.
|
||||
bool IsFeatureSupported(AudioFeatures feature, u32_le revision);
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
|
|
@ -7,8 +7,17 @@
|
|||
|
||||
namespace Service::Audio {
|
||||
|
||||
constexpr Result ERR_INVALID_DEVICE_NAME{ErrorModule::Audio, 1};
|
||||
constexpr Result ERR_OPERATION_FAILED{ErrorModule::Audio, 2};
|
||||
constexpr Result ERR_INVALID_SAMPLE_RATE{ErrorModule::Audio, 3};
|
||||
constexpr Result ERR_INSUFFICIENT_BUFFER_SIZE{ErrorModule::Audio, 4};
|
||||
constexpr Result ERR_MAXIMUM_SESSIONS_REACHED{ErrorModule::Audio, 5};
|
||||
constexpr Result ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8};
|
||||
constexpr Result ERR_INVALID_CHANNEL_COUNT{ErrorModule::Audio, 10};
|
||||
constexpr Result ERR_INVALID_UPDATE_DATA{ErrorModule::Audio, 41};
|
||||
constexpr Result ERR_POOL_MAPPING_FAILED{ErrorModule::Audio, 42};
|
||||
constexpr Result ERR_NOT_SUPPORTED{ErrorModule::Audio, 513};
|
||||
constexpr Result ERR_INVALID_PROCESS_HANDLE{ErrorModule::Audio, 1536};
|
||||
constexpr Result ERR_INVALID_REVISION{ErrorModule::Audio, 1537};
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
|
|
@ -298,7 +298,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx) {
|
|||
const auto sample_rate = rp.Pop<u32>();
|
||||
const auto channel_count = rp.Pop<u32>();
|
||||
|
||||
LOG_CRITICAL(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count);
|
||||
LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count);
|
||||
|
||||
ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
|
||||
sample_rate == 12000 || sample_rate == 8000,
|
||||
|
|
|
@ -511,7 +511,7 @@ struct Memory::Impl {
|
|||
|
||||
[[nodiscard]] u8* GetPointerImpl(VAddr vaddr, auto on_unmapped, auto on_rasterizer) const {
|
||||
// AARCH64 masks the upper 16 bit of all memory accesses
|
||||
vaddr &= 0xffffffffffffLL;
|
||||
vaddr &= 0xffffffffffffULL;
|
||||
|
||||
if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
|
||||
on_unmapped();
|
||||
|
@ -776,6 +776,10 @@ void Memory::CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr s
|
|||
impl->CopyBlock(process, dest_addr, src_addr, size);
|
||||
}
|
||||
|
||||
void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, const std::size_t size) {
|
||||
impl->ZeroBlock(process, dest_addr, size);
|
||||
}
|
||||
|
||||
void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
|
||||
impl->RasterizerMarkRegionCached(vaddr, size, cached);
|
||||
}
|
||||
|
|
|
@ -436,6 +436,19 @@ public:
|
|||
void CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr,
|
||||
std::size_t size);
|
||||
|
||||
/**
|
||||
* Zeros a range of bytes within the current process' address space at the specified
|
||||
* virtual address.
|
||||
*
|
||||
* @param process The process that will have data zeroed within its address space.
|
||||
* @param dest_addr The destination virtual address to zero the data from.
|
||||
* @param size The size of the range to zero out, in bytes.
|
||||
*
|
||||
* @post The range [dest_addr, size) within the process' address space contains the
|
||||
* value 0.
|
||||
*/
|
||||
void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
|
||||
|
||||
/**
|
||||
* Marks each page within the specified address range as cached or uncached.
|
||||
*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue