using ComLight; using System.Runtime.InteropServices; using System.Runtime.Intrinsics.X86; using Whisper.Internal; namespace Whisper { /// Factory methods implemented by the C++ DLL public static class Library { static Library() { if( Environment.OSVersion.Platform != PlatformID.Win32NT ) throw new ApplicationException( "This library requires Windows OS" ); if( !Environment.Is64BitProcess ) throw new ApplicationException( "This library only works in 64-bit processes" ); if( RuntimeInformation.ProcessArchitecture != Architecture.X64 ) throw new ApplicationException( "This library requires a processor with AMD64 instruction set" ); if( !Sse41.IsSupported ) throw new ApplicationException( "This library requires a CPU with SSE 4.1 support" ); NativeLogger.startup(); } const string dll = "Whisper.dll"; [DllImport( dll, CallingConvention = RuntimeClass.defaultCallingConvention, PreserveSig = false )] internal static extern void setupLogger( [In] ref sLoggerSetup setup ); [DllImport( dll, CallingConvention = RuntimeClass.defaultCallingConvention, PreserveSig = true )] static extern int loadModel( [MarshalAs( UnmanagedType.LPWStr )] string path, eModelImplementation impl, eGpuModelFlags flags, [In] ref sLoadModelCallbacks callbacks, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Marshaler ) )] out iModel model ); /// Load Whisper model from GGML file on disk /// Models are large, depending on user’s disk speed this might take a while, and this function blocks the calling thread.
/// Consider instead.
/// public static iModel loadModel( string path, eGpuModelFlags flags = eGpuModelFlags.None, eModelImplementation impl = eModelImplementation.GPU ) { iModel model; sLoadModelCallbacks callbacks = default; NativeLogger.prologue(); int hr = loadModel( path, impl, flags, ref callbacks, out model ); NativeLogger.throwForHR( hr ); return model; } /// Load Whisper model on a background thread, with optional progress reporting and cancellation public static Task loadModelAsync( string path, CancellationToken cancelToken, eGpuModelFlags flags = eGpuModelFlags.None, Action? pfnProgress = null, eModelImplementation impl = eModelImplementation.GPU ) { TaskCompletionSource tcs = new TaskCompletionSource(); WaitCallback wcb = delegate ( object? state ) { try { sLoadModelCallbacks callbacks = new sLoadModelCallbacks( cancelToken, pfnProgress ); iModel model; NativeLogger.prologue(); int hr = loadModel( path, impl, flags, ref callbacks, out model ); NativeLogger.throwForHR( hr ); tcs.SetResult( model ); } catch( Exception ex ) { tcs.SetException( ex ); } }; ThreadPool.QueueUserWorkItem( wcb ); return tcs.Task; } [DllImport( dll, CallingConvention = RuntimeClass.defaultCallingConvention, PreserveSig = true )] static extern int initMediaFoundation( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Marshaler ) )] out iMediaFoundation mf ); /// Initialize Media Foundation runtime public static iMediaFoundation initMediaFoundation() { iMediaFoundation mf; NativeLogger.prologue(); int hr = initMediaFoundation( out mf ); NativeLogger.throwForHR( hr ); return mf; } // The .NET runtime uses UTF-16 for the strings, so we only need the Unicode version of this function. // The native DLL exports both Unicode and ASCII versions. [DllImport( dll, CallingConvention = RuntimeClass.defaultCallingConvention, PreserveSig = true )] static extern uint findLanguageKeyW( [MarshalAs( UnmanagedType.LPWStr )] string lang ); /// Try to resolve language code string like "en", "pl" or "uk" into the strongly-typed enum. /// The function is case-sensitive, "EN" or "UK" gonna fail. public static eLanguage? languageFromCode( string lang ) { uint key = findLanguageKeyW( lang ); if( key != uint.MaxValue ) return (eLanguage)key; return null; } /// Set up delegate to receive log messages from the C++ library public static void setLogSink( eLogLevel lvl, eLoggerFlags flags = eLoggerFlags.SkipFormatMessage, pfnLogMessage? pfn = null ) { NativeLogger.setup( lvl, flags, pfn ); } } }