yum-archive/TaSTT-Whisper
High-performance GPGPU inference of OpenAI's Whisper automatic speech recognition (ASR) model
git clone https://git.yummers.dev/yum-archive/TaSTT-Whisper
45e141c
master
1using System . Diagnostics ; 2using Whisper . Internal ; 3using Whisper . Internals ; 4 5namespace Whisper 6{ 7/// <summary>Stateful context, contains methods to transcribe audio</summary> 8public sealed class Context : IDisposable 9{ 10iContext context ; 11// Caching the results object here saves time spent in ComLight library creating these callable proxies over and over again for the same underlying C++ object 12readonly iTranscribeResult transcribeResult ; 13sFullParams fullParams ; 14sProgressSink progressSink ; 15bool disposed = false ; 16readonly Action < object > pfnBuffer , pfnStream ; 17 18internal Context ( Internal . iContext context ) 19{ 20this . context = context ; 21transcribeResult = context . getResults ( eResultFlags . None ); 22fullParams = context . fullDefaultParams ( eSamplingStrategy . Greedy ); 23pfnBuffer = processBuffer ; 24pfnStream = processStream ; 25progressSink = default ; 26} 27 28void IDisposable . Dispose () 29{ 30if ( disposed ) 31return ; 32disposed = true ; 33context ? . Dispose (); 34GC . SuppressFinalize ( this ); 35} 36 37/// <summary>Adjustable parameters</summary> 38public ref Parameters parameters => ref fullParams . publicParams ; 39 40void processBuffer ( object buffer ) 41{ 42context . runFull ( ref fullParams , ( iAudioBuffer ) buffer ); 43} 44void processStream ( object reader ) 45{ 46context . runStreamed ( ref fullParams , ref progressSink , ( iAudioReader ) reader ); 47} 48 49void runImpl ( object source , Callbacks ? callbacks , ReadOnlySpan < int > promptTokens , Action < object > pfn ) 50{ 51if ( null != callbacks ) 52{ 53// TODO [very low, performance]: the following code creates 2 new GC-allocated objects on each call. 54// Possible to optimize by caching these function pointers in static readonly fields, and use another [ThreadStatic] field for the callbacks object 55fullParams . newSegmentCallback = delegate ( IntPtr ctx , int countNew , IntPtr userData ) 56{ 57return callbacks . newSegment ( this , countNew ); 58}; 59 60fullParams . encoderBeginCallback = delegate ( IntPtr ctx , IntPtr userData ) 61{ 62return callbacks . encoderBegin ( this ); 63}; 64} 65 66try 67{ 68if ( promptTokens . IsEmpty ) 69{ 70pfn ( source ); 71return ; 72} 73 unsafe 74{ 75 fixed( int * tokens = promptTokens ) 76{ 77fullParams . prompt_tokens = ( IntPtr ) tokens ; 78fullParams . prompt_n_tokens = promptTokens . Length ; 79pfn ( source ); 80} 81} 82} 83finally 84{ 85// Reset these delegates. 86// Otherwise, this class will retain the callbacks object preventing it from being garbage collected. 87fullParams . newSegmentCallback = null ; 88fullParams . encoderBeginCallback = null ; 89 90fullParams . prompt_tokens = IntPtr . Zero ; 91fullParams . prompt_n_tokens = 0 ; 92} 93} 94 95/// <summary>Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text</summary> 96public void runFull ( iAudioBuffer buffer , Callbacks ? callbacks , ReadOnlySpan < int > promptTokens ) 97{ 98runImpl ( buffer , callbacks , promptTokens , pfnBuffer ); 99} 100/// <summary>Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text</summary> 101public void runFull ( iAudioBuffer buffer , Callbacks ? callbacks = null ) => 102runFull ( buffer , callbacks , ReadOnlySpan < int > . Empty ); 103/// <summary>Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text</summary> 104public void runFull ( iAudioBuffer buffer , Callbacks ? callbacks , int [] ? promptTokens ) => 105runFull ( buffer , callbacks , promptTokens ?? ReadOnlySpan < int > . Empty ); 106 107/// <summary>Run the entire model, streaming audio from the provided reader object</summary> 108public void runFull ( iAudioReader reader , Callbacks ? callbacks , Action < double >? pfnProgress , ReadOnlySpan < int > promptTokens ) 109{ 110if ( null != pfnProgress ) 111{ 112progressSink . pfn = delegate ( double value , IntPtr context , IntPtr pv ) 113{ 114try 115{ 116pfnProgress . Invoke ( value ); 117return 0 ; 118} 119catch ( Exception ex ) 120{ 121return ex . HResult ; 122} 123}; 124} 125try 126{ 127runImpl ( reader , callbacks , promptTokens , pfnStream ); 128} 129finally 130{ 131progressSink . pfn = null ; 132} 133} 134 135/// <summary>Run the entire model, streaming audio from the provided reader object</summary> 136public void runFull ( iAudioReader reader , Action < double >? pfnProgress = null , Callbacks ? callbacks = null ) => 137runFull ( reader , callbacks , pfnProgress , ReadOnlySpan < int > . Empty ); 138 139/// <summary>Run the entire model, streaming audio from the provided reader object</summary> 140public void runFull ( iAudioReader reader , Callbacks ? callbacks , Action < double >? pfnProgress , int [] ? promptTokens ) => 141runFull ( reader , callbacks , pfnProgress , promptTokens ?? ReadOnlySpan < int > . Empty ); 142 143/// <summary>Get text results out of the context</summary> 144public TranscribeResult results ( eResultFlags flags = eResultFlags . None ) 145{ 146if ( flags . HasFlag ( eResultFlags . NewObject ) ) 147throw new ArgumentException (); 148 149iTranscribeResult res = context . getResults ( flags ); 150Debug . Assert ( ReferenceEquals ( res , transcribeResult ) ); 151return new TranscribeResult ( res ); 152} 153 154/// <summary>Print timing data</summary> 155public void timingsPrint () => context . timingsPrint (); 156 157/// <summary>Reset timing data</summary> 158public void timingsReset () => context . timingsReset (); 159 160/// <summary>Continuously process audio from microphone or a similar capture device</summary> 161/// <remarks>It’s recommended to call this method on a background thread.</remarks> 162public void runCapture ( iAudioCapture capture , Callbacks ? callbacks , CaptureCallbacks ? captureCallbacks ) 163{ 164if ( null != callbacks ) 165{ 166// TODO [very low, performance]: the following code creates 2 new GC-allocated objects on each call. 167// Possible to optimize by caching these function pointers in static readonly fields, and use another [ThreadStatic] field for the callbacks object 168fullParams . newSegmentCallback = delegate ( IntPtr ctx , int countNew , IntPtr userData ) 169{ 170return callbacks . newSegment ( this , countNew ); 171}; 172 173fullParams . encoderBeginCallback = delegate ( IntPtr ctx , IntPtr userData ) 174{ 175return callbacks . encoderBegin ( this ); 176}; 177} 178 179try 180{ 181sCaptureCallbacks cc = default ; 182if ( captureCallbacks != null ) 183{ 184cc . shouldCancel = captureCallbacks . cancel ( this ); 185cc . captureStatus = captureCallbacks . status ( this ); 186} 187context . runCapture ( ref fullParams , ref cc , capture ); 188} 189finally 190{ 191// Reset these delegates. 192// Otherwise, this class will retain the callbacks object preventing it from being garbage collected. 193fullParams . newSegmentCallback = null ; 194fullParams . encoderBeginCallback = null ; 195 196fullParams . prompt_tokens = IntPtr . Zero ; 197fullParams . prompt_n_tokens = 0 ; 198} 199} 200 201/// <summary>Try to detect speaker by comparing channels of the stereo PCM data</summary> 202/// <remarks> 203/// <para>The feature requires stereo PCM data.<br/>Pass <c>stereo=true</c> to <see cref="iMediaFoundation.loadAudioFile" /> or <see cref="iMediaFoundation.openAudioFile"/> methods,<br/> 204/// or <see cref="eCaptureFlags.Stereo" /> to <see cref="iMediaFoundation.openCaptureDevice" /> method.</para> 205/// <para>It seems to work fine with <a href="https://www.bluemic.com/en-us/products/yeti/">Blue Yeti</a> microphone, 206/// after switched the microphone to Stereo pattern.<br/> With recorded sounds however, the performance varies depending on the recording.</para> 207/// </remarks> 208public eSpeakerChannel detectSpeaker ( sTimeInterval interval ) => 209context . detectSpeaker ( ref interval ); 210} 211}