#region Copyright and License /* This file is part of FFTW.NET, a wrapper around the FFTW library for the .NET framework. Copyright (C) 2017 Tobias Meyer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #endregion using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Text; using NativeLibraryLoader; namespace FFTW.NET { public enum DftDirection : int { Forwards = -1, Backwards = 1 } [Flags] public enum PlannerFlags : uint { Default = Measure, /// /// tells FFTW to find an optimized plan by actually /// computing several FFTs and measuring their execution time. /// Depending on your machine, this can take some time (often a few seconds). /// Measure = (0U), /// /// is like , /// but considers an even wider range of algorithms, /// including many that we think are unlikely to be fast, /// to produce the most optimal plan but with a substantially increased planning time. /// Exhaustive = (1U << 3), /// /// is like , /// but considers a wider range of algorithms and often produces /// a “more optimal” plan (especially for large transforms), /// but at the expense of several times longer planning time /// (especially for large transforms). /// Patient = (1U << 5), /// /// specifies that, /// instead of actual measurements of different algorithms, /// a simple heuristic is used to pick a (probably sub-optimal) plan quickly. /// With this flag, the input/output arrays are not overwritten during planning. /// Estimate = (1U << 6), /// /// is a special planning mode in which /// the plan is only created if wisdom is available for the given problem, /// and otherwise a null plan is returned. This can be combined /// with other flags, e.g. ' | ' /// creates a plan only if wisdom is available that was created in /// or mode. /// The flag is intended for users who need to /// detect whether wisdom is available; for example, if wisdom is not /// available one may wish to allocate new arrays for planning so that /// user data is not overwritten. /// WisdomOnly = (1U << 21) } public static class FftwInterop { public delegate bool fftw_init_threads(); private static readonly string windowsdllname = "libfftw3"; private static readonly string linuxdllname = "libfftw3.so"; static FftwInterop() { } public static void Initialize() { if (_NativeLoader != null) return; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) _NativeLoader = new NativeLibraryLoader.NativeLoader(windowsdllname); else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { _NativeLoader = new NativeLibraryLoader.NativeLoader(linuxdllname); } else { throw new PlatformNotSupportedException(); } _NativeLoader.SearchDirectories.Add(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "libs")); _NativeLoader.Init(); _version = GetVersionAndInitialize(); } [AllowNull] private static NativeLibraryLoader.NativeLoader _NativeLoader; private static Version _version = new Version(); public static Version Version => _version; public static bool IsAvailable => _version != null; public static T GetDelegate() where T : Delegate { return _NativeLoader.LoadFunction(typeof(T).Name); } internal static object Lock { get { if (!IsAvailable) throw new InvalidOperationException($"{nameof(FftwInterop.IsAvailable)} is false."); return _version; } } public delegate void WriteCharHandler(byte c, IntPtr ptr); static Version GetVersionAndInitialize() { try { //GetDelegate()(); } catch (DllNotFoundException) { return new Version(); } string version = GetVersion(); return new Version(version); } public delegate int fftw_alignment_of(IntPtr ptr); public delegate void fftw_free(IntPtr ptr); public delegate void fftw_plan_with_nthreads(int nthreads); public delegate IntPtr fftw_plan_dft_r2c(int rank, [MarshalAs(UnmanagedType.LPArray)] int[] n, IntPtr arrIn, IntPtr arrOut, PlannerFlags flags); public delegate void fftw_forget_wisdom(); public delegate IntPtr fftw_malloc(IntPtr size); public delegate bool fftw_export_wisdom_to_filename([MarshalAs(UnmanagedType.LPStr)] string filename); public delegate IntPtr fftw_plan_dft(int rank, [MarshalAs(UnmanagedType.LPArray)] int[] n, IntPtr arrIn, IntPtr arrOut, DftDirection direction, PlannerFlags flags); public delegate void fftw_execute(IntPtr plan); public delegate IntPtr fftw_plan_dft_c2r(int rank, [MarshalAs(UnmanagedType.LPArray)] int[] n, IntPtr arrIn, IntPtr arrOut, PlannerFlags flags); public delegate void fftw_destroy_plan(IntPtr plan); public delegate bool fftw_import_wisdom_from_string([MarshalAs(UnmanagedType.LPStr)] string wisdom); public delegate bool fftw_import_wisdom_from_filename([MarshalAs(UnmanagedType.LPStr)] string filename); public delegate void fftw_export_wisdom([MarshalAs(UnmanagedType.FunctionPtr)] WriteCharHandler writeChar, IntPtr data); public static string fftw_export_wisdom_to_string() { // We cannot use the fftw_export_wisdom_to_string function here // because we have no way of releasing the returned memory. StringBuilder sb = new StringBuilder(); FftwInterop.WriteCharHandler writeChar = (c, ptr) => sb.Append((char)c); GetDelegate()(writeChar, IntPtr.Zero); return sb.ToString(); } static string GetVersion() { const string VersionPrefix = "fftw-"; const byte WhiteSpace = (byte)' '; byte[] prefix = Encoding.ASCII.GetBytes(VersionPrefix); int i = 0; StringBuilder sb = new StringBuilder(); FftwInterop.WriteCharHandler writeChar = (c, ptr) => { if (i < 0) return; if (i == VersionPrefix.Length) { if (c == WhiteSpace) i = -1; else sb.Append((char)c); } else if (c == prefix[i]) i++; else i = 0; }; // This is only called on initialization, so no synchronization/lock is required GetDelegate()(writeChar, IntPtr.Zero); return sb.ToString(); } } }