#include #include "types.h" #include "result.h" #include "arm/atomics.h" #include "kernel/ipc.h" #include "kernel/tmem.h" #include "kernel/event.h" #include "services/sm.h" #include "services/applet.h" #include "services/audren.h" u32 g_audrenRevision; static Service g_audrenIAudioRenderer; static TransferMemory g_audrenWorkBuf; static Event g_audrenEvent; typedef struct { s32 sample_rate; s32 sample_count; s32 mix_buffer_count; s32 submix_count; s32 voice_count; s32 sink_count; s32 effect_count; s32 unk1; u8 unk2; u8 _padding1[3]; s32 splitter_count; s32 unk3; s32 unk4; u32 revision; } AudioRendererParameter; static Result _audrenOpenAudioRenderer(Service* audren_mgr, const AudioRendererParameter* param, u64 aruid); static Result _audrenGetWorkBufferSize(Service* audren_mgr, const AudioRendererParameter* param, u64* out_size); static Result _audrenQuerySystemEvent(void); Result audrenInitialize(const AudioRendererConfig* config) { Result rc; if (serviceIsActive(&g_audrenIAudioRenderer)) return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized); // Validate configuration if (config->num_voices < 1 || config->num_voices > 1024) return MAKERESULT(Module_Libnx, LibnxError_BadInput); if (config->num_effects < 0 || config->num_effects > 256) return MAKERESULT(Module_Libnx, LibnxError_BadInput); if (config->num_sinks < 1 || config->num_sinks > 256) return MAKERESULT(Module_Libnx, LibnxError_BadInput); if (config->num_mix_objs < 1 || config->num_mix_objs > 256) return MAKERESULT(Module_Libnx, LibnxError_BadInput); if (config->num_mix_buffers < 1 || config->num_mix_buffers > 256) return MAKERESULT(Module_Libnx, LibnxError_BadInput); // Choose revision (i.e. if splitters are used then at least revision 2 must be used) // (TODO: abstract away differences between revisions according to provided parameters) g_audrenRevision = AUDREN_REVISION_3; // todo: ?!? // Prepare parameter structure AudioRendererParameter param = {0}; param.sample_rate = config->output_rate == AudioRendererOutputRate_32kHz ? 32000 : 48000; param.sample_count = config->output_rate == AudioRendererOutputRate_32kHz ? AUDREN_SAMPLES_PER_FRAME_32KHZ : AUDREN_SAMPLES_PER_FRAME_48KHZ; param.mix_buffer_count = config->num_mix_buffers; param.submix_count = config->num_mix_objs - 1; param.voice_count = config->num_voices; param.sink_count = config->num_sinks; param.effect_count = config->num_effects; param.revision = g_audrenRevision; // Get aruid u64 aruid = 0; rc = appletGetAppletResourceUserId(&aruid); //if (R_FAILED(rc)) return rc; // apparently audren still inits fine with aruid = 0 so this isn't a fatal error condition // Open IAudioRendererManager Service audrenMgrSrv; rc = smGetService(&audrenMgrSrv, "audren:u"); if (R_SUCCEEDED(rc)) { // Get required work buffer size size_t workBufSize = 0; rc = _audrenGetWorkBufferSize(&audrenMgrSrv, ¶m, &workBufSize); if (R_SUCCEEDED(rc)) { // Create transfermem work buffer object workBufSize = (workBufSize + 0xFFF) &~ 0xFFF; // 1.x fails hard and returns a non-page-aligned work buffer size rc = tmemCreate(&g_audrenWorkBuf, workBufSize, Perm_None); if (R_SUCCEEDED(rc)) { // Create the IAudioRenderer service rc = _audrenOpenAudioRenderer(&audrenMgrSrv, ¶m, aruid); if (R_SUCCEEDED(rc)) { // Finally, get the handle to the system event rc = _audrenQuerySystemEvent(); if (R_FAILED(rc)) serviceClose(&g_audrenIAudioRenderer); } if (R_FAILED(rc)) tmemClose(&g_audrenWorkBuf); } } serviceClose(&audrenMgrSrv); } return rc; } void audrenExit(void) { if (!serviceIsActive(&g_audrenIAudioRenderer)) return; eventClose(&g_audrenEvent); serviceClose(&g_audrenIAudioRenderer); tmemClose(&g_audrenWorkBuf); } void audrenWaitFrame(void) { eventWait(&g_audrenEvent, U64_MAX); } Result _audrenOpenAudioRenderer(Service* audren_mgr, const AudioRendererParameter* param, u64 aruid) { IpcCommand c; ipcInitialize(&c); struct { u64 magic; u64 cmd_id; AudioRendererParameter param; u64 work_buffer_size; u64 aruid; } *raw; ipcSendPid(&c); ipcSendHandleCopy(&c, g_audrenWorkBuf.handle); ipcSendHandleCopy(&c, CUR_PROCESS_HANDLE); raw = ipcPrepareHeader(&c, sizeof(*raw)); raw->magic = SFCI_MAGIC; raw->cmd_id = 0; raw->param = *param; raw->work_buffer_size = g_audrenWorkBuf.size; raw->aruid = aruid; Result rc = serviceIpcDispatch(audren_mgr); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; ipcParse(&r); struct { u64 magic; u64 result; } *resp = r.Raw; rc = resp->result; if (R_SUCCEEDED(rc)) serviceCreate(&g_audrenIAudioRenderer, r.Handles[0]); } return rc; } Result _audrenGetWorkBufferSize(Service* audren_mgr, const AudioRendererParameter* param, size_t* out_size) { IpcCommand c; ipcInitialize(&c); struct { u64 magic; u64 cmd_id; AudioRendererParameter param; } *raw; raw = ipcPrepareHeader(&c, sizeof(*raw)); raw->magic = SFCI_MAGIC; raw->cmd_id = 1; raw->param = *param; Result rc = serviceIpcDispatch(audren_mgr); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; ipcParse(&r); struct { u64 magic; u64 result; u64 work_buf_size; } *resp = r.Raw; rc = resp->result; if (R_SUCCEEDED(rc) && out_size) *out_size = resp->work_buf_size; } return rc; } Result audrenGetState(u32* out_state) { IpcCommand c; ipcInitialize(&c); struct { u64 magic; u64 cmd_id; } *raw; raw = ipcPrepareHeader(&c, sizeof(*raw)); raw->magic = SFCI_MAGIC; raw->cmd_id = 3; Result rc = serviceIpcDispatch(&g_audrenIAudioRenderer); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; ipcParse(&r); struct { u64 magic; u64 result; u32 state; } *resp = r.Raw; rc = resp->result; if (R_SUCCEEDED(rc) && out_state) *out_state = resp->state; } return rc; } Result audrenRequestUpdateAudioRenderer(const void* in_param_buf, size_t in_param_buf_size, void* out_param_buf, size_t out_param_buf_size, void* perf_buf, size_t perf_buf_size) { IpcCommand c; ipcInitialize(&c); struct { u64 magic; u64 cmd_id; } *raw; ipcAddSendBuffer(&c, in_param_buf, in_param_buf_size, BufferType_Normal); ipcAddRecvBuffer(&c, out_param_buf, out_param_buf_size, BufferType_Normal); ipcAddRecvBuffer(&c, perf_buf, perf_buf_size, BufferType_Normal); raw = ipcPrepareHeader(&c, sizeof(*raw)); raw->magic = SFCI_MAGIC; raw->cmd_id = 4; Result rc = serviceIpcDispatch(&g_audrenIAudioRenderer); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; ipcParse(&r); struct { u64 magic; u64 result; } *resp = r.Raw; rc = resp->result; } return rc; } Result audrenStartAudioRenderer(void) { IpcCommand c; ipcInitialize(&c); struct { u64 magic; u64 cmd_id; } *raw; raw = ipcPrepareHeader(&c, sizeof(*raw)); raw->magic = SFCI_MAGIC; raw->cmd_id = 5; Result rc = serviceIpcDispatch(&g_audrenIAudioRenderer); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; ipcParse(&r); struct { u64 magic; u64 result; } *resp = r.Raw; rc = resp->result; } return rc; } Result audrenStopAudioRenderer(void) { IpcCommand c; ipcInitialize(&c); struct { u64 magic; u64 cmd_id; } *raw; raw = ipcPrepareHeader(&c, sizeof(*raw)); raw->magic = SFCI_MAGIC; raw->cmd_id = 6; Result rc = serviceIpcDispatch(&g_audrenIAudioRenderer); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; ipcParse(&r); struct { u64 magic; u64 result; } *resp = r.Raw; rc = resp->result; } return rc; } Result _audrenQuerySystemEvent(void) { IpcCommand c; ipcInitialize(&c); struct { u64 magic; u64 cmd_id; } *raw; raw = ipcPrepareHeader(&c, sizeof(*raw)); raw->magic = SFCI_MAGIC; raw->cmd_id = 7; Result rc = serviceIpcDispatch(&g_audrenIAudioRenderer); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; ipcParse(&r); struct { u64 magic; u64 result; } *resp = r.Raw; rc = resp->result; if (R_SUCCEEDED(rc)) eventLoadRemote(&g_audrenEvent, r.Handles[0], true); } return rc; } Result audrenSetAudioRendererRenderingTimeLimit(int percent) { IpcCommand c; ipcInitialize(&c); struct { u64 magic; u64 cmd_id; int percent; } *raw; raw = ipcPrepareHeader(&c, sizeof(*raw)); raw->magic = SFCI_MAGIC; raw->cmd_id = 8; raw->percent = percent; Result rc = serviceIpcDispatch(&g_audrenIAudioRenderer); if (R_SUCCEEDED(rc)) { IpcParsedCommand r; ipcParse(&r); struct { u64 magic; u64 result; } *resp = r.Raw; rc = resp->result; } return rc; }