本帖最后由 shane007 于 2023-9-4 12:57 编辑 , q* f2 j+ T4 {
4 L7 Q: |$ ?( F- y* r, O7 Q6 A
本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,
9 K J, ~- b& [& u0 X 使用时需要輸入语言,源文件路径和目标文件路径 @7 P D1 [- R
最后是输出srt文件" P* [4 J1 E& p# r6 l7 M4 G, s
9 J* s* U- p' D7 D
代码如下
" L+ S% ?# V% w5 O- a& R/ {! `' {以下这句用多线程可以增速,否则很慢
2 S; ]( M x* e' G' a5 G以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。
/ I, R9 S( f9 x& o( T4 m
: I" e' }, H$ a# Q( D" k
/ G( Q: ], {! H7 W. M- var builder = factory.CreateBuilder()
' Y& ^* b# h8 G( i' u- {( `0 E - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码 " y h4 M& Y: b
, O; e7 b" d& ~, j8 E/ y
- // Licensed under the MIT license: https://opensource.org/licenses/MIT- U4 U0 k j3 {6 h; k8 Y4 M
) ~1 S7 F7 p5 ~! t( x- using System;
/ O" N+ b7 U3 g& ?, b - using System.Diagnostics;
' Q: d# a: e0 }0 ?( ?$ a6 c6 n - using System.IO;
' \& ]) a. l5 R7 f - using System.Threading;8 P$ S) p0 |/ T" }1 B8 p
- using System.Threading.Tasks;7 P, h! X8 e4 C! I6 ?2 f
- using CommandLine;
2 Z. W& G P5 T, M - using Whisper.net;
& {+ n; y# Q n5 I9 Y9 x3 c - using Whisper.net.Ggml;
3 K% X; v# l5 d0 l9 F - using Whisper.net.Wave;- E1 Y* g* {) @$ N+ C
( @2 @% f) e+ Q/ B; q: I& }+ l6 L1 l- await Parser.Default.ParseArguments<Options>(args)
( F, A- ?9 o. r! L8 z( F [ - .WithParsedAsync(Demo);0 A( D* c/ e% Q( D
- % K6 c2 s% s% I/ [4 n9 X7 M
- async Task Demo(Options opt)
0 n: t( ~4 c0 o. ~. ?0 I - 9 w; M' G4 T2 V3 @
- {
`/ x, Z3 |7 G - if (!File.Exists(opt.ModelName))/ W2 ^, F% m6 b7 p; z" m
- {
4 X3 v% G# C0 U) E) o. q' A - Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");, C5 A2 W) \* R& c( i# d
- using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);& v8 ~! h9 d4 h; j, {: p6 M+ g4 _
- using var fileWriter = File.OpenWrite(opt.ModelName);
% l8 f3 J1 j) _8 K) B$ ^% k - await modelStream.CopyToAsync(fileWriter);3 m) H: r$ Q. V$ t; k7 y
- }9 g. Y4 I7 G1 B5 N
- : F' v. _ O% ?" y6 i* {# Z
- switch (opt.Command)5 c2 \" _2 j4 d+ x$ \1 K2 c
- {& h% O* K, U4 r' E* b# j8 x1 _+ @
- case "lang-detect":4 @ j7 k4 U" u2 n
- LanguageIdentification(opt);
! i8 ^" S- G' t7 | - break;
* ~! m( N& L6 y' Q - case "transcribe":% D N1 W* [ J5 O: E5 H8 K
- case "translate":
: d0 O5 N) p" ]2 H- z; P - await FullDetection(opt);
% L% L3 D2 E- q) |% X - break;$ b: h' D, K8 Z! s0 S
- default:
5 ^+ R5 ?, l6 V3 Y8 D! e, M9 K - Console.WriteLine("Unknown command");
, K- V8 c9 ^( N$ U - break;
9 o& q( {; C6 I$ l+ [: V - }
* T; t$ B* [0 Z% c3 j - }
' H' R- G7 f4 Z - , H/ }& _& J- V+ j$ T, h; D
- void LanguageIdentification(Options opt)+ ?4 Q7 n8 U4 N. _7 D, g
- {5 d2 [$ H, x6 T3 b
- var bufferedModel = File.ReadAllBytes(opt.ModelName);. \5 `6 A; l& v) N
t$ i! w. r# ~9 P9 ~- // Same factory can be used by multiple task to create processors.0 }* K- W& ?. N8 ]9 l
- using var factory = WhisperFactory.FromBuffer(bufferedModel);
0 u6 b$ T+ A7 }5 R3 _
3 Q) t% N9 y/ X" @) F1 f: S- /* var builder = factory.CreateBuilder()
" b0 h- g$ A% H - .WithLanguage(opt.Language);*/
. U+ x L# x6 x0 H% T. e/ [ - var builder = factory.CreateBuilder()
9 a6 e, h l; |) R5 D# r - .WithLanguage("english");
* t/ N6 u. P) o u' @ - using var processor = builder.Build();
6 K8 W; P' D; n% _2 Y2 W) P
: g9 A* W8 D- y+ R* s- using var fileStream = File.OpenRead(opt.FileName); R0 i2 C6 R5 Z* {6 J9 S6 E; f
- 0 w& |# z5 O" f! y y
- var wave = new WaveParser(fileStream);: K y+ K" e8 t b$ s! |
; b3 v% L1 I# h% S) Y- m6 T- var samples = wave.GetAvgSamples();
^: Y" Z" Y: i - 7 p" A5 ~, |& `1 H0 c/ }' G$ h
- var language = processor.DetectLanguage(samples, speedUp: true);
5 C) J2 X k* i5 a3 Q1 T - Console.WriteLine("Language is " + language);
6 k. l1 l( q3 v* h3 n$ l% | - }
2 ^" [+ s, ]' b% h0 A% ~ - , o/ B& O- j; g% | Z* E! T
- async Task FullDetection(Options opt)
* M, j5 I3 @! s9 O8 l9 ] - {, u& J2 e( K* \% _3 ?
- // Same factory can be used by multiple task to create processors." d' Z7 m' K# Q% J8 K5 D' ]
- using var factory = WhisperFactory.FromPath(opt.ModelName);( ~! l+ B4 _9 I9 m8 c; H4 ^, h
0 Q* v) J0 m0 S- // var builder = factory.CreateBuilder().WithLanguage(opt.Language); J) K& T- |# j; }, p" a
- Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):"); Q) Z; b) q5 ^( O
- string languageOption = Console.ReadLine();3 ]8 V8 ^- D3 o6 S. |. c/ M2 j! ?
- var builder = factory.CreateBuilder()
0 T4 j1 U. {5 |% ?6 w& {4 [% F* P - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);, H4 n( D+ x. G: a! k
- ' c; d3 M) }% R, X0 n
- if (opt.Command == "translate")$ q7 \" B6 [! h' t1 S" }. R1 q. g; ^
- {8 G; \5 B* f' G1 \0 h$ O. v8 v6 k3 Q* [5 {
- builder.WithTranslate();1 H# I" A4 k1 C8 @8 S
- }1 k$ h6 q4 M, ?$ }. R% t8 q7 P
- 4 @1 M* E/ t& s# U3 z+ J
- WhisperProcessor processor = builder.Build();% i+ D A* P+ ^; h) k( {: a; m
- 7 a6 z3 I5 n/ Y
- Console.WriteLine("请输入wave源文件目录:");
: {2 {/ I$ d% ?. A# V# O - string sourceDirectory = Console.ReadLine();/ b5 X+ c5 n$ Y
- % R( }4 R( ]) M
- Console.WriteLine("请输入目标文件目录:");
. O( ]: N Z1 {% S5 t - string targetDirectory = Console.ReadLine();& [) @# i" J/ v6 q& t% F
3 A8 a6 \, Y% w" k) t: G- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))- l; P* n M3 ^' [; {2 t
- {' O9 i ~* g. k& h
- Console.WriteLine("目录不存在,请检查输入的目录路径。");4 M% k* I$ P$ c/ O8 L4 Q8 u
- return;6 m0 T9 _3 f* C% \+ u5 d; @ F
- }3 C5 k& E: w" b
* D# p# e; w$ A( a( | H' p" ?( p4 K- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);, Z3 P6 {0 E$ M2 K. V" |4 W
! ]" U( | L. X; G9 r- Console.WriteLine("处理完成!");/ V' g) v1 O) F; e
1 e0 s8 ~/ h4 S1 S$ f4 J- }# f O, T) u) N2 y( x( q- j
- static async Task ProcessFilesAsync(WhisperProcessor processor, % E# x+ j3 B, `
- string sourceDirectory, string targetDirectory)1 t$ u- d% ~( k5 w
- {4 Q% }/ K# T4 n$ z" o% H
- var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);
) ?9 z5 w2 |6 ]# a3 r8 n* n# E, L - ( J, u* Q+ f$ b
- foreach (var sourceFilePath in files), {8 N% V3 `' f2 t% l8 J/ |- X. U
- {2 V/ y" o' H; e- _/ d
- string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);
4 |' v6 _) s: \& A* m/ E7 t - string destinationFilePath = Path.Combine(targetDirectory, relativePath);6 ` b* I; A! L% I# z2 P, I
- destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");
+ I) ?1 [: [! o: l8 s& W3 p' H
" ~# z+ }$ i2 y1 m- R- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));
6 F, z( g+ _8 d8 v% K - ' F! Y! e- B/ i) R. b* w
; [" m% Z8 S9 h( I4 K) v0 g, ~) u- if (!File.Exists(destinationFilePath))$ r) ^0 @" B, W
- {
* z' U& U9 o0 [: g - Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");
! e' ], W) d* B( w3 Y, {5 [ - , D7 {( e, J+ r" i, y) L. X
- using var fileStream = File.OpenRead(sourceFilePath);7 X$ ~' ?- t0 g1 g6 r
- var segmentIndex = 1;% x2 q% n; }, T1 _
- using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter
1 {- O/ n0 b5 ]" m+ R; X - var startTime = DateTime.Now; // 记录开始时间6 N- v5 B2 | A4 B _( t
: i5 _4 V; j# R5 q# E0 w- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))
5 G7 A6 T/ v+ `7 C; W* u - {( \2 S! c$ I7 l' @" V3 X& Y9 d
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");. I# r* E: a f$ {+ h; j5 {
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");. M' T) e/ \$ I3 b( V
- Console.WriteLine(segment.Text);
8 G0 O$ ]1 n6 G7 D; O3 I- @' c" d - Console.WriteLine(); v2 N8 I/ \! b4 M( N9 M
- 3 J9 I! G+ V' X/ _# L& n9 l; N
- // 将srt内容写入文件
- _' p' t2 e: v$ F - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
5 g- x1 Z; R7 {+ i- o q - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
) e1 z! o2 W0 q/ D5 d8 @ - await writer.WriteLineAsync(segment.Text);* C; N( s+ N2 t! o9 N0 j
- await writer.WriteLineAsync();% ?/ {! Q" l( {. E( y" M
- 5 F) J6 c: P' g5 L' E: D/ c* L
- writer.Flush(); // 立即保存srt文件6 E$ _& L$ k' h8 V" O# n# u
* Q% z4 e9 y9 @; P9 Q6 u* w/ p4 f( I! O- segmentIndex++;# Q+ b8 [/ s: V
- }6 @2 g1 \( p. ?3 x
) M3 C9 s6 P) f0 c1 v- var endTime = DateTime.Now; // 记录结束时间
; b9 R- m# }! d1 _2 g9 B, A - var elapsedMinutes = (endTime - startTime).TotalMinutes;) Y/ F* h9 X1 c: d% L; I& ] V
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");
. v( T$ W: z2 E3 F4 k" R- h# u - Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");; q* _1 n) k. v& x" ?/ k
- }
- m7 M( a$ H) e! t; z% C - else {
, _4 L. ?, t+ N1 S# M - Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");
g$ i% D+ `# c1 K4 A - }
, C! X0 I6 g1 m, c9 X$ p% B* u - }
) Z2 ^' q" h. d% m4 _. J. [ - }
8 X5 f, Y+ }5 Q - public class Options( w3 Z* K. j2 {3 Y6 T2 r2 D, i( r
- {$ E0 I. z" n) y. K2 }
- [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
# U" f% n* N/ ?6 @/ m; {; F - public string Command { get; set; }+ ~+ J* s1 X( t) J5 B
2 X9 _' Z9 L0 r- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]. z8 \* T- a; ?# x3 W
- public string FileName { get; set; }7 B) h; v" C1 O9 b1 k3 N- T3 L
- / d- H" _% E% y6 a
- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]
3 [; T$ L6 Z9 M' n6 i - public string Language { get; set; }
; q. M) B0 W' g - 0 b2 j+ F4 o/ A2 \% [! g
- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]% ]# r$ y: j$ ~% Z! }
- public string ModelName { get; set; }
F( D! x [; `$ p$ _0 R! E9 C
0 w @) j; W3 q- C3 ^5 |- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]
# r. O( Z) b$ A9 P( h - public GgmlType ModelType { get; set; }: c5 p1 i: d; ?1 L* y, g& {
- }
* n& \- ~2 ~3 D9 S
复制代码 8 \4 H9 O s4 p- Y& ^" v9 s+ ^
/ {: }7 _: ~3 f! q9 ~7 T& l
( x* t. R4 H2 |7 ]" E% v
4 K4 z$ P, |3 X5 F n+ ]8 \3 [ |