IAudioOut.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. using Ryujinx.Audio;
  2. using Ryujinx.Cpu;
  3. using Ryujinx.HLE.HOS.Ipc;
  4. using Ryujinx.HLE.HOS.Kernel;
  5. using Ryujinx.HLE.HOS.Kernel.Common;
  6. using Ryujinx.HLE.HOS.Kernel.Threading;
  7. using System;
  8. using System.Runtime.InteropServices;
  9. namespace Ryujinx.HLE.HOS.Services.Audio.AudioOutManager
  10. {
  11. class IAudioOut : IpcService, IDisposable
  12. {
  13. private readonly KernelContext _kernelContext;
  14. private readonly IAalOutput _audioOut;
  15. private readonly KEvent _releaseEvent;
  16. private int _releaseEventHandle;
  17. private readonly int _track;
  18. private readonly int _clientHandle;
  19. public IAudioOut(KernelContext kernelContext, IAalOutput audioOut, KEvent releaseEvent, int track, int clientHandle)
  20. {
  21. _kernelContext = kernelContext;
  22. _audioOut = audioOut;
  23. _releaseEvent = releaseEvent;
  24. _track = track;
  25. _clientHandle = clientHandle;
  26. }
  27. [Command(0)]
  28. // GetAudioOutState() -> u32 state
  29. public ResultCode GetAudioOutState(ServiceCtx context)
  30. {
  31. context.ResponseData.Write((int)_audioOut.GetState(_track));
  32. return ResultCode.Success;
  33. }
  34. [Command(1)]
  35. // StartAudioOut()
  36. public ResultCode StartAudioOut(ServiceCtx context)
  37. {
  38. _audioOut.Start(_track);
  39. return ResultCode.Success;
  40. }
  41. [Command(2)]
  42. // StopAudioOut()
  43. public ResultCode StopAudioOut(ServiceCtx context)
  44. {
  45. _audioOut.Stop(_track);
  46. return ResultCode.Success;
  47. }
  48. [Command(3)]
  49. // AppendAudioOutBuffer(u64 tag, buffer<nn::audio::AudioOutBuffer, 5>)
  50. public ResultCode AppendAudioOutBuffer(ServiceCtx context)
  51. {
  52. return AppendAudioOutBufferImpl(context, context.Request.SendBuff[0].Position);
  53. }
  54. [Command(4)]
  55. // RegisterBufferEvent() -> handle<copy>
  56. public ResultCode RegisterBufferEvent(ServiceCtx context)
  57. {
  58. if (_releaseEventHandle == 0)
  59. {
  60. if (context.Process.HandleTable.GenerateHandle(_releaseEvent.ReadableEvent, out _releaseEventHandle) != KernelResult.Success)
  61. {
  62. throw new InvalidOperationException("Out of handles!");
  63. }
  64. }
  65. context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_releaseEventHandle);
  66. return ResultCode.Success;
  67. }
  68. [Command(5)]
  69. // GetReleasedAudioOutBuffer() -> (u32 count, buffer<nn::audio::AudioOutBuffer, 6>)
  70. public ResultCode GetReleasedAudioOutBuffer(ServiceCtx context)
  71. {
  72. long position = context.Request.ReceiveBuff[0].Position;
  73. long size = context.Request.ReceiveBuff[0].Size;
  74. return GetReleasedAudioOutBufferImpl(context, position, size);
  75. }
  76. [Command(6)]
  77. // ContainsAudioOutBuffer(u64 tag) -> b8
  78. public ResultCode ContainsAudioOutBuffer(ServiceCtx context)
  79. {
  80. long tag = context.RequestData.ReadInt64();
  81. context.ResponseData.Write(_audioOut.ContainsBuffer(_track, tag));
  82. return ResultCode.Success;
  83. }
  84. [Command(7)] // 3.0.0+
  85. // AppendAudioOutBufferAuto(u64 tag, buffer<nn::audio::AudioOutBuffer, 0x21>)
  86. public ResultCode AppendAudioOutBufferAuto(ServiceCtx context)
  87. {
  88. (long position, _) = context.Request.GetBufferType0x21();
  89. return AppendAudioOutBufferImpl(context, position);
  90. }
  91. public ResultCode AppendAudioOutBufferImpl(ServiceCtx context, long position)
  92. {
  93. long tag = context.RequestData.ReadInt64();
  94. AudioOutData data = MemoryHelper.Read<AudioOutData>(context.Memory, position);
  95. // NOTE: Assume PCM16 all the time, change if new format are found.
  96. short[] buffer = new short[data.SampleBufferSize / sizeof(short)];
  97. context.Process.HandleTable.GetKProcess(_clientHandle).CpuMemory.Read((ulong)data.SampleBufferPtr, MemoryMarshal.Cast<short, byte>(buffer));
  98. _audioOut.AppendBuffer(_track, tag, buffer);
  99. return ResultCode.Success;
  100. }
  101. [Command(8)] // 3.0.0+
  102. // GetReleasedAudioOutBufferAuto() -> (u32 count, buffer<nn::audio::AudioOutBuffer, 0x22>)
  103. public ResultCode GetReleasedAudioOutBufferAuto(ServiceCtx context)
  104. {
  105. (long position, long size) = context.Request.GetBufferType0x22();
  106. return GetReleasedAudioOutBufferImpl(context, position, size);
  107. }
  108. public ResultCode GetReleasedAudioOutBufferImpl(ServiceCtx context, long position, long size)
  109. {
  110. uint count = (uint)((ulong)size >> 3);
  111. long[] releasedBuffers = _audioOut.GetReleasedBuffers(_track, (int)count);
  112. for (uint index = 0; index < count; index++)
  113. {
  114. long tag = 0;
  115. if (index < releasedBuffers.Length)
  116. {
  117. tag = releasedBuffers[index];
  118. }
  119. context.Memory.Write((ulong)(position + index * 8), tag);
  120. }
  121. context.ResponseData.Write(releasedBuffers.Length);
  122. return ResultCode.Success;
  123. }
  124. [Command(9)] // 4.0.0+
  125. // GetAudioOutBufferCount() -> u32
  126. public ResultCode GetAudioOutBufferCount(ServiceCtx context)
  127. {
  128. uint bufferCount = _audioOut.GetBufferCount(_track);
  129. context.ResponseData.Write(bufferCount);
  130. return ResultCode.Success;
  131. }
  132. [Command(10)] // 4.0.0+
  133. // GetAudioOutPlayedSampleCount() -> u64
  134. public ResultCode GetAudioOutPlayedSampleCount(ServiceCtx context)
  135. {
  136. ulong playedSampleCount = _audioOut.GetPlayedSampleCount(_track);
  137. context.ResponseData.Write(playedSampleCount);
  138. return ResultCode.Success;
  139. }
  140. [Command(11)] // 4.0.0+
  141. // FlushAudioOutBuffers() -> b8
  142. public ResultCode FlushAudioOutBuffers(ServiceCtx context)
  143. {
  144. bool heldBuffers = _audioOut.FlushBuffers(_track);
  145. context.ResponseData.Write(heldBuffers);
  146. return ResultCode.Success;
  147. }
  148. [Command(12)] // 6.0.0+
  149. // SetAudioOutVolume(s32)
  150. public ResultCode SetAudioOutVolume(ServiceCtx context)
  151. {
  152. float volume = context.RequestData.ReadSingle();
  153. _audioOut.SetVolume(_track, volume);
  154. return ResultCode.Success;
  155. }
  156. [Command(13)] // 6.0.0+
  157. // GetAudioOutVolume() -> s32
  158. public ResultCode GetAudioOutVolume(ServiceCtx context)
  159. {
  160. float volume = _audioOut.GetVolume(_track);
  161. context.ResponseData.Write(volume);
  162. return ResultCode.Success;
  163. }
  164. public void Dispose()
  165. {
  166. Dispose(true);
  167. }
  168. protected virtual void Dispose(bool disposing)
  169. {
  170. if (disposing)
  171. {
  172. _kernelContext.Syscall.CloseHandle(_clientHandle);
  173. _audioOut.CloseTrack(_track);
  174. }
  175. }
  176. }
  177. }