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
3f3a9a1
master
1#include "textWriter.h" 2#include "../../ComLightLib/comLightClient.h" 3#include <array> 4#define WIN32_LEAN_AND_MEAN 5#include <pathcch.h> 6#include <atlstr.h> 7#include <atlfile.h> 8#pragma comment(lib, "Pathcch.lib") 9 10namespace 11{ 12HRESULT replaceExtension (CString & path ,LPCTSTR inputPath ,LPCTSTR ext ) 13 { 14path = inputPath ; 15 16const size_t len = (size_t )path .GetLength ()+ 4 ; 17wchar_t * buffer = path .GetBufferSetLength ( (int )len ); 18const HRESULT hr = PathCchRenameExtension (buffer ,len ,ext ); 19path .ReleaseBuffer (); 20return hr ; 21 } 22 23// Abstract base class for text writers 24class Writer 25 { 26protected : 27CAtlFile file ; 28virtual HRESULT impl (const Whisper ::sSegment * const segments ,const size_t length )= 0 ; 29 30public : 31HRESULT write (Whisper ::iContext * context ,LPCTSTR audioPath ,LPCTSTR ext ) 32 { 33CString path ; 34CHECK (replaceExtension (path ,audioPath ,ext ) ); 35CHECK (file .Create (path ,GENERIC_WRITE ,0 ,CREATE_ALWAYS ) ); 36 37using namespace Whisper ; 38 39const eResultFlags resultFlags = eResultFlags::Timestamps | eResultFlags::Tokens ; 40ComLight ::CComPtr < iTranscribeResult > result ; 41CHECK (context -> getResults (resultFlags ,& result ) ); 42 43sTranscribeLength len ; 44CHECK (result -> getSize (len ) ); 45const sSegment * const segments = result -> getSegments (); 46 47return impl (segments ,len .countSegments ); 48 } 49 }; 50 51HRESULT writeUtf8Bom (CAtlFile & file ) 52 { 53const std::array < uint8_t ,3 > bom = {0xEF ,0xBB ,0xBF }; 54return file .Write (bom .data (),3 ); 55 } 56 57void printTime (CStringA & rdi ,Whisper ::sTimeSpan time ,bool comma = false ) 58 { 59Whisper ::sTimeSpanFields fields = time ; 60const uint32_t hours = fields .days * 24 + fields .hours ; 61const char separator = comma ?',' :'.' ; 62rdi .AppendFormat ("%02d:%02d:%02d%c%03d" , 63 (int )hours , 64 (int )fields .minutes , 65 (int )fields .seconds , 66separator , 67fields .ticks /10'000 ); 68 } 69 70const char * skipBlank (const char * rsi ) 71 { 72while ( true ) 73 { 74const char c = * rsi ; 75if (c == ' ' || c == '\t' ) 76 { 77rsi ++ ; 78continue ; 79 } 80return rsi ; 81 } 82 } 83 84inline const char * cstr (const CStringA & s ) {return s ; } 85 86HRESULT writeString (CAtlFile & file ,const CStringA & line ) 87 { 88if (line .GetLength ()> 0 ) 89CHECK (file .Write (cstr (line ), (DWORD )line .GetLength () ) ); 90return S_OK ; 91 } 92 93// Writer for UTF-8 text files 94class TextWriter :public Writer 95 { 96const bool timestamps ; 97 98HRESULT impl (const Whisper ::sSegment * const segments ,const size_t length )override final 99 { 100CHECK (writeUtf8Bom (file ) ); 101using namespace Whisper ; 102 103CStringA line ; 104for (size_t i = 0 ;i < length ;i ++ ) 105 { 106const sSegment & seg = segments [i ]; 107 108if (timestamps ) 109 { 110line = "[" ; 111printTime (line ,seg .time .begin ); 112line += " --> " ; 113printTime (line ,seg .time .end ); 114line += "] " ; 115 } 116else 117line = "" ; 118 119line += skipBlank (seg .text ); 120line += "\r\n" ; 121CHECK (writeString (file ,line ) ); 122 } 123return S_OK ; 124 } 125public : 126TextWriter (bool tt ) :timestamps (tt ) { } 127 }; 128 129// Writer for SubRip format: https://en.wikipedia.org/wiki/SubRip#SubRip_file_format 130class SubRipWriter :public Writer 131 { 132HRESULT impl (const Whisper ::sSegment * const segments ,const size_t length )override final 133 { 134CHECK (writeUtf8Bom (file ) ); 135using namespace Whisper ; 136 137CStringA line ; 138for (size_t i = 0 ;i < length ;i ++ ) 139 { 140const sSegment & seg = segments [i ]; 141 142line .Format ("%zu\r\n" ,i + 1 ); 143printTime (line ,seg .time .begin , true ); 144line += " --> " ; 145printTime (line ,seg .time .end , true ); 146line += "\r\n" ; 147line += skipBlank (seg .text ); 148line += "\r\n\r\n" ; 149CHECK (writeString (file ,line ) ); 150 } 151return S_OK ; 152 } 153 }; 154 155// Writer for WebVTT format: https://en.wikipedia.org/wiki/WebVTT 156class VttWriter :public Writer 157 { 158HRESULT impl (const Whisper ::sSegment * const segments ,const size_t length )override final 159 { 160CHECK (writeUtf8Bom (file ) ); 161using namespace Whisper ; 162 163CStringA line ; 164line = "WEBVTT\r\n\r\n" ; 165CHECK (writeString (file ,line ) ); 166 167for (size_t i = 0 ;i < length ;i ++ ) 168 { 169const sSegment & seg = segments [i ]; 170line = "" ; 171 172printTime (line ,seg .time .begin ); 173line += " --> " ; 174printTime (line ,seg .time .end ); 175line += "\r\n" ; 176line += skipBlank (seg .text ); 177line += "\r\n\r\n" ; 178CHECK (writeString (file ,line ) ); 179 } 180return S_OK ; 181 } 182 }; 183} 184 185HRESULT writeText (Whisper ::iContext * context ,LPCTSTR audioPath ,bool timestamps ) 186{ 187TextWriter writer {timestamps }; 188return writer .write (context ,audioPath ,L".txt" ); 189} 190 191HRESULT writeSubRip (Whisper ::iContext * context ,LPCTSTR audioPath ) 192{ 193SubRipWriter writer ; 194return writer .write (context ,audioPath ,L".srt" ); 195} 196 197HRESULT writeWebVTT (Whisper ::iContext * context ,LPCTSTR audioPath ) 198{ 199VttWriter writer ; 200return writer .write (context ,audioPath ,L".vtt" ); 201}