From a1ddefd3ca099c4e72bf8d40e32b4fada20ce6d3 Mon Sep 17 00:00:00 2001 From: plutoo Date: Sun, 30 May 2021 17:33:11 -0700 Subject: [PATCH] audctl: Initial commit --- nx/include/switch/services/audctl.h | 65 ++++++ nx/source/services/audctl.c | 342 ++++++++++++++++++++++++++++ 2 files changed, 407 insertions(+) create mode 100644 nx/include/switch/services/audctl.h create mode 100644 nx/source/services/audctl.c diff --git a/nx/include/switch/services/audctl.h b/nx/include/switch/services/audctl.h new file mode 100644 index 00000000..9995b7a7 --- /dev/null +++ b/nx/include/switch/services/audctl.h @@ -0,0 +1,65 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + AudioTarget_Invalid = 0, + AudioTarget_Speaker = 1, + AudioTarget_Headphone = 2, + AudioTarget_Tv = 3, + AudioTarget_UsbOutputDevice = 4, +} AudioTarget; + +typedef enum { + AudioOutputMode_Invalid = 0, + AudioOutputMode_Pcm1ch = 1, + AudioOutputMode_Pcm2ch = 2, + AudioOutputMode_Pcm6ch = 3, + AudioOutputMode_PcmAuto = 4, +} AudioOutputMode; + +typedef enum { + AudioForceMutePolicy_Disable = 0, + AudioForceMutePolicy_SpeakerMuteOnHeadphoneUnplugged = 1, +} AudioForceMutePolicy; + +typedef enum { + HeadphoneOutputLevelMode_Normal = 0, + HeadphoneOutputLevelMode_HighPower = 1, +} HeadphoneOutputLevelMode; + +Result audctlInitialize(void); +void audctlExit(void); +Service* audctlGetServiceSession(void); + +Result audctlGetTargetVolume(float* volume_out, AudioTarget target); +Result audctlSetTargetVolume(AudioTarget target, float volume); +Result audctlGetTargetVolumeMin(float* volume_out); +Result audctlGetTargetVolumeMax(float* volume_out); +Result audctlIsTargetMute(bool* mute_out, AudioTarget target); +Result audctlSetTargetMute(AudioTarget target, bool mute); +Result audctlIsTargetConnected(bool* connected_out, AudioTarget target); +Result audctlSetDefaultTarget(AudioTarget target, u64 fade_in_ns, u64 fade_out_ns); +Result audctlGetDefaultTarget(AudioTarget* target_out); +Result audctlGetAudioOutputMode(AudioOutputMode* mode_out, AudioTarget target); +Result audctlSetAudioOutputMode(AudioTarget target, AudioOutputMode mode); +Result audctlSetForceMutePolicy(AudioForceMutePolicy policy); +Result audctlGetForceMutePolicy(AudioForceMutePolicy* policy_out); +Result audctlGetOutputModeSetting(AudioOutputMode* mode_out, AudioTarget target); +Result audctlSetOutputModeSetting(AudioTarget target, AudioOutputMode mode); +Result audctlSetOutputTarget(AudioTarget target); +Result audctlSetInputTargetForceEnabled(bool enable); +Result audctlSetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode mode); +Result audctlGetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode* mode_out); +Result audctlAcquireAudioVolumeUpdateEventForPlayReport(Event* event_out); +Result audctlAcquireAudioOutputDeviceUpdateEventForPlayReport(Event* event_out); +Result audctlGetAudioOutputTargetForPlayReport(AudioTarget* target_out); +Result audctlNotifyHeadphoneVolumeWarningDisplayedEvent(void); +Result audctlSetSystemOutputMasterVolume(float volume); +Result audctlGetSystemOutputMasterVolume(float* volume_out); + +#ifdef __cplusplus +}; +#endif diff --git a/nx/source/services/audctl.c b/nx/source/services/audctl.c new file mode 100644 index 00000000..d2b8a5f1 --- /dev/null +++ b/nx/source/services/audctl.c @@ -0,0 +1,342 @@ +#define NX_SERVICE_ASSUME_NON_DOMAIN +#include "service_guard.h" +#include "kernel/event.h" +#include "runtime/hosversion.h" +#include "services/audctl.h" + +NX_GENERATE_SERVICE_GUARD(audctl); + +static Service g_audctlSrv; + +Result _audctlInitialize(void) { + return smGetService(&g_audctlSrv, "audctl"); +} + +void _audctlCleanup(void) { + serviceClose(&g_audctlSrv); +} + +Service* audctlGetServiceSession(void) { + return &g_audctlSrv; +} + +Result audctlGetTargetVolume(float* volume_out, AudioTarget target) { + const struct { + u32 target; + } in = { target }; + + struct { + float volume; + } out; + + Result rc = serviceDispatchInOut(&g_audctlSrv, 0, in, out); + + if (R_SUCCEEDED(rc)) { + *volume_out = out.volume; + } + return rc; +} + +Result audctlSetTargetVolume(AudioTarget target, float volume) { + const struct { + u32 target; + float volume; + } in = { target, volume }; + + return serviceDispatchIn(&g_audctlSrv, 1, in); +} + +Result audctlGetTargetVolumeMin(float* volume_out) { + struct { + float volume; + } out; + + Result rc = serviceDispatchOut(&g_audctlSrv, 2, out); + + if (R_SUCCEEDED(rc)) { + *volume_out = out.volume; + } + return rc; +} + +Result audctlGetTargetVolumeMax(float* volume_out) { + struct { + float volume; + } out; + + Result rc = serviceDispatchOut(&g_audctlSrv, 3, out); + + if (R_SUCCEEDED(rc)) { + *volume_out = out.volume; + } + return rc; +} + +Result audctlIsTargetMute(bool* mute_out, AudioTarget target) { + const struct { + u32 target; + } in = { target }; + + struct { + u8 mute; + } out; + + Result rc = serviceDispatchInOut(&g_audctlSrv, 4, in, out); + + if (R_SUCCEEDED(rc)) { + *mute_out = out.mute; + } + return rc; +} + +Result audctlSetTargetMute(AudioTarget target, bool mute) { + const struct { + u32 mute; + u32 target; + } in = { mute, target }; + + return serviceDispatchIn(&g_audctlSrv, 5, in); +} + +Result audctlIsTargetConnected(bool* connected_out, AudioTarget target) { + const struct { + u32 target; + } in = { target }; + + struct { + u8 connected; + } out; + + Result rc = serviceDispatchInOut(&g_audctlSrv, 6, in, out); + + if (R_SUCCEEDED(rc)) { + *connected_out = out.connected; + } + return rc; +} + +Result audctlSetDefaultTarget(AudioTarget target, u64 fade_in_ns, u64 fade_out_ns) { + const struct { + u32 target; + u32 padding; + u64 fade_in_ns; + u64 fade_out_ns; + } in = { target, 0, fade_in_ns, fade_out_ns }; + + return serviceDispatchIn(&g_audctlSrv, 7, in); +} + +Result audctlGetDefaultTarget(AudioTarget* target_out) { + struct { + u32 target; + } out; + + Result rc = serviceDispatchOut(&g_audctlSrv, 8, out); + + if (R_SUCCEEDED(rc)) { + *target_out = out.target; + } + return rc; +} + +Result audctlGetAudioOutputMode(AudioOutputMode* mode_out, AudioTarget target) { + const struct { + u32 target; + } in = { target }; + + struct { + u32 mode; + } out; + + Result rc = serviceDispatchInOut(&g_audctlSrv, 9, in, out); + + if (R_SUCCEEDED(rc)) { + *mode_out = out.mode; + } + return rc; +} + +Result audctlSetAudioOutputMode(AudioTarget target, AudioOutputMode mode) { + const struct { + u32 target; + u32 mode; + } in = { target, mode }; + + return serviceDispatchIn(&g_audctlSrv, 10, in); +} + +Result audctlSetForceMutePolicy(AudioForceMutePolicy policy) { + const struct { + u32 policy; + } in = { policy }; + + return serviceDispatchIn(&g_audctlSrv, 11, in); +} + +Result audctlGetForceMutePolicy(AudioForceMutePolicy* policy_out) { + struct { + u32 policy; + } out; + + Result rc = serviceDispatchOut(&g_audctlSrv, 12, out); + + if (R_SUCCEEDED(rc)) { + *policy_out = out.policy; + } + return rc; +} + +Result audctlGetOutputModeSetting(AudioOutputMode* mode_out, AudioTarget target) { + const struct { + u32 target; + } in = { target }; + + struct { + u32 mode; + } out; + + Result rc = serviceDispatchInOut(&g_audctlSrv, 13, in, out); + + if (R_SUCCEEDED(rc)) { + *mode_out = out.mode; + } + return rc; +} + +Result audctlSetOutputModeSetting(AudioTarget target, AudioOutputMode mode) { + const struct { + u32 target; + u32 mode; + } in = { target, mode }; + + return serviceDispatchIn(&g_audctlSrv, 14, in); +} + +Result audctlSetOutputTarget(AudioTarget target) { + const struct { + u32 target; + } in = { target }; + + return serviceDispatchIn(&g_audctlSrv, 15, in); +} + +Result audctlSetInputTargetForceEnabled(bool enable) { + const struct { + bool enable; + } in = { enable }; + + return serviceDispatchIn(&g_audctlSrv, 16, in); +} + +Result audctlSetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode mode) { + if (hosversionBefore(3,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + const struct { + u32 mode; + } in = { mode }; + + return serviceDispatchIn(&g_audctlSrv, 17, in); +} + +Result audctlGetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode* mode_out) { + if (hosversionBefore(3,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + struct { + u32 mode; + } out; + + Result rc = serviceDispatchOut(&g_audctlSrv, 18, out); + + if (R_SUCCEEDED(rc)) { + *mode_out = out.mode; + } + return rc; +} + +Result audctlAcquireAudioVolumeUpdateEventForPlayReport(Event* event_out) { + if (hosversionBefore(3,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + Handle tmp_handle; + + Result rc = serviceDispatch(&g_audctlSrv, 19, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = &tmp_handle, + ); + + if (R_SUCCEEDED(rc)) { + eventLoadRemote(event_out, tmp_handle, 1); + } + + return rc; +} + +Result audctlAcquireAudioOutputDeviceUpdateEventForPlayReport(Event* event_out) { + if (hosversionBefore(3,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + Handle tmp_handle; + + Result rc = serviceDispatch(&g_audctlSrv, 20, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = &tmp_handle, + ); + + if (R_SUCCEEDED(rc)) { + eventLoadRemote(event_out, tmp_handle, 1); + } + + return rc; +} + +Result audctlGetAudioOutputTargetForPlayReport(AudioTarget* target_out) { + if (hosversionBefore(3,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + struct { + u32 target; + } out; + + Result rc = serviceDispatchOut(&g_audctlSrv, 21, out); + + if (R_SUCCEEDED(rc)) { + *target_out = out.target; + } + return rc; +} + +Result audctlNotifyHeadphoneVolumeWarningDisplayedEvent(void) { + if (hosversionBefore(3,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return serviceDispatch(&g_audctlSrv, 22); +} + +Result audctlSetSystemOutputMasterVolume(float volume) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + const struct { + float volume; + } in = { volume }; + + return serviceDispatchIn(&g_audctlSrv, 23, in); +} + +Result audctlGetSystemOutputMasterVolume(float* volume_out) { + if (hosversionBefore(4,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + struct { + float volume; + } out; + + Result rc = serviceDispatchOut(&g_audctlSrv, 24, out); + + if (R_SUCCEEDED(rc)) { + *volume_out = out.volume; + } + return rc; +}