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