FftwInterop.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #region Copyright and License
  2. /*
  3. This file is part of FFTW.NET, a wrapper around the FFTW library
  4. for the .NET framework.
  5. Copyright (C) 2017 Tobias Meyer
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. */
  15. #endregion
  16. using System;
  17. using System.Runtime.InteropServices;
  18. using System.Text;
  19. using NativeLibraryLoader;
  20. namespace FFTW.NET
  21. {
  22. public enum DftDirection : int
  23. {
  24. Forwards = -1,
  25. Backwards = 1
  26. }
  27. [Flags]
  28. public enum PlannerFlags : uint
  29. {
  30. Default = Measure,
  31. /// <summary>
  32. /// <see cref="Measure"/> tells FFTW to find an optimized plan by actually
  33. /// computing several FFTs and measuring their execution time.
  34. /// Depending on your machine, this can take some time (often a few seconds).
  35. /// </summary>
  36. Measure = (0U),
  37. /// <summary>
  38. /// <see cref="Exhaustive"/> is like <see cref="Patient"/>,
  39. /// but considers an even wider range of algorithms,
  40. /// including many that we think are unlikely to be fast,
  41. /// to produce the most optimal plan but with a substantially increased planning time.
  42. /// </summary>
  43. Exhaustive = (1U << 3),
  44. /// <summary>
  45. /// <see cref="Patient"/> is like <see cref="Measure"/>,
  46. /// but considers a wider range of algorithms and often produces
  47. /// a “more optimal” plan (especially for large transforms),
  48. /// but at the expense of several times longer planning time
  49. /// (especially for large transforms).
  50. /// </summary>
  51. Patient = (1U << 5),
  52. /// <summary>
  53. /// <see cref="Estimate"/> specifies that,
  54. /// instead of actual measurements of different algorithms,
  55. /// a simple heuristic is used to pick a (probably sub-optimal) plan quickly.
  56. /// With this flag, the input/output arrays are not overwritten during planning.
  57. /// </summary>
  58. Estimate = (1U << 6),
  59. /// <summary>
  60. /// <see cref="WisdomOnly"/> is a special planning mode in which
  61. /// the plan is only created if wisdom is available for the given problem,
  62. /// and otherwise a <c>null</c> plan is returned. This can be combined
  63. /// with other flags, e.g. '<see cref="WisdomOnly"/> | <see cref="Patient"/>'
  64. /// creates a plan only if wisdom is available that was created in
  65. /// <see cref="Patient"/> or <see cref="Exhaustive"/> mode.
  66. /// The <see cref="WisdomOnly"/> flag is intended for users who need to
  67. /// detect whether wisdom is available; for example, if wisdom is not
  68. /// available one may wish to allocate new arrays for planning so that
  69. /// user data is not overwritten.
  70. /// </summary>
  71. WisdomOnly = (1U << 21)
  72. }
  73. public static class FftwInterop
  74. {
  75. public delegate bool fftw_init_threads();
  76. private static readonly string windowsdllname = "libfftw3";
  77. private static readonly string linuxdllname = "libfftw3.so";
  78. static FftwInterop()
  79. {
  80. }
  81. public static void Initialize()
  82. {
  83. if (_NativeLoader != null) return;
  84. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) _NativeLoader = new NativeLibraryLoader.NativeLoader(windowsdllname);
  85. else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
  86. {
  87. _NativeLoader = new NativeLibraryLoader.NativeLoader(linuxdllname);
  88. }
  89. else
  90. {
  91. throw new PlatformNotSupportedException();
  92. }
  93. _NativeLoader.SearchDirectories.Add(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "libs"));
  94. _NativeLoader.Init();
  95. _version = GetVersionAndInitialize();
  96. }
  97. private static NativeLibraryLoader.NativeLoader _NativeLoader;
  98. static Version _version;
  99. public static Version Version => _version;
  100. public static bool IsAvailable => _version != null;
  101. public static T GetDelegate<T>() where T : Delegate
  102. {
  103. return _NativeLoader.LoadFunction<T>(typeof(T).Name);
  104. }
  105. internal static object Lock
  106. {
  107. get
  108. {
  109. if (!IsAvailable)
  110. throw new InvalidOperationException($"{nameof(FftwInterop.IsAvailable)} is false.");
  111. return _version;
  112. }
  113. }
  114. public delegate void WriteCharHandler(byte c, IntPtr ptr);
  115. static Version GetVersionAndInitialize()
  116. {
  117. try
  118. {
  119. //GetDelegate<fftw_init_threads>()();
  120. }
  121. catch (DllNotFoundException) { return null; }
  122. string version = GetVersion();
  123. return new Version(version);
  124. }
  125. public delegate int fftw_alignment_of(IntPtr ptr);
  126. public delegate void fftw_free(IntPtr ptr);
  127. public delegate void fftw_plan_with_nthreads(int nthreads);
  128. public delegate IntPtr fftw_plan_dft_r2c(int rank, [MarshalAs(UnmanagedType.LPArray)] int[] n, IntPtr arrIn, IntPtr arrOut, PlannerFlags flags);
  129. public delegate void fftw_forget_wisdom();
  130. public delegate IntPtr fftw_malloc(IntPtr size);
  131. public delegate bool fftw_export_wisdom_to_filename([MarshalAs(UnmanagedType.LPStr)] string filename);
  132. public delegate IntPtr fftw_plan_dft(int rank, [MarshalAs(UnmanagedType.LPArray)] int[] n, IntPtr arrIn, IntPtr arrOut, DftDirection direction, PlannerFlags flags);
  133. public delegate void fftw_execute(IntPtr plan);
  134. public delegate IntPtr fftw_plan_dft_c2r(int rank, [MarshalAs(UnmanagedType.LPArray)] int[] n, IntPtr arrIn, IntPtr arrOut, PlannerFlags flags);
  135. public delegate void fftw_destroy_plan(IntPtr plan);
  136. public delegate bool fftw_import_wisdom_from_string([MarshalAs(UnmanagedType.LPStr)] string wisdom);
  137. public delegate bool fftw_import_wisdom_from_filename([MarshalAs(UnmanagedType.LPStr)] string filename);
  138. public delegate void fftw_export_wisdom([MarshalAs(UnmanagedType.FunctionPtr)] WriteCharHandler writeChar, IntPtr data);
  139. public static string fftw_export_wisdom_to_string()
  140. {
  141. // We cannot use the fftw_export_wisdom_to_string function here
  142. // because we have no way of releasing the returned memory.
  143. StringBuilder sb = new StringBuilder();
  144. FftwInterop.WriteCharHandler writeChar = (c, ptr) => sb.Append((char)c);
  145. GetDelegate<fftw_export_wisdom>()(writeChar, IntPtr.Zero);
  146. return sb.ToString();
  147. }
  148. static string GetVersion()
  149. {
  150. const string VersionPrefix = "fftw-";
  151. const byte WhiteSpace = (byte)' ';
  152. byte[] prefix = Encoding.ASCII.GetBytes(VersionPrefix);
  153. int i = 0;
  154. StringBuilder sb = new StringBuilder();
  155. FftwInterop.WriteCharHandler writeChar = (c, ptr) =>
  156. {
  157. if (i < 0)
  158. return;
  159. if (i == VersionPrefix.Length)
  160. {
  161. if (c == WhiteSpace)
  162. i = -1;
  163. else
  164. sb.Append((char)c);
  165. }
  166. else if (c == prefix[i])
  167. i++;
  168. else
  169. i = 0;
  170. };
  171. // This is only called on initialization, so no synchronization/lock is required
  172. GetDelegate<fftw_export_wisdom>()(writeChar, IntPtr.Zero);
  173. return sb.ToString();
  174. }
  175. }
  176. }