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 );
}
}
}