IAudioOutManager.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. using Ryujinx.Audio;
  2. using Ryujinx.Common.Logging;
  3. using Ryujinx.Cpu;
  4. using Ryujinx.HLE.HOS.Kernel.Threading;
  5. using Ryujinx.HLE.HOS.Services.Audio.AudioOutManager;
  6. using System.Text;
  7. namespace Ryujinx.HLE.HOS.Services.Audio
  8. {
  9. [Service("audout:u")]
  10. class IAudioOutManager : IpcService
  11. {
  12. private const string DefaultAudioOutput = "DeviceOut";
  13. private const int DefaultSampleRate = 48000;
  14. private const int DefaultChannelsCount = 2;
  15. public IAudioOutManager(ServiceCtx context) : base(context.Device.System.AudOutServer) { }
  16. [Command(0)]
  17. // ListAudioOuts() -> (u32 count, buffer<bytes, 6>)
  18. public ResultCode ListAudioOuts(ServiceCtx context)
  19. {
  20. return ListAudioOutsImpl(context, context.Request.ReceiveBuff[0].Position, context.Request.ReceiveBuff[0].Size);
  21. }
  22. [Command(1)]
  23. // OpenAudioOut(u32 sample_rate, u16 unused, u16 channel_count, nn::applet::AppletResourceUserId, pid, handle<copy, process>, buffer<bytes, 5> name_in)
  24. // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioOut>, buffer<bytes, 6> name_out)
  25. public ResultCode OpenAudioOut(ServiceCtx context)
  26. {
  27. return OpenAudioOutImpl(context, context.Request.SendBuff[0].Position, context.Request.SendBuff[0].Size,
  28. context.Request.ReceiveBuff[0].Position, context.Request.ReceiveBuff[0].Size);
  29. }
  30. [Command(2)] // 3.0.0+
  31. // ListAudioOutsAuto() -> (u32 count, buffer<bytes, 0x22>)
  32. public ResultCode ListAudioOutsAuto(ServiceCtx context)
  33. {
  34. (long recvPosition, long recvSize) = context.Request.GetBufferType0x22();
  35. return ListAudioOutsImpl(context, recvPosition, recvSize);
  36. }
  37. [Command(3)] // 3.0.0+
  38. // OpenAudioOutAuto(u32 sample_rate, u16 unused, u16 channel_count, nn::applet::AppletResourceUserId, pid, handle<copy, process>, buffer<bytes, 0x21>)
  39. // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioOut>, buffer<bytes, 0x22> name_out)
  40. public ResultCode OpenAudioOutAuto(ServiceCtx context)
  41. {
  42. (long sendPosition, long sendSize) = context.Request.GetBufferType0x21();
  43. (long recvPosition, long recvSize) = context.Request.GetBufferType0x22();
  44. return OpenAudioOutImpl(context, sendPosition, sendSize, recvPosition, recvSize);
  45. }
  46. private ResultCode ListAudioOutsImpl(ServiceCtx context, long position, long size)
  47. {
  48. int nameCount = 0;
  49. byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(DefaultAudioOutput + "\0");
  50. if ((ulong)deviceNameBuffer.Length <= (ulong)size)
  51. {
  52. context.Memory.Write((ulong)position, deviceNameBuffer);
  53. nameCount++;
  54. }
  55. else
  56. {
  57. Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
  58. }
  59. context.ResponseData.Write(nameCount);
  60. return ResultCode.Success;
  61. }
  62. private ResultCode OpenAudioOutImpl(ServiceCtx context, long sendPosition, long sendSize, long receivePosition, long receiveSize)
  63. {
  64. string deviceName = MemoryHelper.ReadAsciiString(context.Memory, sendPosition, sendSize);
  65. if (deviceName == string.Empty)
  66. {
  67. deviceName = DefaultAudioOutput;
  68. }
  69. if (deviceName != DefaultAudioOutput)
  70. {
  71. Logger.Warning?.Print(LogClass.Audio, "Invalid device name!");
  72. return ResultCode.DeviceNotFound;
  73. }
  74. byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(deviceName + "\0");
  75. if ((ulong)deviceNameBuffer.Length <= (ulong)receiveSize)
  76. {
  77. context.Memory.Write((ulong)receivePosition, deviceNameBuffer);
  78. }
  79. else
  80. {
  81. Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {receiveSize} too small!");
  82. }
  83. int sampleRate = context.RequestData.ReadInt32();
  84. int channels = context.RequestData.ReadInt32();
  85. if (sampleRate == 0)
  86. {
  87. sampleRate = DefaultSampleRate;
  88. }
  89. if (sampleRate != DefaultSampleRate)
  90. {
  91. Logger.Warning?.Print(LogClass.Audio, "Invalid sample rate!");
  92. return ResultCode.UnsupportedSampleRate;
  93. }
  94. channels = (ushort)channels;
  95. if (channels == 0)
  96. {
  97. channels = DefaultChannelsCount;
  98. }
  99. KEvent releaseEvent = new KEvent(context.Device.System.KernelContext);
  100. ReleaseCallback callback = () =>
  101. {
  102. releaseEvent.ReadableEvent.Signal();
  103. };
  104. IAalOutput audioOut = context.Device.AudioOut;
  105. int track = audioOut.OpenTrack(sampleRate, channels, callback);
  106. MakeObject(context, new IAudioOut(context.Device.System.KernelContext, audioOut, releaseEvent, track, context.Request.HandleDesc.ToCopy[0]));
  107. context.ResponseData.Write(sampleRate);
  108. context.ResponseData.Write(channels);
  109. context.ResponseData.Write((int)SampleFormat.PcmInt16);
  110. context.ResponseData.Write((int)PlaybackState.Stopped);
  111. return ResultCode.Success;
  112. }
  113. }
  114. }