using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.SystemState; using System; using System.Collections.Generic; using System.Text; namespace Ryujinx.HLE.HOS.Services.Aud { class IAudioDevice : IpcService { private Dictionary _commands; public override IReadOnlyDictionary Commands => _commands; private KEvent _systemEvent; public IAudioDevice(Horizon system) { _commands = new Dictionary { { 0, ListAudioDeviceName }, { 1, SetAudioDeviceOutputVolume }, { 3, GetActiveAudioDeviceName }, { 4, QueryAudioDeviceSystemEvent }, { 5, GetActiveChannelCount }, { 6, ListAudioDeviceNameAuto }, { 7, SetAudioDeviceOutputVolumeAuto }, { 8, GetAudioDeviceOutputVolumeAuto }, { 10, GetActiveAudioDeviceNameAuto }, { 11, QueryAudioDeviceInputEvent }, { 12, QueryAudioDeviceOutputEvent } }; _systemEvent = new KEvent(system); //TODO: We shouldn't be signaling this here. _systemEvent.ReadableEvent.Signal(); } public long ListAudioDeviceName(ServiceCtx context) { string[] deviceNames = SystemStateMgr.AudioOutputs; context.ResponseData.Write(deviceNames.Length); long position = context.Request.ReceiveBuff[0].Position; long size = context.Request.ReceiveBuff[0].Size; long basePosition = position; foreach (string name in deviceNames) { byte[] buffer = Encoding.ASCII.GetBytes(name + "\0"); if ((position - basePosition) + buffer.Length > size) { Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); break; } context.Memory.WriteBytes(position, buffer); position += buffer.Length; } return 0; } public long SetAudioDeviceOutputVolume(ServiceCtx context) { float volume = context.RequestData.ReadSingle(); long position = context.Request.SendBuff[0].Position; long size = context.Request.SendBuff[0].Size; byte[] deviceNameBuffer = context.Memory.ReadBytes(position, size); string deviceName = Encoding.ASCII.GetString(deviceNameBuffer); Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } public long GetActiveAudioDeviceName(ServiceCtx context) { string name = context.Device.System.State.ActiveAudioOutput; long position = context.Request.ReceiveBuff[0].Position; long size = context.Request.ReceiveBuff[0].Size; byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(name + "\0"); if ((ulong)deviceNameBuffer.Length <= (ulong)size) { context.Memory.WriteBytes(position, deviceNameBuffer); } else { Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); } return 0; } public long QueryAudioDeviceSystemEvent(ServiceCtx context) { if (context.Process.HandleTable.GenerateHandle(_systemEvent.ReadableEvent, out int handle) != KernelResult.Success) { throw new InvalidOperationException("Out of handles!"); } context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } public long GetActiveChannelCount(ServiceCtx context) { context.ResponseData.Write(2); Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } public long ListAudioDeviceNameAuto(ServiceCtx context) { string[] deviceNames = SystemStateMgr.AudioOutputs; context.ResponseData.Write(deviceNames.Length); (long position, long size) = context.Request.GetBufferType0x22(); long basePosition = position; foreach (string name in deviceNames) { byte[] buffer = Encoding.UTF8.GetBytes(name + '\0'); if ((position - basePosition) + buffer.Length > size) { Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); break; } context.Memory.WriteBytes(position, buffer); position += buffer.Length; } return 0; } public long SetAudioDeviceOutputVolumeAuto(ServiceCtx context) { float volume = context.RequestData.ReadSingle(); (long position, long size) = context.Request.GetBufferType0x21(); byte[] deviceNameBuffer = context.Memory.ReadBytes(position, size); string deviceName = Encoding.UTF8.GetString(deviceNameBuffer); Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } public long GetAudioDeviceOutputVolumeAuto(ServiceCtx context) { context.ResponseData.Write(1f); Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } public long GetActiveAudioDeviceNameAuto(ServiceCtx context) { string name = context.Device.System.State.ActiveAudioOutput; (long position, long size) = context.Request.GetBufferType0x22(); byte[] deviceNameBuffer = Encoding.UTF8.GetBytes(name + '\0'); if ((ulong)deviceNameBuffer.Length <= (ulong)size) { context.Memory.WriteBytes(position, deviceNameBuffer); } else { Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); } return 0; } public long QueryAudioDeviceInputEvent(ServiceCtx context) { if (context.Process.HandleTable.GenerateHandle(_systemEvent.ReadableEvent, out int handle) != KernelResult.Success) { throw new InvalidOperationException("Out of handles!"); } context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } public long QueryAudioDeviceOutputEvent(ServiceCtx context) { if (context.Process.HandleTable.GenerateHandle(_systemEvent.ReadableEvent, out int handle) != KernelResult.Success) { throw new InvalidOperationException("Out of handles!"); } context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } } }