Extension.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. using NAudio.MediaFoundation;
  2. using NAudio.Wave;
  3. using System.Reflection;
  4. namespace Bmp.Core.FFMpeg.CsCorePorts;
  5. internal static class Extension
  6. {
  7. /// <summary>
  8. /// Gets the length of a <see cref="IAudioSource"/> as a <see cref="TimeSpan"/>.
  9. /// </summary>
  10. /// <param name="source">The source to get the length for.</param>
  11. /// <returns>The length of the specified <paramref name="source"/> as a <see cref="TimeSpan"/> value.</returns>
  12. public static TimeSpan GetLength(this IAudioSource source)
  13. {
  14. return GetTime(source, source.Length);
  15. }
  16. /// <summary>
  17. /// Gets the position of a <see cref="IAudioSource"/> as a <see cref="TimeSpan"/> value.
  18. /// </summary>
  19. /// <param name="source">The source to get the position of.</param>
  20. /// <returns>The position of the specified <paramref name="source"/> as a <see cref="TimeSpan"/> value.</returns>
  21. /// <remarks>The source must support seeking to get or set the position.
  22. /// Use the <see cref="IAudioSource.CanSeek"/> property to determine whether the stream supports seeking.
  23. /// Otherwise a call to this method may result in an exception.</remarks>
  24. public static TimeSpan GetPosition(this IAudioSource source)
  25. {
  26. return GetTime(source, source.Position);
  27. }
  28. /// <summary>
  29. /// Sets the position of a <see cref="IAudioSource"/> as a <see cref="TimeSpan"/> value.
  30. /// </summary>
  31. /// <param name="source">The source to set the new position for.</param>
  32. /// <param name="position">The new position as a <see cref="TimeSpan"/> value.</param>
  33. /// <remarks>
  34. /// The source must support seeking to get or set the position.
  35. /// Use the <see cref="IAudioSource.CanSeek"/> property to determine whether the stream supports seeking.
  36. /// Otherwise a call to this method may result in an exception.
  37. /// </remarks>
  38. public static void SetPosition(this IAudioSource source, TimeSpan position)
  39. {
  40. if (source == null)
  41. throw new ArgumentNullException("source");
  42. if (position.TotalMilliseconds < 0)
  43. throw new ArgumentOutOfRangeException("position");
  44. var bytes = GetRawElements(source, position);
  45. source.Position = bytes;
  46. }
  47. /// <summary>
  48. /// Converts a duration in raw elements to a <see cref="TimeSpan"/> value. For more information about "raw elements" see remarks.
  49. /// </summary>
  50. /// <param name="source">The <see cref="IAudioSource"/> instance which provides the <see cref="WaveFormat"/> used
  51. /// to convert the duration in "raw elements" to a <see cref="TimeSpan"/> value.</param>
  52. /// <param name="elementCount">The duration in "raw elements" to convert to a <see cref="TimeSpan"/> value.</param>
  53. /// <returns>The duration as a <see cref="TimeSpan"/> value.</returns>
  54. /// <exception cref="System.ArgumentNullException">
  55. /// source
  56. /// or
  57. /// elementCount
  58. /// </exception>
  59. /// <remarks>
  60. /// The term "raw elements" describes the elements, an audio source uses.
  61. /// What type of unit an implementation of the <see cref="IAudioSource"/> interface uses, depends on the implementation itself.
  62. /// For example, a <see cref="IWaveSource"/> uses bytes while a <see cref="ISampleSource"/> uses samples.
  63. /// That means that a <see cref="IWaveSource"/> provides its position, length,... in bytes
  64. /// while a <see cref="ISampleSource"/> provides its position, length,... in samples.
  65. /// <para></para>
  66. /// To get the length or the position of a <see cref="IAudioSource"/> as a <see cref="TimeSpan"/> value, use the
  67. /// <see cref="GetLength"/> or the <see cref="GetPosition"/> property.
  68. /// <para></para><para></para>
  69. /// Internally this method uses the <see cref="TimeConverterFactory"/> class.
  70. /// </remarks>
  71. public static TimeSpan GetTime(this IAudioSource source, long elementCount)
  72. {
  73. if (source == null)
  74. throw new ArgumentNullException("source");
  75. if (elementCount < 0)
  76. throw new ArgumentNullException("elementCount");
  77. return TimeConverterFactory.Instance.GetTimeConverterForSource(source)
  78. .ToTimeSpan(source.WaveFormat, elementCount);
  79. }
  80. /// <summary>
  81. /// Converts a duration in raw elements to a duration in milliseconds. For more information about "raw elements" see remarks.
  82. /// </summary>
  83. /// <param name="source">The <see cref="IAudioSource"/> instance which provides the <see cref="WaveFormat"/> used
  84. /// to convert the duration in "raw elements" to a duration in milliseconds.</param>
  85. /// <param name="elementCount">The duration in "raw elements" to convert to duration in milliseconds.</param>
  86. /// <returns>The duration in milliseconds.</returns>
  87. /// <exception cref="System.ArgumentNullException">
  88. /// source
  89. /// or
  90. /// elementCount
  91. /// </exception>
  92. /// <remarks>
  93. /// The term "raw elements" describes the elements, an audio source uses.
  94. /// What type of unit an implementation of the <see cref="IAudioSource"/> interface uses, depends on the implementation itself.
  95. /// For example, a <see cref="IWaveSource"/> uses bytes while a <see cref="ISampleSource"/> uses samples.
  96. /// That means that a <see cref="IWaveSource"/> provides its position, length,... in bytes
  97. /// while a <see cref="ISampleSource"/> provides its position, length,... in samples.
  98. /// <para></para>
  99. /// To get the length or the position of a <see cref="IAudioSource"/> as a <see cref="TimeSpan"/> value, use the
  100. /// <see cref="GetLength"/> or the <see cref="GetPosition"/> property.
  101. /// <para></para><para></para>
  102. /// Internally this method uses the <see cref="TimeConverterFactory"/> class.
  103. /// </remarks>
  104. public static long GetMilliseconds(this IAudioSource source, long elementCount)
  105. {
  106. if (source == null)
  107. throw new ArgumentNullException("source");
  108. if (elementCount < 0)
  109. throw new ArgumentOutOfRangeException("elementCount");
  110. return (long)GetTime(source, elementCount).TotalMilliseconds;
  111. }
  112. /// <summary>
  113. /// Converts a duration as a <see cref="TimeSpan" /> to a duration in "raw elements". For more information about "raw elements" see remarks.
  114. /// </summary>
  115. /// <param name="source">
  116. /// <see cref="IWaveSource" /> instance which provides the <see cref="WaveFormat" /> used to convert
  117. /// the duration as a <see cref="TimeSpan" /> to a duration in "raw elements".
  118. /// </param>
  119. /// <param name="timespan">Duration as a <see cref="TimeSpan" /> to convert to a duration in "raw elements".</param>
  120. /// <returns>Duration in "raw elements".</returns>
  121. /// <exception cref="ArgumentNullException">source</exception>
  122. /// <remarks>
  123. /// The term "raw elements" describes the elements, an audio source uses.
  124. /// What type of unit an implementation of the <see cref="IAudioSource"/> interface uses, depends on the implementation itself.
  125. /// For example, a <see cref="IWaveSource"/> uses bytes while a <see cref="ISampleSource"/> uses samples.
  126. /// That means that a <see cref="IWaveSource"/> provides its position, length,... in bytes
  127. /// while a <see cref="ISampleSource"/> provides its position, length,... in samples.
  128. /// <para></para>
  129. /// To get the length or the position of a <see cref="IAudioSource"/> as a <see cref="TimeSpan"/> value, use the
  130. /// <see cref="GetLength"/> or the <see cref="GetPosition"/> property.
  131. /// <para></para><para></para>
  132. /// Internally this method uses the <see cref="TimeConverterFactory"/> class.
  133. /// </remarks>
  134. public static long GetRawElements(this IAudioSource source, TimeSpan timespan)
  135. {
  136. if (source == null)
  137. throw new ArgumentNullException("source");
  138. return TimeConverterFactory.Instance.GetTimeConverterForSource(source)
  139. .ToRawElements(source.WaveFormat, timespan);
  140. }
  141. /// <summary>
  142. /// Converts a duration in milliseconds to a duration in "raw elements". For more information about "raw elements" see remarks.
  143. /// </summary>
  144. /// <param name="source"><see cref="IWaveSource" /> instance which provides the <see cref="WaveFormat" /> used to convert
  145. /// the duration in milliseconds to a duration in "raw elements".</param>
  146. /// <param name="milliseconds">Duration in milliseconds to convert to a duration in "raw elements".</param>
  147. /// <returns>
  148. /// Duration in "raw elements".
  149. /// </returns>
  150. /// <remarks>
  151. /// The term "raw elements" describes the elements, an audio source uses.
  152. /// What type of unit an implementation of the <see cref="IAudioSource"/> interface uses, depends on the implementation itself.
  153. /// For example, a <see cref="IWaveSource"/> uses bytes while a <see cref="ISampleSource"/> uses samples.
  154. /// That means that a <see cref="IWaveSource"/> provides its position, length,... in bytes
  155. /// while a <see cref="ISampleSource"/> provides its position, length,... in samples.
  156. /// <para></para>
  157. /// To get the length or the position of a <see cref="IAudioSource"/> as a <see cref="TimeSpan"/> value, use the
  158. /// <see cref="GetLength"/> or the <see cref="GetPosition"/> property.
  159. /// <para></para><para></para>
  160. /// Internally this method uses the <see cref="TimeConverterFactory"/> class.
  161. /// </remarks>
  162. /// <exception cref="System.ArgumentNullException">source</exception>
  163. /// <exception cref="System.ArgumentOutOfRangeException">milliseconds is less than zero.</exception>
  164. public static long GetRawElements(this IAudioSource source, long milliseconds)
  165. {
  166. if (source == null)
  167. throw new ArgumentNullException("source");
  168. if (milliseconds < 0)
  169. throw new ArgumentOutOfRangeException("milliseconds");
  170. return GetRawElements(source, TimeSpan.FromMilliseconds(milliseconds));
  171. }
  172. /// <summary>
  173. /// Writes all audio data of the <paramref name="waveSource"/> to a stream. In comparison to the <see cref="WriteToWaveStream"/> method,
  174. /// the <see cref="WriteToStream"/> method won't encode the provided audio to any particular format. No wav, aiff,... header won't be included.
  175. /// </summary>
  176. /// <param name="waveSource">The waveSource which provides the audio data to write to the <paramref name="stream"/>.</param>
  177. /// <param name="stream">The <see cref="Stream"/> to store the audio data in.</param>
  178. /// <exception cref="System.ArgumentNullException">
  179. /// waveSource
  180. /// or
  181. /// stream
  182. /// </exception>
  183. /// <exception cref="System.ArgumentException">Stream is not writeable.;stream</exception>
  184. public static void WriteToStream(this IWaveSource waveSource, Stream stream)
  185. {
  186. if (waveSource == null)
  187. throw new ArgumentNullException("waveSource");
  188. if (stream == null)
  189. throw new ArgumentNullException("stream");
  190. if (!stream.CanWrite)
  191. throw new ArgumentException("Stream is not writeable.", "stream");
  192. var buffer = new byte[waveSource.WaveFormat.BytesPerSecond];
  193. int read;
  194. while ((read = waveSource.Read(buffer, 0, buffer.Length)) > 0)
  195. {
  196. stream.Write(buffer, 0, read);
  197. }
  198. }
  199. /// <summary>
  200. /// Checks the length of an array.
  201. /// </summary>
  202. /// <typeparam name="T">Type of the array.</typeparam>
  203. /// <param name="inst">The array to check. This parameter can be null.</param>
  204. /// <param name="size">The target length of the array.</param>
  205. /// <param name="exactSize">A value which indicates whether the length of the array has to fit exactly the specified <paramref name="size"/>.</param>
  206. /// <returns>Array which fits the specified requirements. Note that if a new array got created, the content of the old array won't get copied to the return value.</returns>
  207. public static T[] CheckBuffer<T>(this T[] inst, long size, bool exactSize = false)
  208. {
  209. if (inst == null || (!exactSize && inst.Length < size) || (exactSize && inst.Length != size))
  210. return new T[size];
  211. return inst;
  212. }
  213. internal static byte[] ReadBytes(this IWaveSource waveSource, int count)
  214. {
  215. if (waveSource == null)
  216. throw new ArgumentNullException("waveSource");
  217. count -= (count % waveSource.WaveFormat.BlockAlign);
  218. if (count <= 0)
  219. throw new ArgumentOutOfRangeException("count");
  220. var buffer = new byte[count];
  221. var read = waveSource.Read(buffer, 0, buffer.Length);
  222. if (read < count)
  223. Array.Resize(ref buffer, read);
  224. return buffer;
  225. }
  226. internal static bool IsClosed(this Stream stream)
  227. {
  228. return !stream.CanRead && !stream.CanWrite;
  229. }
  230. internal static bool IsEndOfStream(this Stream stream)
  231. {
  232. return stream.Position == stream.Length;
  233. }
  234. //copied from http://stackoverflow.com/questions/1436190/c-sharp-get-and-set-the-high-order-word-of-an-integer
  235. internal static int LowWord(this int number)
  236. {
  237. return number & 0x0000FFFF;
  238. }
  239. internal static int LowWord(this int number, int newValue)
  240. {
  241. return (int)((number & 0xFFFF0000) + (newValue & 0x0000FFFF));
  242. }
  243. internal static int HighWord(this int number)
  244. {
  245. return (int)(number & 0xFFFF0000);
  246. }
  247. internal static int HighWord(this int number, int newValue)
  248. {
  249. return (number & 0x0000FFFF) + (newValue << 16);
  250. }
  251. internal static uint LowWord(this uint number)
  252. {
  253. return number & 0x0000FFFF;
  254. }
  255. internal static uint LowWord(this uint number, int newValue)
  256. {
  257. return (uint)((number & 0xFFFF0000) + (newValue & 0x0000FFFF));
  258. }
  259. internal static uint HighWord(this uint number)
  260. {
  261. return number & 0xFFFF0000;
  262. }
  263. internal static uint HighWord(this uint number, int newValue)
  264. {
  265. return (uint)((number & 0x0000FFFF) + (newValue << 16));
  266. }
  267. //--
  268. internal static Guid GetGuid(this Object obj)
  269. {
  270. return obj.GetType().GUID;
  271. }
  272. internal static void WaitForExit(this Thread thread)
  273. {
  274. if (thread == null)
  275. return;
  276. if (thread == Thread.CurrentThread)
  277. throw new InvalidOperationException("Deadlock detected.");
  278. thread.Join();
  279. }
  280. internal static bool WaitForExit(this Thread thread, int timeout)
  281. {
  282. if (thread == null)
  283. return true;
  284. if (thread == Thread.CurrentThread)
  285. throw new InvalidOperationException("Deadlock detected.");
  286. return thread.Join(timeout);
  287. }
  288. // ReSharper disable once InconsistentNaming
  289. //copied from http://stackoverflow.com/questions/9927590/can-i-set-a-value-on-a-struct-through-reflection-without-boxing
  290. internal static void SetValueForValueType<T>(this FieldInfo field, ref T item, object value) where T : struct
  291. {
  292. field.SetValueDirect(__makeref(item), value);
  293. }
  294. }