本帖最后由 shane007 于 2023-9-4 12:57 编辑 4 _; W/ _& R! f; V6 k& ^' @7 ~
9 |. Y+ u! n5 v7 k& ~7 d/ x 本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,3 ?9 B9 q r l( ?1 a% ]
使用时需要輸入语言,源文件路径和目标文件路径+ @; Q# k0 W4 ?7 M7 ?
最后是输出srt文件
* G) |- Z$ ]( J: E2 J, G F& Q
" k" G$ C$ p0 q4 E- N代码如下
. Z3 e" s0 P1 t% v/ t以下这句用多线程可以增速,否则很慢# e5 h# ^4 F. o& L) h# f1 o
以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。
4 g; {) a& E+ n' S$ z6 w$ l
. o/ S! j& w) q+ D3 ]9 L4 i6 n2 H1 [% Z5 w
- var builder = factory.CreateBuilder()
6 Q6 o. \9 u6 B& X4 K - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码 8 j6 z4 \6 @8 x; \. e
0 C( @5 `; a& ]* t9 y# j: [ L$ \
- // Licensed under the MIT license: https://opensource.org/licenses/MIT# [, d- z7 M9 S+ ^% b. x! I: Q
- 6 H. K5 G# K" f$ a: S& t# T
- using System;" P9 v6 V8 T/ a( P! ]/ a& B
- using System.Diagnostics;
. [. [1 H5 r$ k6 a - using System.IO;
' z. ~* b: } w: R2 Y: | - using System.Threading;! i7 c( R6 N- \: `% g# Y$ @
- using System.Threading.Tasks;
$ Z' _" |) ]2 ?& p, R+ z - using CommandLine;- J! ^* l7 l& t6 P$ f5 x! M8 s+ V) ^
- using Whisper.net;0 y6 `% P. d6 A3 H, D# R
- using Whisper.net.Ggml;
. T( T P7 @& o5 @! A1 K# ] - using Whisper.net.Wave;
, y) Z2 k; z# B - 0 s8 e( E' Y. N( c" y
- await Parser.Default.ParseArguments<Options>(args)- Q8 Y0 m! L. B4 N) D2 |
- .WithParsedAsync(Demo);, `- b5 Q) j( J+ r; L
5 @. g1 K& @/ {$ S- async Task Demo(Options opt)
& ]* B, [& b/ w$ @+ } - 5 M& I1 a1 H0 \: K. y$ ? a9 x; A v
- {% Z2 l, a% h4 }+ q4 ?
- if (!File.Exists(opt.ModelName))/ E* I9 ]( u8 f' U) s5 x7 n4 Z, S
- {
0 N4 R# p8 ?5 B' V2 @ - Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");
' I& r, b1 S' W. o; A6 ]# | - using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);3 m* t: c/ q$ j# Y. ~ |9 N; V
- using var fileWriter = File.OpenWrite(opt.ModelName);
. v& R/ @* z U; u* Z - await modelStream.CopyToAsync(fileWriter);
" b. e: l1 V0 m) v - }0 Z& o0 E( Q( X( v7 A1 O5 M7 w0 D; ~
( |9 y; Q, O# Z/ _8 ?- switch (opt.Command)3 R0 u# ?6 V+ b! G) a
- {8 r' A8 _) |# V* K! a. H& l Y
- case "lang-detect":6 q( W3 y5 H3 ]" _% e: ^
- LanguageIdentification(opt);
$ c. ]9 U' W; F+ y s4 s - break;4 ]# ^2 h+ L$ {0 \2 F
- case "transcribe":
9 E; \/ M0 K3 P t - case "translate":7 O' U& z9 y( }6 Y$ E% d9 }- g. ^
- await FullDetection(opt);9 x2 M, w3 a2 ^1 `! a
- break;
& Z0 k# r! ?* @' X# y/ ?( j - default:
$ s+ [' P3 d# C; L - Console.WriteLine("Unknown command");
* u1 u1 ]1 J: t6 t1 ~8 |* j8 N7 n - break;
( C7 F7 W5 b+ \* I8 A; ` - }9 j/ _# L- r: t& e4 F
- }
# L! l% T0 o9 \6 d, S - , q% |0 s }) x. h0 }2 M4 `9 \8 o
- void LanguageIdentification(Options opt)
- R2 B. |( X) I9 ` - {
, h: E$ u& n) S1 x - var bufferedModel = File.ReadAllBytes(opt.ModelName);: K' S- P; h, G* a) k. r
- % M( d5 c( G" g- x
- // Same factory can be used by multiple task to create processors.1 Z8 e- P: Z" m
- using var factory = WhisperFactory.FromBuffer(bufferedModel);
9 d, Q3 K$ u$ k9 `1 u( ^
# N- f9 s) u: d7 @& {* w2 \% A- /* var builder = factory.CreateBuilder(); F# y6 }% o! o* B- L: P; i
- .WithLanguage(opt.Language);*/' W# [# d% g& `3 N" B
- var builder = factory.CreateBuilder()
9 u8 f% |3 f0 P" m t - .WithLanguage("english");- ]) n3 Z8 \7 t2 o! h
- using var processor = builder.Build();# \$ M! h/ N- j% F& U8 {) S$ T' y
' x+ c! o9 t- z. s3 V; G- using var fileStream = File.OpenRead(opt.FileName);
! ]/ Z5 h) k, W4 p& S( `! o1 z% a- p4 _
+ A. k5 n* h: l' l1 D1 w- var wave = new WaveParser(fileStream);; R6 H* |1 k3 n6 d
- . q1 f W7 }* h) |# C6 @
- var samples = wave.GetAvgSamples();
( f8 g' ~' ]5 q1 v$ D8 f/ y2 A
( p2 t- H0 `+ D5 r- var language = processor.DetectLanguage(samples, speedUp: true);
$ [# {; P. g+ o1 [; g - Console.WriteLine("Language is " + language);
: T* P6 A! k7 k9 e - }
s5 O: C! F4 m+ h( u$ z
1 i! n" X$ s3 {+ i6 Q$ y& v- async Task FullDetection(Options opt)
0 \( m7 A; h0 |$ x0 R1 Y. v# s - {
# J6 v3 L g5 Y& }( ^ - // Same factory can be used by multiple task to create processors.
2 B! ?4 G2 e$ a. V: @- ~5 s - using var factory = WhisperFactory.FromPath(opt.ModelName);% N3 y+ _2 d- ]: c1 a- Z9 a- A5 N
0 r8 H3 Y( P8 W0 N7 ]- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);% A' y, y: O8 Q' {: H
- Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");8 a7 `6 i) p% P- x# G7 Y/ q) [ j
- string languageOption = Console.ReadLine();
: D: c5 D& a+ I' } - var builder = factory.CreateBuilder()0 F. H9 k4 h0 A6 y
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);2 d* w5 p! Y5 _# @( b
- 1 h) D( k. S1 h# L$ @, k8 U: k
- if (opt.Command == "translate")1 V ~3 W% W- @
- {
! d9 t# d ]% ^7 c: L - builder.WithTranslate();
( j F+ q# D" X& W - }
' s1 d9 x5 X2 H2 ~" E, r8 F - ' e$ _' m& P# y- R8 ~
- WhisperProcessor processor = builder.Build();
2 B. n8 a1 G P6 q% d -
/ a- g/ [8 G' f% g& L+ m - Console.WriteLine("请输入wave源文件目录:");
% b1 e/ A! l& y0 \# D. P: N0 F - string sourceDirectory = Console.ReadLine();& K. _" d% \* j2 x) V- V
- % K: u0 l% p$ w/ G1 c C0 V. l/ }
- Console.WriteLine("请输入目标文件目录:");
( W$ z% W" B4 M7 p4 {+ H6 R - string targetDirectory = Console.ReadLine();8 E% @5 n4 D/ N9 _) r: I+ l9 f
: W, y/ k. T2 C7 H- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))4 J$ l/ f4 X4 F k& p+ o
- {# q$ |/ a5 i6 u7 v, S' B( s& x6 w' u
- Console.WriteLine("目录不存在,请检查输入的目录路径。");
5 I" U9 Y/ q2 u - return;) [8 K% v4 _% y U
- }
& X, {; B5 d4 j
3 }0 h# R- y0 M8 d" Z+ I Y- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);
( l; W% N R) a' K5 d
# b" s9 D5 y% m: Q3 p0 A" ?; F- Console.WriteLine("处理完成!");
# t9 b9 {6 r4 N* s: `1 `
0 g7 q& g% W/ m6 V- }0 x, m: N# O) Y
- static async Task ProcessFilesAsync(WhisperProcessor processor,
6 J/ |4 z) J- z& S$ h( j - string sourceDirectory, string targetDirectory)
! D: t( `- k2 e - {
6 K/ {' C; S0 W" l - var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);0 i l0 e7 ^; P; }7 N
6 p+ ]7 M+ f* r/ p0 J. O- foreach (var sourceFilePath in files)6 v& ] A& y, h: U9 l
- {
# s+ m* ]0 i) n2 P5 A6 y9 [' O0 R - string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);
" N; F4 i6 Y+ R M+ n - string destinationFilePath = Path.Combine(targetDirectory, relativePath);" |& o0 I+ E) J8 c
- destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");
. b; v, h; E+ ? - 3 ?7 o0 m& y+ m* C/ K$ L
- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));2 ?2 H+ o* o7 W% U
- & P, E7 j0 b! O2 _
- ' \. i1 v# D, |- v" n8 p* F# R: {* @
- if (!File.Exists(destinationFilePath))
j% j+ c& M# Y$ t1 Q - {
- F; l/ _) w4 f1 K5 t - Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");0 ^! A2 G0 _7 q: ?! v8 x0 ]
( _7 m1 Q: J0 N/ e6 G5 I- C" q, e- using var fileStream = File.OpenRead(sourceFilePath);
2 u- u, @& e; K" ] - var segmentIndex = 1;
, B; N& h- l" G4 {7 m O - using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter( K. d7 `4 a+ U8 s- J; l1 d
- var startTime = DateTime.Now; // 记录开始时间8 e: n: `: W/ g2 B, _% E5 l+ X0 q
- i t$ k4 r5 C9 V$ D' c
- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))$ n6 s0 S8 V# d( H
- {+ c% l2 z% a F U0 ]! O
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
! k: z% z k/ E+ A* o - Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
! D L! d# p8 y - Console.WriteLine(segment.Text);
( C* p' E' `) S7 w6 v# z- }' `% m - Console.WriteLine();
5 F, @/ [( i6 c5 R1 w - / |' Y& q. L, _8 u
- // 将srt内容写入文件
1 k% H( ]3 s1 X9 b - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");1 m$ ]' M* ~2 A- N
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");' m5 T3 K& ^$ m' a& q1 S% @. ]
- await writer.WriteLineAsync(segment.Text);
& R# P; f4 c6 Y& G W - await writer.WriteLineAsync();0 L0 r6 W2 {$ R
- ' T3 W I& v2 U* U4 A; o
- writer.Flush(); // 立即保存srt文件
9 }3 w, \! g ]% c& S - : J' @2 i9 P& y
- segmentIndex++;
% H6 E( b8 x" D* Z - }
t* q6 D0 t3 h' w
2 ]) ~6 T; h! Y0 {3 |' l E% p# Q, z- var endTime = DateTime.Now; // 记录结束时间
- V+ J$ B4 S) T2 c# k4 h - var elapsedMinutes = (endTime - startTime).TotalMinutes;' M( j% E! e- u0 z! C }
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");
/ }6 e; n. E3 ~, s/ T4 v - Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");% T% e' h9 E3 m- l
- }
5 j( Q. v0 @& B8 G0 Q* N: o - else {
3 Z5 R/ M q" d, y/ `& |/ L: J - Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");
- q, W. e* x/ `* J$ ` - }9 B }6 R( w T" p
- }
; ?) t6 m# A+ D; o B# [ - }
7 ]+ R5 \( p2 C& h; Z - public class Options
4 L$ v" Z1 [# w- q* U - {4 {6 s& I4 K$ L4 i
- [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
9 P6 i2 a; r( b W ? - public string Command { get; set; }
6 U+ v4 \7 E4 D - 7 U8 A+ z# v, x5 J- X
- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]
' X8 E' w! K* i9 _" U/ ] - public string FileName { get; set; }* b5 } Z C' c! z6 t
3 n6 M* H" q8 M6 p- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]
4 ~6 v, g( a, A9 i - public string Language { get; set; }8 I6 e% K$ a! A7 c$ @ H
) V8 |! Y& \4 T. U q- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]! T- S6 B9 ] u$ T, b, ?* E' h
- public string ModelName { get; set; }
5 K' M- a' H' b( \) g# \
6 c3 O8 N. L! p, N. G2 Q$ r* ^- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]5 ^8 P' U0 u: @9 Z( |3 P u, p4 O" g
- public GgmlType ModelType { get; set; }
7 k' x* u- r$ M' X: d - }- x/ Q2 l' X6 i
复制代码 ( \) D9 L! I: X6 H6 {9 e
) T5 w5 a6 v8 S3 x a1 W
. j$ ] [/ u4 e" Y3 I, `- ?2 I9 w' D
|