本帖最后由 shane007 于 2023-9-4 12:57 编辑
+ w( J' u1 Q: r& L7 {7 j9 d
W/ u" [; q3 k 本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,
4 H8 I- R# P& l1 B: y 使用时需要輸入语言,源文件路径和目标文件路径7 k7 ~& s. Z! d, Z
最后是输出srt文件
' }% e/ q4 E6 o9 k& w- |* R4 ]+ c/ B
代码如下
5 w% @, |* a/ w0 Z以下这句用多线程可以增速,否则很慢
; v( a0 ]: E* v5 U( U3 P/ t9 A+ }以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。7 p% h) e" T2 {! s# n$ b- x
4 v0 |1 U7 O1 ?$ X+ G
( r3 i$ O, d9 f: X1 u- var builder = factory.CreateBuilder(): O3 ]3 U! A/ I6 O* t2 w
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码
# B/ S/ S7 j- [0 Z: V a- W- \" L9 [- D5 D/ }; l0 u. }
- // Licensed under the MIT license: https://opensource.org/licenses/MIT: k5 L, U; g; k/ [6 A ~
- . V& N, @5 m: H p \2 R4 j
- using System;
5 d9 \& k: T% S6 }. D; z, L - using System.Diagnostics;7 C( ~) ]% q3 M- A6 m
- using System.IO;
8 l; m. L) u& h5 p - using System.Threading;
$ S; z% R+ h. n# b8 u - using System.Threading.Tasks;
% a# q. X! |. a/ U" a7 q - using CommandLine;" G+ o) O P* ]' b
- using Whisper.net;
1 a6 k$ T: h8 ~$ b0 @1 G% T, w8 s - using Whisper.net.Ggml;9 A5 J3 W# |" g& B$ s/ c; C& N
- using Whisper.net.Wave;+ a! s: |5 d* Z* ?; ^; F
( _# F( Q# H9 S2 }; V4 o6 I. a- await Parser.Default.ParseArguments<Options>(args)
0 v- L+ k* F# {1 h - .WithParsedAsync(Demo);
. m: g8 t7 M9 C( E6 o3 ?
8 q% D# @& y8 L) q# p& I- async Task Demo(Options opt)
3 @/ {% }# b9 E. z( b. M$ [/ v: n - 9 D/ J) s: H U3 b- W I) W
- {
1 C, H' L' @3 V: D# w- O& r+ u - if (!File.Exists(opt.ModelName))
' ^! K, `+ w( V$ K - {3 g- N8 L$ j. i4 E$ \
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");
% e5 c% H5 R/ e$ e) W' m( z - using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);
) u( y: A1 I6 u2 ?; _0 X - using var fileWriter = File.OpenWrite(opt.ModelName);7 E3 G6 H3 K, v6 N- `
- await modelStream.CopyToAsync(fileWriter);3 B2 d: f3 w H% B0 p5 U
- }
# a5 v( g( }6 k& J) \8 ]
+ o9 e3 W/ Y5 J6 a+ }8 c; G* n- switch (opt.Command)
& l0 F2 p0 F) _& S2 g! [6 b - {
/ c3 ~, f' j) j. }+ x( r6 J - case "lang-detect":" [% \9 K9 I9 s
- LanguageIdentification(opt);
& D% O' N* X# i9 x4 G* {- B+ [- e P - break;( Y7 F& H& r! [# I) I/ i
- case "transcribe":
: W, j3 y) V8 W# x- Q( |4 [1 L% b - case "translate": Q. ~) R8 c- O9 ~! {' N8 R
- await FullDetection(opt);3 c/ A Z) `4 ~& a1 c d' }' M2 \, s
- break;
* W) w9 r& t: h# g3 ]9 @( u - default:; z! K" t+ |& h7 H9 b
- Console.WriteLine("Unknown command");
0 o' u" P& I5 |6 J! Z7 y - break;
; I7 C. Y% V. K; N6 ? - }! G1 ~! m7 v# |
- }
, U! h+ s7 p2 F" e: B) I
1 m5 {3 K- c8 B- void LanguageIdentification(Options opt)
: S/ u( n T& ?1 S - {; b, V1 Q3 w" v i* w( x4 q8 ^4 k
- var bufferedModel = File.ReadAllBytes(opt.ModelName);
' Y" \: @* P+ p! U - # m( Z5 n1 q4 J6 C" ^* X3 X+ {
- // Same factory can be used by multiple task to create processors.6 a2 f3 j: \- X3 S! X# _
- using var factory = WhisperFactory.FromBuffer(bufferedModel);* a' z3 K" y. I. H
5 ?7 ?$ S2 f8 K. q- /* var builder = factory.CreateBuilder()% D2 G8 e, Z2 z X, g q
- .WithLanguage(opt.Language);*/8 y2 T+ k5 |+ d. ~ a7 c
- var builder = factory.CreateBuilder()8 A9 n9 c, l& p; G d! E! H
- .WithLanguage("english");
0 P3 J6 `/ k ]# S h: r - using var processor = builder.Build();
4 Z0 t# C$ G5 e* n/ t& }; @
1 }3 `# w4 H5 Z p( t3 d( k5 d, N- using var fileStream = File.OpenRead(opt.FileName);$ S5 a$ G7 W' b) L6 j
) p' S. z; B9 q. ?; D7 H: `- var wave = new WaveParser(fileStream);
4 M4 w3 T$ o6 X3 W2 H( o m+ |; v" [ - 0 A. i* a8 G1 O& D( P3 R
- var samples = wave.GetAvgSamples();
7 E2 f9 a# {& F Y - & V$ }( i% a+ W" x0 ?" d- Q
- var language = processor.DetectLanguage(samples, speedUp: true);; j) G* |6 y8 U, Z9 {9 V* S3 M
- Console.WriteLine("Language is " + language);
/ r: O+ ~: t @+ Y - }( G3 _6 s7 n; J
- 3 t. ]9 ~; U+ o6 ?) z) e
- async Task FullDetection(Options opt)$ a& b8 G0 S, o2 G
- {5 h4 ] s( ?* g+ U
- // Same factory can be used by multiple task to create processors.
* u0 w& N; z2 B1 n ]" Q6 U - using var factory = WhisperFactory.FromPath(opt.ModelName);: K& J& u! i2 }" t! @% r1 {5 Z: w+ p
- 2 z3 S' G S) t1 ]. z
- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);0 ^( E" X6 Y) k; x B5 ]) Z
- Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");+ e: O2 v: h% ?$ b8 U% ?
- string languageOption = Console.ReadLine();0 O5 A/ [; s9 v3 o0 N2 i( c/ {
- var builder = factory.CreateBuilder()
( _# g4 g/ \ Y5 C& r( w - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
- ~ `; r, [0 F! Y# H" J0 y - 6 v6 [/ B, `2 t4 l
- if (opt.Command == "translate")
& R/ w# u* d/ i - {+ [# s0 {) W) g/ ^" Y+ i" A) y
- builder.WithTranslate();4 a* f3 k3 o1 h
- }' a6 M) x' L9 \( i3 L2 A
- ' Q2 v4 Z) A* b
- WhisperProcessor processor = builder.Build();3 j8 H" W& |& _
-
4 b/ V6 r# R0 j3 E5 x" M ^ - Console.WriteLine("请输入wave源文件目录:");
2 W% U* ]3 g1 F$ R: ~ - string sourceDirectory = Console.ReadLine();
! M. _4 G' R1 q$ r - ; v2 W7 }! j$ z
- Console.WriteLine("请输入目标文件目录:");6 `) c7 c$ X( @6 S4 g% N6 o; Z/ r
- string targetDirectory = Console.ReadLine();
. }( `8 I& R% r! S' j- N - - P' z' ]9 s2 ~$ O% `: Q
- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))- {3 H, w( k/ m5 V, v# @7 O o# P
- {
) m: D( `" s: n/ ` - Console.WriteLine("目录不存在,请检查输入的目录路径。");
9 J( S! f* v# T0 n8 _: H - return;. G0 o! l4 A. ]5 J8 k1 m
- }% {' p% u( r3 @5 H3 X# f# G5 f
; r4 J% r1 _* m5 G2 o* q5 z9 c- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);" k* D% Q& T% m: s
* Z$ y% O! @2 W- Console.WriteLine("处理完成!");
$ I5 w* O b% Y' j/ O - : [+ ^3 p. v& g9 f9 e9 V
- }
6 i# Y. i- t$ h+ F% z7 D8 l$ m( _+ \ - static async Task ProcessFilesAsync(WhisperProcessor processor, 2 l7 J1 J( H; n, Z5 v! V4 h' j
- string sourceDirectory, string targetDirectory) `; C8 M: m- D2 D* U9 k
- {
2 E$ D! }' f' h3 A3 h - var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);& j6 q& d: f, t, j* o
- 7 G8 P% `' y0 F/ Y* G
- foreach (var sourceFilePath in files)
( S4 X2 e Q3 l, n1 U - {0 g. C9 d: X- P8 i9 M7 g5 p0 o
- string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);
* F) Y8 Y. i3 Y$ I0 S - string destinationFilePath = Path.Combine(targetDirectory, relativePath);
" U, P; w+ m) K J0 e - destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");
6 S5 v5 N P' ^9 F7 t
, U) [" D* @" g2 {- ~4 d# d- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));
6 b( y+ H, Y/ L
# K, L2 C0 U( N5 a- + d/ Y+ P; R+ M- ?$ P& T" f
- if (!File.Exists(destinationFilePath))9 }8 s# t2 R1 z, N7 D, e3 J( w/ j
- {
9 g4 s6 l; a v. d; {% ] - Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");' U1 S' B, x+ w# ~1 S# J) }
% S& _! z2 _0 ^3 J. E0 k- using var fileStream = File.OpenRead(sourceFilePath);' G* q$ c& ]: k0 A' N
- var segmentIndex = 1;
, |* P$ {: w0 h0 B" `" c - using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter
4 o9 P- L% {8 _ - var startTime = DateTime.Now; // 记录开始时间. b9 e) h1 L0 V
4 |1 }% r: E. g) P6 r0 h- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))
. ?% Z/ T$ a/ u: t' G: {0 N. _ - {# ~! @! X- t; j( ?; n
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");: }2 a1 R0 J6 h- C
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
: _9 Q# A- K9 O - Console.WriteLine(segment.Text);, x, M2 H! p# h* P; b% O E
- Console.WriteLine();9 R6 A8 s9 _. P" Q2 p2 }* a
- 4 L1 K6 P, z6 c. t
- // 将srt内容写入文件
! m$ _4 M& ~8 k/ \* }! B - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");/ s5 L3 Q9 b( Z9 y. N
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");5 _6 ^. m: \8 F* B
- await writer.WriteLineAsync(segment.Text);
$ ]9 G0 j$ _# I: ~% \/ k2 o* @0 y/ V - await writer.WriteLineAsync();
1 i1 W# R) h- y. `2 U - % q2 V2 A9 ~2 N6 S5 r& S
- writer.Flush(); // 立即保存srt文件% {0 M" d$ r/ p
/ v: j5 l" x: M$ j# R- segmentIndex++;
) ]4 v8 E! X: I l9 V - }
' r c; ~- ~3 T, u' ^7 u1 o - ( s$ {1 o- a3 q; t
- var endTime = DateTime.Now; // 记录结束时间1 F6 d/ I4 v& \* ?* R5 a
- var elapsedMinutes = (endTime - startTime).TotalMinutes;
# e& Z6 N6 ]" l& Z8 c- ^# } - Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");/ Y" | o$ y) ~$ \, l- ?. m2 Q3 V
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");3 L( `0 {" l# N% i
- }1 Z- x" }" e7 o) c; a D: ^
- else {
A3 x3 ~" j a& v: Z - Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");5 \. v1 E, v+ G7 L' {- V( s/ ]
- }
3 v( k8 B( g( U% g U$ x8 a - }: L& J( K, q! |& k! ?2 Y/ B
- }
( q/ K+ O5 D( A8 x - public class Options0 z/ E3 R8 Q& z x( z' h/ \/ m5 [
- {/ \, e; X! t% d; H# Y N `3 t
- [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
! j ?& t6 U* a$ o( J$ x% U: t& B - public string Command { get; set; }- f4 H# r" f0 }- G J* S
- 8 o$ [; {8 k: Q
- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]
9 ?; [. E5 u: X2 X% R) D" F - public string FileName { get; set; }
0 u8 D; T) O0 t* C0 s) m, W9 k - 8 R2 n, r+ h3 ^5 Z7 ?( N
- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]; j9 {5 F/ D6 x1 [/ U$ I
- public string Language { get; set; }* c- W0 H/ l4 I [4 \
! d, L$ X0 i/ G- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]. h& ?% Y# g( l8 m6 \' j3 D/ u
- public string ModelName { get; set; }
$ f) u3 R5 Z' {) Y* T - ) A& c7 Q- x1 [- e( F# }3 t% U
- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]
; F3 J% f+ l# J( e) M - public GgmlType ModelType { get; set; }; X9 c' g# `, ]
- }
% j# g; g- U$ }; i0 t% {3 t9 a8 Y
复制代码 . l6 K' S# { x+ t+ S; v/ v
7 U& Y) e' M; L+ x
, h: e9 j5 R R9 s
* f7 e. Q& K0 ^ Q+ F; G! N) H$ s |