SoundIoOutStreamContext.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using System.Runtime.InteropServices;
  4. using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
  5. namespace Ryujinx.Audio.Backends.SoundIo.Native
  6. {
  7. public class SoundIoOutStreamContext : IDisposable
  8. {
  9. [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  10. private unsafe delegate void WriteCallbackDelegate(IntPtr ctx, int frameCountMin, int frameCountMax);
  11. private IntPtr _context;
  12. private IntPtr _nameStored;
  13. private Action<int, int> _writeCallback;
  14. private WriteCallbackDelegate _writeCallbackNative;
  15. public IntPtr Context => _context;
  16. internal SoundIoOutStreamContext(IntPtr context)
  17. {
  18. _context = context;
  19. _nameStored = IntPtr.Zero;
  20. _writeCallback = null;
  21. _writeCallbackNative = null;
  22. }
  23. private ref SoundIoOutStream GetOutContext()
  24. {
  25. unsafe
  26. {
  27. return ref Unsafe.AsRef<SoundIoOutStream>((SoundIoOutStream*)_context);
  28. }
  29. }
  30. public string Name
  31. {
  32. get => Marshal.PtrToStringAnsi(GetOutContext().Name);
  33. set
  34. {
  35. var context = GetOutContext();
  36. if (_nameStored != IntPtr.Zero && context.Name == _nameStored)
  37. {
  38. Marshal.FreeHGlobal(_nameStored);
  39. }
  40. _nameStored = Marshal.StringToHGlobalAnsi(value);
  41. GetOutContext().Name = _nameStored;
  42. }
  43. }
  44. public SoundIoChannelLayout Layout
  45. {
  46. get => GetOutContext().Layout;
  47. set => GetOutContext().Layout = value;
  48. }
  49. public SoundIoFormat Format
  50. {
  51. get => GetOutContext().Format;
  52. set => GetOutContext().Format = value;
  53. }
  54. public int SampleRate
  55. {
  56. get => GetOutContext().SampleRate;
  57. set => GetOutContext().SampleRate = value;
  58. }
  59. public float Volume
  60. {
  61. get => GetOutContext().Volume;
  62. set => GetOutContext().Volume = value;
  63. }
  64. public int BytesPerFrame
  65. {
  66. get => GetOutContext().BytesPerFrame;
  67. set => GetOutContext().BytesPerFrame = value;
  68. }
  69. public int BytesPerSample
  70. {
  71. get => GetOutContext().BytesPerSample;
  72. set => GetOutContext().BytesPerSample = value;
  73. }
  74. public Action<int, int> WriteCallback
  75. {
  76. get { return _writeCallback; }
  77. set
  78. {
  79. _writeCallback = value;
  80. if (_writeCallback == null)
  81. {
  82. _writeCallbackNative = null;
  83. }
  84. else
  85. {
  86. _writeCallbackNative = (ctx, frameCountMin, frameCountMax) => _writeCallback(frameCountMin, frameCountMax);
  87. }
  88. GetOutContext().WriteCallback = Marshal.GetFunctionPointerForDelegate(_writeCallbackNative);
  89. }
  90. }
  91. private static void CheckError(SoundIoError error)
  92. {
  93. if (error != SoundIoError.None)
  94. {
  95. throw new SoundIoException(error);
  96. }
  97. }
  98. public void Open() => CheckError(soundio_outstream_open(_context));
  99. public void Start() => CheckError(soundio_outstream_start(_context));
  100. public void Pause(bool pause) => CheckError(soundio_outstream_pause(_context, pause));
  101. public void SetVolume(double volume) => CheckError(soundio_outstream_set_volume(_context, volume));
  102. public Span<SoundIoChannelArea> BeginWrite(ref int frameCount)
  103. {
  104. IntPtr arenas = default;
  105. int nativeFrameCount = frameCount;
  106. unsafe
  107. {
  108. var frameCountPtr = &nativeFrameCount;
  109. var arenasPtr = &arenas;
  110. CheckError(soundio_outstream_begin_write(_context, (IntPtr)arenasPtr, (IntPtr)frameCountPtr));
  111. frameCount = *frameCountPtr;
  112. return new Span<SoundIoChannelArea>((void*)arenas, Layout.ChannelCount);
  113. }
  114. }
  115. public void EndWrite() => CheckError(soundio_outstream_end_write(_context));
  116. protected virtual void Dispose(bool disposing)
  117. {
  118. if (_context != IntPtr.Zero)
  119. {
  120. soundio_outstream_destroy(_context);
  121. _context = IntPtr.Zero;
  122. }
  123. }
  124. public void Dispose()
  125. {
  126. Dispose(true);
  127. GC.SuppressFinalize(this);
  128. }
  129. ~SoundIoOutStreamContext()
  130. {
  131. Dispose(false);
  132. }
  133. }
  134. }