本帖最后由 shane007 于 2023-9-4 12:57 编辑 0 x8 Y% }6 u. _( |/ h, B8 o% I6 g
' M$ i- ^/ h' t- `# N# M
本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,
" E# v: I/ N p. o2 D( G! T 使用时需要輸入语言,源文件路径和目标文件路径: f" B3 R% b0 _
最后是输出srt文件
2 ~3 C8 G* X7 N% L" O
" [( ?! c: \+ j) m* J( _- m代码如下
$ t; C: Y8 B5 B$ N5 F) o以下这句用多线程可以增速,否则很慢
1 x, A- r, B: w" V4 R3 e: m. g J以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。
. ^' P l4 @+ S! l p0 ?
5 _* R% }8 i% {0 u0 S8 Q5 V" A& u; ^3 z3 E* o% B
- var builder = factory.CreateBuilder()
+ V- D/ W. Z% q4 z+ T6 x% X( J - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码
& B7 Y( g6 ?1 y' ~; y ?( g7 b
/ j& E4 d" P+ x5 S0 m- // Licensed under the MIT license: https://opensource.org/licenses/MIT( P6 Q+ z& z/ R0 q2 A, C4 o# m9 h
- 1 W% ]5 ^( Z/ f* T/ ]- P( J
- using System;
( r* T) N: d+ [1 l( N9 f/ I+ e - using System.Diagnostics;
1 R! y+ W' V8 T5 ^$ n - using System.IO;2 L, ?9 u; X }3 E; a! I
- using System.Threading;& a# u, k( R! G$ y a( `5 c; X
- using System.Threading.Tasks;
. \% O p: z8 Z# g% V7 f - using CommandLine;/ L% y v$ j: n0 ]/ h
- using Whisper.net;$ o9 ^- K0 G* I: y, r) y4 R
- using Whisper.net.Ggml;
7 E: G' I1 p3 J - using Whisper.net.Wave;: `% s, y* c5 @3 m: A5 J# f
# n' b4 R& F" a) W9 R& E4 B/ A- await Parser.Default.ParseArguments<Options>(args)7 b C2 b3 e. B k0 G# W
- .WithParsedAsync(Demo);
/ S8 i. A* Y6 k0 D8 z
% p6 F! ^7 {0 K! g- async Task Demo(Options opt)) M! Y- R) ~" A& \; a. X! {
- " O# Z. \0 b2 d, {1 B0 \. K
- {" C+ i3 ^0 m; p: M; S
- if (!File.Exists(opt.ModelName))
- }- B! v) [* K7 ^* U+ {+ Z - {
; Z7 R' t! ], t" K - Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");) `- @6 |& F5 y# [' F
- using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);
) g9 K+ X* J: e# A. | - using var fileWriter = File.OpenWrite(opt.ModelName);; ~' \" n) S; k1 c9 L$ _+ L ^2 f
- await modelStream.CopyToAsync(fileWriter);
- f9 I% r# Q) u3 n& m7 \ - }
- n7 M! l" K+ n5 f; z% n - # o/ i) j" N. B2 F4 J
- switch (opt.Command)
# v4 z+ r+ z: @' ] F - {+ v) {3 ~: y" E3 R+ t; K" }6 I+ w
- case "lang-detect":- V8 J8 q6 t/ R
- LanguageIdentification(opt);' @9 q; f4 O8 B
- break;, q W# Q+ q' I, F" x2 a2 B7 T
- case "transcribe":
! D( Y, v- }+ I8 v) K) s- D - case "translate":+ o/ {+ C# g4 i
- await FullDetection(opt);1 `8 z1 \3 K q& k, T H5 _
- break;$ d( M/ @) h% R# m( w. _7 J4 Y0 ~* N
- default:
8 ?# o. V# m3 J: E2 d" h; u - Console.WriteLine("Unknown command");
+ ~3 V2 H5 [: U% S; X, Q - break;
$ x) J5 n( G" s- J! O$ r - }
. k! ^) D; Z# _, ?0 u& N0 M0 D - }
3 U9 C8 Z+ J& n4 I# C
0 z- z% [1 P4 G) a0 y- void LanguageIdentification(Options opt)
+ v( L' B/ U5 B6 ^2 w# e- I; ~ - {
# m9 m6 f& S% V" b0 ^/ y2 S# \2 o - var bufferedModel = File.ReadAllBytes(opt.ModelName);
4 Q7 f; O% J- E. r& O, N
5 f" \0 m# f( y8 k6 ~8 h( V( u6 i- // Same factory can be used by multiple task to create processors.! Y" C3 h/ k h7 m1 Q$ V2 m- S+ _3 ?
- using var factory = WhisperFactory.FromBuffer(bufferedModel);1 |# n% Z# y- @; |( `; I
- & u/ T( W; m' K! w3 F$ B( @
- /* var builder = factory.CreateBuilder()
8 c" M, _" I9 Q7 J* `* ]" R - .WithLanguage(opt.Language);*/
; x1 d1 V$ w% B5 Q' L3 ?) x - var builder = factory.CreateBuilder()6 e) i: T- c" `4 w. v
- .WithLanguage("english");
( C$ ]% V; `/ d0 ]" C& P: ` - using var processor = builder.Build();
9 k1 L# W3 l* r6 M' K% k
# k( `& R- b9 Y* g: K# k- using var fileStream = File.OpenRead(opt.FileName);
' a3 b) a j3 \/ [; x - , b1 k6 C( v: z! o J/ q6 q5 W4 Y
- var wave = new WaveParser(fileStream);
/ d0 L- ^% Z _! O; I5 O$ t5 Z
1 |8 ^0 \# l# v5 W4 l$ m8 d7 F+ z- var samples = wave.GetAvgSamples();5 G7 P4 m l- i# ~' a4 C
" N7 D0 t( G1 ]! ^/ z. T6 E- var language = processor.DetectLanguage(samples, speedUp: true);6 c$ @, X$ p: `6 {
- Console.WriteLine("Language is " + language);
, t+ Z+ q f" h( E- e% @ - }# r2 c6 J* z, Y1 ]2 n6 P/ h0 [' V8 P
- ( b3 V, Y3 G* h! d5 f
- async Task FullDetection(Options opt)
% b" a1 k) d* S* a* I7 x - {
/ X, d9 W& y: z' a2 \# } - // Same factory can be used by multiple task to create processors.
5 D7 i5 t8 U. U8 J8 w9 W4 l$ K - using var factory = WhisperFactory.FromPath(opt.ModelName);
% U6 J i4 \; q7 Z - . I7 J7 K$ V P7 o1 ^
- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);
/ Q& w5 _: V: f9 C) h4 H/ b - Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");
/ C* f* K* w2 U8 k9 @ Y - string languageOption = Console.ReadLine();) Y( i# x# } \, i/ w3 v
- var builder = factory.CreateBuilder()
: `0 Z% ^: G6 Y( W) | - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);, S/ c g+ x# s t0 O; _+ a1 t5 B D# J2 I
( d2 f7 p# x% M+ Q- if (opt.Command == "translate")7 ~0 E* Z7 S h( R
- {) t% t6 y* N3 L4 I
- builder.WithTranslate();
5 V0 e" _" x6 u2 H - }
2 m$ u4 N( c7 n7 r$ b( o4 Y& v
7 i. g' o+ N9 A$ ~- WhisperProcessor processor = builder.Build();1 J: }4 b4 ?+ i) I; c
- ( {2 {. L& [3 f# X2 b
- Console.WriteLine("请输入wave源文件目录:");( c8 ?+ S+ z6 ^8 p' B2 U4 w% z1 `$ p
- string sourceDirectory = Console.ReadLine();
9 ~9 a c6 j7 N* \" U+ d - , O3 e# z7 N; Q* L7 h" \$ ~
- Console.WriteLine("请输入目标文件目录:");
5 Q* Q3 U1 q" k7 N3 H - string targetDirectory = Console.ReadLine();0 V# y. n! J! E0 K" P
5 [- Z1 o7 v) {( Q- w; z" h$ S, V- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))+ d- E- D B2 P7 k% L# z
- {
7 t1 [0 v% `) ?* J4 ?. U, h - Console.WriteLine("目录不存在,请检查输入的目录路径。");5 O4 [4 Q; F( G8 d% W
- return;
* L) w: J% q7 y2 M8 ]# _ - }
d2 a B) ?2 e - 5 L5 F) q: A( \; ?, U
- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);: U5 l: r! R- Z% J9 L# s% |2 M
- 5 k0 K- H% U4 J' g5 Q
- Console.WriteLine("处理完成!");
0 {' u( r+ D, k' h1 O- ^
! O/ m7 Y4 n) E1 P8 A8 d1 u7 H- }
! j6 _/ n- ?/ H" C - static async Task ProcessFilesAsync(WhisperProcessor processor,
, y# K# X3 k6 q2 C6 N0 E - string sourceDirectory, string targetDirectory)
3 G j- b0 ?; e - {8 }$ }, H, _: s$ a& E6 _
- var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);& r2 P) J) f# S3 c& M
% u: |, E" ^7 ]; K+ c- foreach (var sourceFilePath in files)4 v' w% T; p. @+ s
- {1 f( h5 H1 H' ?% r( B. g, ~1 u; _
- string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);) {; Y! T0 a% N1 f$ {1 G
- string destinationFilePath = Path.Combine(targetDirectory, relativePath);* W0 [& Q& @8 ]1 h F& p1 I. c5 N" }
- destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");
t1 l; r. f/ X1 u - , \6 @; r/ f0 u4 C- d- @6 h
- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath)); ?- U2 p% m: e- J
- 0 R/ W& p3 U# I" J ?( i; Z) g
2 A- E8 l& r5 |: I- O- if (!File.Exists(destinationFilePath)): \6 ?2 r5 T4 L. {, V9 \+ u
- {
! e( B+ V8 I0 B3 s - Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");0 ~4 o9 C) I* K
' _: J4 ]2 m/ L1 y- using var fileStream = File.OpenRead(sourceFilePath);* ^# l9 ^6 O" t. A- ^ Q
- var segmentIndex = 1;1 |5 |+ R7 @: K1 \6 s! |/ q
- using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter
" x. I( _* B9 A. o& o - var startTime = DateTime.Now; // 记录开始时间3 |3 J0 w1 h& o. H- V
" r! J I! d0 h) B" C* ^* y0 j- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))
+ ^3 W3 X: P- k( B5 h& q3 f8 M - {6 ^% L% g7 d8 S1 d0 J/ c
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");' [' W8 t& c. y8 ^% {3 p+ T9 V) x! O
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
, ?) Q. }, o& L. n. D2 F - Console.WriteLine(segment.Text);$ s4 X& X; i R
- Console.WriteLine();( E+ @/ p) U0 v- b9 ^$ R
9 M# ~( y/ d* ~8 f: L6 t& t- // 将srt内容写入文件
s' N2 l( Z/ S) a - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");5 T1 Z( R7 M6 Y: k3 ?7 n8 M
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
+ i7 k: q1 i1 k% f4 [: K - await writer.WriteLineAsync(segment.Text);
- B5 d U6 G- \. t! L% [+ w - await writer.WriteLineAsync();$ B) ^5 `3 N0 j b% ~
- ( {. x$ \* q, @8 c! k
- writer.Flush(); // 立即保存srt文件
& p, f$ d2 }& U7 c( T- ?4 s5 w& d
2 T+ x( o w, \- segmentIndex++;
S0 v; @& n' x. Z* d - }: l/ j$ |: d5 F5 L4 M
9 b7 w8 r5 D: ~9 k! t- var endTime = DateTime.Now; // 记录结束时间
; K6 g* o- v& I - var elapsedMinutes = (endTime - startTime).TotalMinutes;9 d4 c5 h+ K7 ^, F
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");% H. y# |; o0 w) O9 q$ I9 N
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");
' I1 d: o2 S! N5 y( o - }
* _" M3 [1 d' n3 Q% G: Q - else {
/ `7 t o; v1 T+ O - Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");
8 P& Q+ U2 e m - }6 T8 H8 A% x" x/ f" V" K
- }
% N# i, X$ T/ M1 s - }; _+ Y$ F6 }( v8 t7 a/ N3 ^
- public class Options
' j8 z9 J7 }1 ]3 N - {
+ h* }/ U9 m7 m8 I9 Z+ H - [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]* G% U$ h! J$ h/ W' F5 s
- public string Command { get; set; }( g1 }6 d; u5 J; W' b
- ( r5 D7 |2 y7 B" ]8 t
- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]! ]/ @4 f& ^6 c
- public string FileName { get; set; } L8 d/ C2 _% A) s8 P
% s$ A, I' e) g5 }2 m- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]. e( k8 L0 |- H
- public string Language { get; set; }
; H8 S0 x$ H6 W2 Q1 m7 f - 2 k) {4 p% v3 s5 s5 [& \- H: t: u
- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]
) Z# s% m+ X% }5 d9 @ - public string ModelName { get; set; }8 G5 Q1 W+ v) F9 N; e! g
- 6 W0 @' o! o* S4 N4 R" R
- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]
3 l8 K+ o" m( S! @8 X - public GgmlType ModelType { get; set; }
# n- c8 x% h9 l: U- i8 C3 V* Q - }. k6 L, ~7 U. E* ]
复制代码
; K' ^0 B7 h9 x$ e4 ?. O# L2 Q
2 q! k: c+ t5 s; p2 ~7 r' i- q' t) W+ S( y3 P$ B
8 |$ G6 E6 @2 N5 ]
|