本帖最后由 shane007 于 2023-9-4 12:57 编辑
* b/ ~3 N) b5 Z& _" s7 Z
# x8 A# o# u- x H: t& c; c8 \ 本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,
4 _2 O q* {# L$ }4 Q 使用时需要輸入语言,源文件路径和目标文件路径) n) j* i% U; ?1 n/ k! @$ T
最后是输出srt文件
* I+ G5 W, Y# g$ z5 Z% e$ d
! ^0 L, \5 \" q, d! ]" P代码如下) j. Q+ i! P) Z P1 z% S
以下这句用多线程可以增速,否则很慢
n3 E" A0 c; c o8 i4 y1 u F. f以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。
( [$ v: B3 [6 O. l/ N6 `5 F3 T6 w# N1 F* m% s/ A$ c0 V
2 V' s7 S3 D8 l0 j
- var builder = factory.CreateBuilder()
9 P% X: j- `) T! [ - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码
) W# |# O+ O" j1 X: u. f3 {% m- I- j$ j$ j) B( T
- // Licensed under the MIT license: https://opensource.org/licenses/MIT
7 d1 s' l" ]* X9 k' |
3 O9 O+ Z) B& N- using System;
1 ~3 Q' u+ ?2 ? Z3 l - using System.Diagnostics;/ @4 N3 G5 S$ h. [' I! B# J/ O
- using System.IO;
! ^2 Y$ o! o+ b5 w0 t7 y. f - using System.Threading;: H; ?$ t# X* R( c
- using System.Threading.Tasks;" ~: I9 N1 ^5 ^/ F4 P
- using CommandLine;) U, y, {9 m8 j Q' s5 h& E; a; [
- using Whisper.net;4 P% w' g- i5 e2 n
- using Whisper.net.Ggml;$ a3 v, M5 `) f& Z- x7 v) w
- using Whisper.net.Wave;$ G0 w# I$ ?% P E6 {& [
- , Y6 k+ }* z. _; y6 w
- await Parser.Default.ParseArguments<Options>(args)5 B* f, _) L+ p6 t+ X8 r4 M
- .WithParsedAsync(Demo);
0 Y. k3 L+ a% D$ t7 n( A
) B R; L. | C. `$ A7 c8 L8 C" m- async Task Demo(Options opt)
9 V, [0 ^ P8 b! X
" Q. p' D5 M' v- J- {
7 s+ Y/ J3 ]+ ]+ d2 z - if (!File.Exists(opt.ModelName))
' S1 Q4 G3 k7 P. R: l) O - {
- b& D& G Z7 t0 Y- z - Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");+ d; i& x0 g' ~
- using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);4 M2 O- n) i; d L
- using var fileWriter = File.OpenWrite(opt.ModelName);0 }. t; L* ~! i+ K
- await modelStream.CopyToAsync(fileWriter);2 U% b4 Q5 C! b( n, L/ Q
- }
7 q" m/ _, ^2 n: ^
" v2 m# ]1 }' Z- switch (opt.Command)
' @7 W+ F$ x' A8 G6 s - {; Z0 ~; i$ c7 F( C1 }4 L
- case "lang-detect":3 e& f7 v7 f) q2 s! P/ m
- LanguageIdentification(opt);- k2 y; A) m* F. F( b
- break;
1 g( B- }3 S% }3 c& X# F/ [; }% p - case "transcribe":
2 f+ D' U; K! P1 U3 ]4 R - case "translate":
' @ n/ V2 E7 L! s$ |3 I - await FullDetection(opt);
$ J! o% G8 i8 D+ E: ^3 s - break;
1 p; z- U! h I) X1 V" w# r; ` - default:& r# n1 [7 H: T9 T& u) ?
- Console.WriteLine("Unknown command");* G( e/ q% d5 e- S* X* u! I/ B
- break;; C# } w/ G& l9 L( F9 K' [# f
- }4 s6 n4 Z: b" f6 `! E! f6 j: `' V
- }
! w0 L$ i$ D; M \: @ - , u. ?$ [3 E8 j1 ?; ~: O
- void LanguageIdentification(Options opt)0 f1 k( Z) c6 @* a8 `0 |
- {
$ z$ h9 x. m+ f" N - var bufferedModel = File.ReadAllBytes(opt.ModelName);. z6 Q! S1 x) V1 p8 c( \
- , y- D* L/ q" \; Z0 ^0 C
- // Same factory can be used by multiple task to create processors.9 G2 g9 V' R6 m3 ~
- using var factory = WhisperFactory.FromBuffer(bufferedModel);
9 _, |. j M( u7 w
2 G3 F W+ g: {- M- /* var builder = factory.CreateBuilder(), S& {7 d% U3 K5 Q- V5 z
- .WithLanguage(opt.Language);*/$ k5 Q" t8 a( _+ y' c: E. n k
- var builder = factory.CreateBuilder()
- i' w- H- ` c2 _+ X& B - .WithLanguage("english");6 R) R' q+ z) Y; A, d5 m5 F" z
- using var processor = builder.Build();
, q% L: v: o7 C+ C$ d q- A ?( u
; j4 S7 R* N5 y, t- W- using var fileStream = File.OpenRead(opt.FileName);
2 k1 D& a, c. M. v& U - ! M# j6 ?0 p3 H1 P
- var wave = new WaveParser(fileStream);
7 k* O' J+ A Z# Y5 g
: G9 P& S4 T8 v- var samples = wave.GetAvgSamples();
3 m J$ z7 e1 u; T - 4 P& X7 q, Q9 n# e5 f
- var language = processor.DetectLanguage(samples, speedUp: true);
. Y, y6 v+ ]4 }5 u/ F - Console.WriteLine("Language is " + language);% w. j2 E9 M) Z5 J2 c
- }
; }7 E4 m3 H8 m, p - & s2 r+ e1 L' K3 x( F8 g
- async Task FullDetection(Options opt)$ b* Y. M: _" e
- {
. _- [+ s$ Z$ S4 g7 F+ g# }, s6 { - // Same factory can be used by multiple task to create processors.1 Q3 f- E, k+ m3 ]
- using var factory = WhisperFactory.FromPath(opt.ModelName);
; C6 J: ~6 p- U7 T( d8 n) k
/ \& S H7 K2 R6 n7 }* I* N- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);9 u2 U, k+ f9 t& `
- Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");" P, L# N3 h* q1 w p; W( z
- string languageOption = Console.ReadLine();+ t6 Q6 p0 ^' j B
- var builder = factory.CreateBuilder(): }- l, d9 f& @1 _/ {
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
4 a& q& ^- u! G+ D6 }* V! X* E0 ` - $ x7 {( y0 e! u% m. y9 ^
- if (opt.Command == "translate")6 K% e/ [% O% C; T. s
- {
3 T9 e# V3 n2 a# U - builder.WithTranslate();
/ Q0 ?( @- j1 t( B - }
6 ^6 ~! y( h5 W
. V. M! T/ V- H+ o( s- WhisperProcessor processor = builder.Build();
( r& H# w: I8 C, z. L -
6 y' b8 u" }" k7 p' O1 Q - Console.WriteLine("请输入wave源文件目录:");! ?, R5 C: c* i& T
- string sourceDirectory = Console.ReadLine();) [! t2 Y2 a, \6 V( P
- 5 ?6 v/ E$ O) A" l8 E
- Console.WriteLine("请输入目标文件目录:"); {* u0 [, q! {1 i- e( _ ?% s
- string targetDirectory = Console.ReadLine();
`: o4 ]) [; U# F8 b
5 M# l6 `5 N- o3 N3 W" `- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))
: z: M+ S) N. `- e3 X" D - {, B3 l: T8 J! n* ` ~9 X
- Console.WriteLine("目录不存在,请检查输入的目录路径。");
, r" u9 g' E/ z& {2 G# _ - return;
) x/ k! I! V. V: w* ~ - }- q: i* k; l6 }* E' j8 l6 N* Y
0 w. ?! ]3 {/ j$ y8 z9 g3 B- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);' X0 b) p" M o) p6 k9 e. I/ U1 B
7 L5 x- r' s7 w6 Y- Console.WriteLine("处理完成!");' a0 m5 N5 u$ ]/ ^
- , O: u* ]( n6 m: m# j' V; B# Z
- }7 f( X% {' L; j+ G1 B* ]+ B7 |- w
- static async Task ProcessFilesAsync(WhisperProcessor processor,
" P/ @8 t$ E; a! D, ^* d: g - string sourceDirectory, string targetDirectory)
$ _0 l; a, F0 X) N: ? - {5 @' w1 e5 G0 `5 ~
- var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories); Z% m* ?* d- o( A
5 y( Y/ \% w7 p% ^! @- foreach (var sourceFilePath in files)# c8 M3 t- e; S' { e
- {
! @, m* j' N$ E/ S+ H - string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);
- D4 ?: N" U" u - string destinationFilePath = Path.Combine(targetDirectory, relativePath);, a: E9 k! Y( I; ]) p; U+ W7 y
- destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");$ f- G1 x q4 n5 U6 o2 _7 @
- - j# t% }! w7 k0 s$ m$ W
- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));6 S- s3 }7 G) @! ?( s5 T- }# b
- $ T, p: k9 J/ w" g
, s# u" f4 ~2 o% z$ ]- if (!File.Exists(destinationFilePath))* ]8 z) q7 ?' W
- {0 C' ?% E( r& _2 |7 n7 l! j/ \7 P
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");: J6 j4 M8 J+ D# W$ ?
6 c+ H$ ?7 X! a' r& x- using var fileStream = File.OpenRead(sourceFilePath);, d- g8 n. W, b+ r! D
- var segmentIndex = 1;& @7 n+ _7 J( D! ~6 S( A/ ?
- using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter4 N* T Z, d1 C/ A4 [2 \
- var startTime = DateTime.Now; // 记录开始时间7 }& z$ p r/ S3 @( `
- 1 V" N+ t# P0 @8 A( `& o& v
- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))
' h" V2 B! g: g) [ - {
' `% l8 z& O( n- n0 O - Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");. t4 y$ G! U2 x4 r3 N
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
* x4 P/ N$ K* B - Console.WriteLine(segment.Text);
# h1 l7 h0 a* n$ g# X) T# C3 f - Console.WriteLine();
( y% e' ~; i) s - 6 p% o8 K/ z* q) p( `. }
- // 将srt内容写入文件
- W/ a* S% t- E6 ~ - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
& @9 d4 I. j; r - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");+ @$ ^. U9 @- Y3 z y
- await writer.WriteLineAsync(segment.Text);/ n+ J* s0 [; U' C3 Z+ ?
- await writer.WriteLineAsync();% g8 Q7 h9 ~% M
- # g6 D' v4 s% {4 N
- writer.Flush(); // 立即保存srt文件9 n( G/ p5 J9 W
- " o* X6 I2 h7 y6 p
- segmentIndex++;
& r8 E- s( N% |* C, x3 W - }# {3 O: h6 q7 ~- D
- ; B# W l, D/ }7 \- d6 E
- var endTime = DateTime.Now; // 记录结束时间7 @$ G# J ` D) d* a& j/ s
- var elapsedMinutes = (endTime - startTime).TotalMinutes;0 j, p8 a9 z0 f# |
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");
7 [, z; l, s9 y; S3 u* } - Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");8 n1 v# H1 l6 P) B" X: X/ H. _
- }
3 l4 M8 J2 }+ i) T - else {
8 k% z3 U8 U9 h$ \ - Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");6 r( i, Y/ H/ f' z8 r8 s. N; ~6 N
- }
' ~0 s; V. W8 s& Z; R - }
: l* _$ {9 W: P6 i2 B - }# w' y( R. F9 S2 O8 L) ^
- public class Options$ h- [/ N8 G/ s" c) N
- {
! g# o* Y" p4 Q7 m2 [( r+ ` - [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]) f7 O* Y% [1 z) l, L6 } X
- public string Command { get; set; }
@" p( {8 s- g4 u+ V& V2 H3 c - $ ]. l/ |6 u0 l# @
- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]8 T5 s9 Q" Y- A, y& Z; T
- public string FileName { get; set; }& F# w' x2 W! p2 L9 W
R7 U' B! A- k1 G& i- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]
3 g; v( b; ~& ^ W - public string Language { get; set; }
2 y; z4 U% `3 O. G9 q1 {* U- J5 l
' T# p+ F+ J$ O2 |1 e# V6 K' Y- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]; C7 ]" o: [) T6 g9 P8 ~: \$ b" M
- public string ModelName { get; set; }
4 S6 \5 J8 N3 Q4 i
1 W* k' m) @/ u# p5 N- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]
( b) f2 N2 L2 C4 H v - public GgmlType ModelType { get; set; }
/ `1 ?7 j* Z& `0 Q, q' Y% A4 Z - }
: Y: O. E, M7 a* M% N/ p
复制代码 $ @5 {1 O! V4 Q: [" _
- e# R) O' w/ [: L% A w* Q. X+ g. f$ E
6 {( {( f' \9 T9 ]8 b4 n y- [4 y |