Project Andio

This commit is contained in:
Kelebek1 2022-07-16 23:48:45 +01:00
parent 6e36f4d230
commit 458da8a948
270 changed files with 33712 additions and 8445 deletions

View file

@ -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;
}

View file

@ -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();

View file

@ -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()};

View file

@ -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

View file

@ -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;
}

View file

@ -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;

View file

@ -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>();

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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);
}

View file

@ -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.
*