/ src / Ryujinx.Audio.Backends.SoundIo / Native / SoundIoOutStreamContext.cs
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  }