IAudioOut.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. using ARMeilleure.Memory;
  2. using Ryujinx.Audio;
  3. using Ryujinx.HLE.HOS.Ipc;
  4. using Ryujinx.HLE.HOS.Kernel.Common;
  5. using Ryujinx.HLE.HOS.Kernel.Threading;
  6. using System;
  7. namespace Ryujinx.HLE.HOS.Services.Audio.AudioOutManager
  8. {
  9. class IAudioOut : IpcService, IDisposable
  10. {
  11. private IAalOutput _audioOut;
  12. private KEvent _releaseEvent;
  13. private int _track;
  14. public IAudioOut(IAalOutput audioOut, KEvent releaseEvent, int track)
  15. {
  16. _audioOut = audioOut;
  17. _releaseEvent = releaseEvent;
  18. _track = track;
  19. }
  20. [Command(0)]
  21. // GetAudioOutState() -> u32 state
  22. public ResultCode GetAudioOutState(ServiceCtx context)
  23. {
  24. context.ResponseData.Write((int)_audioOut.GetState(_track));
  25. return ResultCode.Success;
  26. }
  27. [Command(1)]
  28. // StartAudioOut()
  29. public ResultCode StartAudioOut(ServiceCtx context)
  30. {
  31. _audioOut.Start(_track);
  32. return ResultCode.Success;
  33. }
  34. [Command(2)]
  35. // StopAudioOut()
  36. public ResultCode StopAudioOut(ServiceCtx context)
  37. {
  38. _audioOut.Stop(_track);
  39. return ResultCode.Success;
  40. }
  41. [Command(3)]
  42. // AppendAudioOutBuffer(u64 tag, buffer<nn::audio::AudioOutBuffer, 5>)
  43. public ResultCode AppendAudioOutBuffer(ServiceCtx context)
  44. {
  45. return AppendAudioOutBufferImpl(context, context.Request.SendBuff[0].Position);
  46. }
  47. [Command(4)]
  48. // RegisterBufferEvent() -> handle<copy>
  49. public ResultCode RegisterBufferEvent(ServiceCtx context)
  50. {
  51. if (context.Process.HandleTable.GenerateHandle(_releaseEvent.ReadableEvent, out int handle) != KernelResult.Success)
  52. {
  53. throw new InvalidOperationException("Out of handles!");
  54. }
  55. context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
  56. return ResultCode.Success;
  57. }
  58. [Command(5)]
  59. // GetReleasedAudioOutBuffer() -> (u32 count, buffer<nn::audio::AudioOutBuffer, 6>)
  60. public ResultCode GetReleasedAudioOutBuffer(ServiceCtx context)
  61. {
  62. long position = context.Request.ReceiveBuff[0].Position;
  63. long size = context.Request.ReceiveBuff[0].Size;
  64. return GetReleasedAudioOutBufferImpl(context, position, size);
  65. }
  66. [Command(6)]
  67. // ContainsAudioOutBuffer(u64 tag) -> b8
  68. public ResultCode ContainsAudioOutBuffer(ServiceCtx context)
  69. {
  70. long tag = context.RequestData.ReadInt64();
  71. context.ResponseData.Write(_audioOut.ContainsBuffer(_track, tag) ? 1 : 0);
  72. return 0;
  73. }
  74. [Command(7)] // 3.0.0+
  75. // AppendAudioOutBufferAuto(u64 tag, buffer<nn::audio::AudioOutBuffer, 0x21>)
  76. public ResultCode AppendAudioOutBufferAuto(ServiceCtx context)
  77. {
  78. (long position, long size) = context.Request.GetBufferType0x21();
  79. return AppendAudioOutBufferImpl(context, position);
  80. }
  81. public ResultCode AppendAudioOutBufferImpl(ServiceCtx context, long position)
  82. {
  83. long tag = context.RequestData.ReadInt64();
  84. AudioOutData data = MemoryHelper.Read<AudioOutData>(
  85. context.Memory,
  86. position);
  87. byte[] buffer = context.Memory.ReadBytes(
  88. data.SampleBufferPtr,
  89. data.SampleBufferSize);
  90. _audioOut.AppendBuffer(_track, tag, buffer);
  91. return ResultCode.Success;
  92. }
  93. [Command(8)] // 3.0.0+
  94. // GetReleasedAudioOutBufferAuto() -> (u32 count, buffer<nn::audio::AudioOutBuffer, 0x22>)
  95. public ResultCode GetReleasedAudioOutBufferAuto(ServiceCtx context)
  96. {
  97. (long position, long size) = context.Request.GetBufferType0x22();
  98. return GetReleasedAudioOutBufferImpl(context, position, size);
  99. }
  100. public ResultCode GetReleasedAudioOutBufferImpl(ServiceCtx context, long position, long size)
  101. {
  102. uint count = (uint)((ulong)size >> 3);
  103. long[] releasedBuffers = _audioOut.GetReleasedBuffers(_track, (int)count);
  104. for (uint index = 0; index < count; index++)
  105. {
  106. long tag = 0;
  107. if (index < releasedBuffers.Length)
  108. {
  109. tag = releasedBuffers[index];
  110. }
  111. context.Memory.WriteInt64(position + index * 8, tag);
  112. }
  113. context.ResponseData.Write(releasedBuffers.Length);
  114. return ResultCode.Success;
  115. }
  116. [Command(12)] // 6.0.0+
  117. // SetAudioOutVolume(s32)
  118. public ResultCode SetAudioOutVolume(ServiceCtx context)
  119. {
  120. // Games send a gain value here, so we need to apply it on the current volume value.
  121. float gain = context.RequestData.ReadSingle();
  122. float currentVolume = _audioOut.GetVolume();
  123. float newVolume = Math.Clamp(currentVolume + gain, 0.0f, 1.0f);
  124. _audioOut.SetVolume(newVolume);
  125. return ResultCode.Success;
  126. }
  127. [Command(13)] // 6.0.0+
  128. // GetAudioOutVolume() -> s32
  129. public ResultCode GetAudioOutVolume(ServiceCtx context)
  130. {
  131. float volume = _audioOut.GetVolume();
  132. context.ResponseData.Write(volume);
  133. return ResultCode.Success;
  134. }
  135. public void Dispose()
  136. {
  137. Dispose(true);
  138. }
  139. protected virtual void Dispose(bool disposing)
  140. {
  141. if (disposing)
  142. {
  143. _audioOut.CloseTrack(_track);
  144. }
  145. }
  146. }
  147. }