IAudioOutManager.cs 5.5 KB

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