SoundIOInStream.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.InteropServices;
  4. namespace SoundIOSharp
  5. {
  6. public class SoundIOInStream : IDisposable
  7. {
  8. internal SoundIOInStream(Pointer<SoundIoInStream> handle)
  9. {
  10. this.handle = handle;
  11. }
  12. Pointer<SoundIoInStream> handle;
  13. public void Dispose()
  14. {
  15. Natives.soundio_instream_destroy(handle);
  16. }
  17. // Equality (based on handle)
  18. public override bool Equals(object other)
  19. {
  20. var d = other as SoundIOInStream;
  21. return d != null && (this.handle == d.handle);
  22. }
  23. public override int GetHashCode()
  24. {
  25. return (int)(IntPtr)handle;
  26. }
  27. public static bool operator == (SoundIOInStream obj1, SoundIOInStream obj2)
  28. {
  29. return obj1 is null ? obj2 is null : obj1.Equals(obj2);
  30. }
  31. public static bool operator != (SoundIOInStream obj1, SoundIOInStream obj2)
  32. {
  33. return obj1 is null ? obj2 is object : !obj1.Equals(obj2);
  34. }
  35. // fields
  36. public SoundIODevice Device
  37. {
  38. get { return new SoundIODevice(Marshal.ReadIntPtr(handle, device_offset)); }
  39. }
  40. static readonly int device_offset = (int)Marshal.OffsetOf<SoundIoInStream>("device");
  41. public SoundIOFormat Format
  42. {
  43. get { return (SoundIOFormat)Marshal.ReadInt32(handle, format_offset); }
  44. set { Marshal.WriteInt32(handle, format_offset, (int) value); }
  45. }
  46. static readonly int format_offset = (int)Marshal.OffsetOf<SoundIoInStream>("format");
  47. public int SampleRate
  48. {
  49. get { return Marshal.ReadInt32(handle, sample_rate_offset); }
  50. set { Marshal.WriteInt32(handle, sample_rate_offset, value); }
  51. }
  52. static readonly int sample_rate_offset = (int)Marshal.OffsetOf<SoundIoInStream>("sample_rate");
  53. public SoundIOChannelLayout Layout
  54. {
  55. get { return new SoundIOChannelLayout ((IntPtr) handle + layout_offset); }
  56. set
  57. {
  58. unsafe
  59. {
  60. Buffer.MemoryCopy((void*)((IntPtr)handle + layout_offset), (void*)value.Handle, Marshal.SizeOf<SoundIoChannelLayout>(), Marshal.SizeOf<SoundIoChannelLayout>());
  61. }
  62. }
  63. }
  64. static readonly int layout_offset = (int)Marshal.OffsetOf<SoundIoInStream>("layout");
  65. public double SoftwareLatency
  66. {
  67. get { return MarshalEx.ReadDouble(handle, software_latency_offset); }
  68. set { MarshalEx.WriteDouble(handle, software_latency_offset, value); }
  69. }
  70. static readonly int software_latency_offset = (int)Marshal.OffsetOf<SoundIoInStream>("software_latency");
  71. // error_callback
  72. public Action ErrorCallback
  73. {
  74. get { return error_callback; }
  75. set
  76. {
  77. error_callback = value;
  78. error_callback_native = _ => error_callback();
  79. var ptr = Marshal.GetFunctionPointerForDelegate(error_callback_native);
  80. Marshal.WriteIntPtr(handle, error_callback_offset, ptr);
  81. }
  82. }
  83. static readonly int error_callback_offset = (int)Marshal.OffsetOf<SoundIoInStream>("error_callback");
  84. Action error_callback;
  85. delegate void error_callback_delegate(IntPtr handle);
  86. error_callback_delegate error_callback_native;
  87. // read_callback
  88. public Action<int,int> ReadCallback
  89. {
  90. get { return read_callback; }
  91. set
  92. {
  93. read_callback = value;
  94. read_callback_native = (_, minFrameCount, maxFrameCount) => read_callback(minFrameCount, maxFrameCount);
  95. var ptr = Marshal.GetFunctionPointerForDelegate(read_callback_native);
  96. Marshal.WriteIntPtr(handle, read_callback_offset, ptr);
  97. }
  98. }
  99. static readonly int read_callback_offset = (int)Marshal.OffsetOf<SoundIoInStream>("read_callback");
  100. Action<int, int> read_callback;
  101. delegate void read_callback_delegate(IntPtr handle, int min, int max);
  102. read_callback_delegate read_callback_native;
  103. // overflow_callback
  104. public Action OverflowCallback
  105. {
  106. get { return overflow_callback; }
  107. set
  108. {
  109. overflow_callback = value;
  110. overflow_callback_native = _ => overflow_callback();
  111. var ptr = Marshal.GetFunctionPointerForDelegate(overflow_callback_native);
  112. Marshal.WriteIntPtr(handle, overflow_callback_offset, ptr);
  113. }
  114. }
  115. static readonly int overflow_callback_offset = (int)Marshal.OffsetOf<SoundIoInStream>("overflow_callback");
  116. Action overflow_callback;
  117. delegate void overflow_callback_delegate(IntPtr handle);
  118. overflow_callback_delegate overflow_callback_native;
  119. // FIXME: this should be taken care in more centralized/decent manner... we don't want to write
  120. // this kind of code anywhere we need string marshaling.
  121. List<IntPtr> allocated_hglobals = new List<IntPtr>();
  122. public string Name
  123. {
  124. get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, name_offset)); }
  125. set
  126. {
  127. unsafe
  128. {
  129. var existing = Marshal.ReadIntPtr(handle, name_offset);
  130. if (allocated_hglobals.Contains(existing))
  131. {
  132. allocated_hglobals.Remove(existing);
  133. Marshal.FreeHGlobal(existing);
  134. }
  135. var ptr = Marshal.StringToHGlobalAnsi(value);
  136. Marshal.WriteIntPtr(handle, name_offset, ptr);
  137. allocated_hglobals.Add(ptr);
  138. }
  139. }
  140. }
  141. static readonly int name_offset = (int)Marshal.OffsetOf<SoundIoInStream>("name");
  142. public bool NonTerminalHint
  143. {
  144. get { return Marshal.ReadInt32(handle, non_terminal_hint_offset) != 0; }
  145. }
  146. static readonly int non_terminal_hint_offset = (int)Marshal.OffsetOf<SoundIoInStream>("non_terminal_hint");
  147. public int BytesPerFrame
  148. {
  149. get { return Marshal.ReadInt32(handle, bytes_per_frame_offset); }
  150. }
  151. static readonly int bytes_per_frame_offset = (int)Marshal.OffsetOf<SoundIoInStream>("bytes_per_frame");
  152. public int BytesPerSample
  153. {
  154. get { return Marshal.ReadInt32(handle, bytes_per_sample_offset); }
  155. }
  156. static readonly int bytes_per_sample_offset = (int)Marshal.OffsetOf<SoundIoInStream>("bytes_per_sample");
  157. public string LayoutErrorMessage
  158. {
  159. get
  160. {
  161. var code = (SoundIoError)Marshal.ReadInt32(handle, layout_error_offset);
  162. return code == SoundIoError.SoundIoErrorNone ? null : Marshal.PtrToStringAnsi(Natives.soundio_strerror((int)code));
  163. }
  164. }
  165. static readonly int layout_error_offset = (int)Marshal.OffsetOf<SoundIoInStream>("layout_error");
  166. // functions
  167. public void Open()
  168. {
  169. var ret = (SoundIoError)Natives.soundio_instream_open(handle);
  170. if (ret != SoundIoError.SoundIoErrorNone)
  171. {
  172. throw new SoundIOException(ret);
  173. }
  174. }
  175. public void Start()
  176. {
  177. var ret = (SoundIoError)Natives.soundio_instream_start(handle);
  178. if (ret != SoundIoError.SoundIoErrorNone)
  179. {
  180. throw new SoundIOException(ret);
  181. }
  182. }
  183. public SoundIOChannelAreas BeginRead(ref int frameCount)
  184. {
  185. IntPtr ptrs = default;
  186. int nativeFrameCount = frameCount;
  187. unsafe
  188. {
  189. var frameCountPtr = &nativeFrameCount;
  190. var ptrptr = &ptrs;
  191. var ret = (SoundIoError)Natives.soundio_instream_begin_read(handle, (IntPtr)ptrptr, (IntPtr)frameCountPtr);
  192. frameCount = *frameCountPtr;
  193. if (ret != SoundIoError.SoundIoErrorNone)
  194. {
  195. throw new SoundIOException(ret);
  196. }
  197. return new SoundIOChannelAreas(ptrs, Layout.ChannelCount, frameCount);
  198. }
  199. }
  200. public void EndRead()
  201. {
  202. var ret = (SoundIoError)Natives.soundio_instream_end_read(handle);
  203. if (ret != SoundIoError.SoundIoErrorNone)
  204. {
  205. throw new SoundIOException(ret);
  206. }
  207. }
  208. public void Pause(bool pause)
  209. {
  210. var ret = (SoundIoError)Natives.soundio_instream_pause(handle, pause);
  211. if (ret != SoundIoError.SoundIoErrorNone)
  212. {
  213. throw new SoundIOException(ret);
  214. }
  215. }
  216. public double GetLatency()
  217. {
  218. unsafe
  219. {
  220. double* dptr = null;
  221. IntPtr p = new IntPtr(dptr);
  222. var ret = (SoundIoError)Natives.soundio_instream_get_latency(handle, p);
  223. if (ret != SoundIoError.SoundIoErrorNone)
  224. {
  225. throw new SoundIOException(ret);
  226. }
  227. dptr = (double*)p;
  228. return *dptr;
  229. }
  230. }
  231. }
  232. }