本帖最后由 shane007 于 2023-9-4 12:57 编辑 . V ^2 G0 k7 ]1 y6 m
" v$ L+ a% n, |! n
本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,) f% N- K, O' U; T( [
使用时需要輸入语言,源文件路径和目标文件路径1 J/ D; h+ E( K3 d d) A2 g
最后是输出srt文件: J0 b0 K5 l+ L/ n2 r& r( A! j0 U
/ j& x5 l2 |- f F& r( {) a
代码如下6 o3 X' B, L: I: `: N% a }
以下这句用多线程可以增速,否则很慢
1 r, ^( c' @( y" l( p$ _以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。2 Q) r5 ^$ `4 ?9 O. ~0 V) t+ j2 h
3 s1 A$ A5 i0 @/ N* _# |0 f& T2 g% G: N2 A! b3 c; J. H' @
- var builder = factory.CreateBuilder()
* w/ q9 r! ]3 v% E; @0 x& r/ ~" {) l - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码
5 H( f, h' q5 C2 n. A* `" l0 ^
$ s) g/ P, M( j5 ^( F6 @( n. X- // Licensed under the MIT license: https://opensource.org/licenses/MIT7 F5 _ V$ @' J9 }: H
- & k9 ]- }, G% P) H, B3 F$ F
- using System;& o7 y6 s$ g% w+ K1 K* F3 q; e
- using System.Diagnostics;
" B, R8 V9 j! t - using System.IO;
- F5 L' h- _0 u7 I1 q% z% y& q; l - using System.Threading;) `$ ]6 \1 z' ?' a: Y
- using System.Threading.Tasks;+ @3 P3 w. |7 D) {+ N! _3 c
- using CommandLine;! z9 M( p; }0 @. M/ M& ~
- using Whisper.net;
9 [ r, a5 G. b7 i2 | - using Whisper.net.Ggml;9 h/ D7 L" w# a" Y5 ]( I% P
- using Whisper.net.Wave;
/ W# [7 Z. f! W2 l7 j7 ]4 u
, e) M2 y) [# g' S1 w$ ?) B- await Parser.Default.ParseArguments<Options>(args)
' N1 Z+ `+ A" W - .WithParsedAsync(Demo);
- T; E+ o; K( y$ H; e, _8 q/ e E - , y& f7 I0 e1 h% ]$ j+ |0 U
- async Task Demo(Options opt)
" q" z$ X- V0 A( X4 {: o( g/ ? - & Z" t6 F' J) O' p
- {0 A4 K' q2 ]. n; _, [2 Q8 [
- if (!File.Exists(opt.ModelName))
/ G ^" Y/ v. z0 T - {4 L. F1 i* r4 n( _% b
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");: h& ^% \2 I7 j; K. b$ `; A
- using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);5 T/ \% R- R5 v# p
- using var fileWriter = File.OpenWrite(opt.ModelName);
: D' {9 [6 Y6 Q' s: P( D, U4 I' N - await modelStream.CopyToAsync(fileWriter);
% j# h3 |3 ^7 r8 S! T; h+ L c - }9 G6 E: r2 V r5 }( E1 j4 H
- B# | x8 C# G5 F; \: T- switch (opt.Command)
' X) U9 w ^0 h5 F, O7 w/ q - {$ a+ ` d: s( L3 Q
- case "lang-detect":( I; U4 R0 Q4 H2 |2 t. h
- LanguageIdentification(opt);) _; E! U4 M: j' D" i3 N! h
- break;2 w3 J# V1 E( b3 z# Q/ a/ i
- case "transcribe":
I( q8 n n+ ~* i - case "translate":
) n9 @5 n2 v3 u - await FullDetection(opt);
, |3 P2 c+ L5 J/ t2 [- |: C* Q6 t - break;
: R$ Z) x0 |1 G4 L- N - default:
6 z2 |2 N- }, O0 @$ \ - Console.WriteLine("Unknown command");
. X* O/ j3 a' x; k9 w - break;
3 K2 M0 k" O, Q: E/ W - }
+ M" m. ]) A, O1 y% U g0 E - }
; ]/ F D! {+ r; f6 y& U, B" |+ S8 R
0 O6 m% h% q+ N, P* a9 |- Q- void LanguageIdentification(Options opt)) T7 p' T1 s6 }6 g) @/ {& F
- {
* O: K- ]( O/ i0 @0 {, ^ - var bufferedModel = File.ReadAllBytes(opt.ModelName);
% x }/ |+ w$ `' \7 {- p( l
0 c; v! y' @0 M/ J1 E0 a0 P- // Same factory can be used by multiple task to create processors.* l' E" N5 d; j, s3 W
- using var factory = WhisperFactory.FromBuffer(bufferedModel);" c1 X$ t: v7 h0 l1 H2 H
- : V: ~4 q' e7 _* z+ d2 J: \
- /* var builder = factory.CreateBuilder()
) ^0 {3 h$ D) E: B4 n2 ~ - .WithLanguage(opt.Language);*/3 z, v) E+ d$ p! z: H5 a, F; u
- var builder = factory.CreateBuilder()
; k3 U" n' z# e. _( O - .WithLanguage("english");
/ D6 v- b" y# L4 f) D5 `; t- j" n - using var processor = builder.Build();4 _5 ~7 d( t) n/ m
6 }8 d, V- L/ A. _. F- using var fileStream = File.OpenRead(opt.FileName);, U, [$ n5 Q. j2 _! ?
- - s3 k: ?* S1 L
- var wave = new WaveParser(fileStream);
: q/ @0 Z9 w1 J( [% c0 `) ^
# L2 a; L. _* d4 r- var samples = wave.GetAvgSamples();; D* U" h& Q! n1 s% J9 U1 m9 h
( \; [4 V3 a, h9 N; e. q# W3 i& j- var language = processor.DetectLanguage(samples, speedUp: true);7 P" p8 s/ j- w. F0 q
- Console.WriteLine("Language is " + language);2 x4 i3 j5 @. v4 {* ^+ ^ R
- }6 S7 V% o! b2 k/ _) `( {( n) S, c
; t, m" [; o' d* E' e5 N- async Task FullDetection(Options opt)
6 W4 g+ f# o1 _6 d' w: N2 [5 A2 } - {
" V* y4 s) }- i& s7 C) f/ E - // Same factory can be used by multiple task to create processors.+ u7 X5 ~: I4 X8 ~7 N; r. B* |
- using var factory = WhisperFactory.FromPath(opt.ModelName);' w3 L; L! a! \) J/ `# G3 ^0 b7 T7 w
: n) s t% D4 P/ R6 \& r5 b- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);% e- U0 \0 w+ L& u/ y/ E
- Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");8 h2 W6 d, y8 F0 d! t
- string languageOption = Console.ReadLine();
2 R3 b" I* u! R1 j5 {: Y - var builder = factory.CreateBuilder()9 \6 x6 ^: c' ?6 c8 J
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
1 v7 @$ F8 c. h1 A# ` - 0 A$ u' y" v& O+ J+ r# _
- if (opt.Command == "translate")9 N+ P f5 Y( a8 U. {* W. r
- {
+ S; S; e. U' s# O8 r; s - builder.WithTranslate();9 P2 [4 L/ s J z4 ~
- }
5 g$ ^0 }0 X- F+ v# p' e8 Q3 @9 s
" i B( q) V2 {, Q' `8 o1 H) H- WhisperProcessor processor = builder.Build();
( s; S2 M, x9 y- `- v& } -
& @- B2 L! S: T; N - Console.WriteLine("请输入wave源文件目录:");
4 ^" }% c, `, p( g - string sourceDirectory = Console.ReadLine();+ |: b5 Z9 U# ?
- : s% I& H- a6 S6 U+ d; b" Q
- Console.WriteLine("请输入目标文件目录:");8 W7 d; ^- E8 D1 o A2 m4 l- W
- string targetDirectory = Console.ReadLine();% e7 V9 }# W1 M% L
- 8 V! N0 f1 a8 Z$ p' _: Y8 `1 ?$ g4 R# Z9 O
- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))
& b0 x/ f- [( ~. \2 k - {
+ S7 w8 w l8 d+ |+ b - Console.WriteLine("目录不存在,请检查输入的目录路径。");
, p7 Z0 H6 d& a* b0 Q z - return;* Z* N& f! x( K: O g
- }" Y5 t$ h, W) P- E% x2 L1 z
- & P: F& F N" `) H
- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);
" u* T2 k( N2 j! b' O9 V, a4 {: v
1 a8 B$ z- u8 f6 P& t0 Z- Console.WriteLine("处理完成!");$ _$ d* g" Q9 d9 v5 h5 G; L3 g0 ]/ ]
- 4 F2 ]' \. L; T9 d2 K8 v( \7 {6 @7 }
- }
9 E* V2 i+ W: G C8 U) a - static async Task ProcessFilesAsync(WhisperProcessor processor, " W k7 K$ |- ]% d& |+ K; x
- string sourceDirectory, string targetDirectory)" M( j! o" Y! X i( [# j- Z
- {; {2 a9 Y8 }; ]/ ~' X% T6 A5 c s5 U2 L
- var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);
# t. _* y7 O7 R1 R4 ~2 h& ?
3 B' l2 T& K8 O6 n* o- foreach (var sourceFilePath in files)
& f" I2 S) S% ` - {; Y6 r$ j ]& ~! Z. Z3 @( f8 L
- string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);6 I4 m: p. ]. Q; L
- string destinationFilePath = Path.Combine(targetDirectory, relativePath);
. b' o3 C4 l& D7 \! R! | x - destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");
5 h. @9 N- m6 v6 S - ) k) [: H: Q9 i# {' ^; M
- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));
2 ?" U( a$ N1 i$ U0 a - * P0 c- Q e" m/ r* H
- ; g% Z5 D% g9 o! l; x
- if (!File.Exists(destinationFilePath))) v5 O* q9 @& {, X) `
- {' H* {. @/ z6 N. a6 W% Z. p& P/ s M
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");0 z6 A# a! X/ U
# \* y: @/ d/ O7 s7 e* x& n; k- using var fileStream = File.OpenRead(sourceFilePath);
% W! m# r5 m2 ~9 u$ K - var segmentIndex = 1;
: |* ]1 n! N* p1 S9 g - using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter
" t! T Q5 s/ b- Q0 { - var startTime = DateTime.Now; // 记录开始时间/ F% j1 a& N9 y# F, X* E/ X
! S/ L& q: H6 r" n- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))6 t+ B& [' b4 i2 e3 H8 J
- {
) H1 U- R6 @: ~1 s) f* c - Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
3 `, {( H% G" {3 @5 h, b - Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
7 W X0 O* d" a - Console.WriteLine(segment.Text);
% J( ]9 W1 Q' T, x - Console.WriteLine();. s+ e* x8 p$ a+ J: a4 x$ ^8 o& A8 t
) P: H. k$ n8 {9 ]) P2 P6 S1 b8 D- // 将srt内容写入文件9 o* y4 {3 W& \
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");/ l& i$ Y( F! \! B1 x
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");' n7 o/ \3 l; E) a/ _/ e
- await writer.WriteLineAsync(segment.Text);
$ g- E5 U, d. x3 p - await writer.WriteLineAsync();6 B6 I3 ^) J! |- p
- ) c# ~- l2 n# ~' _7 J" G
- writer.Flush(); // 立即保存srt文件
6 y2 T9 T) z: Q+ G; b
! Z, y$ y1 T) ~- v6 z, Y1 y5 J- segmentIndex++;) I! M! m/ L% J, K
- }5 q$ m& X' Q1 C8 I- ?% k
9 e5 r; l, |) R$ Y' d e7 v- var endTime = DateTime.Now; // 记录结束时间
% x4 N8 n% J1 o - var elapsedMinutes = (endTime - startTime).TotalMinutes;
9 l$ Y$ V2 w4 E: U2 {' |' g - Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");+ o- Z; X f- C) x! z
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");
; w8 G; p; |. n, N - }
" B8 ^3 B$ p- }- F% y - else {
' v! j% n2 I) U/ B# E# L# T/ p# g - Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");
3 x4 p/ n. w) N+ e - }1 F% V4 I% q' } L
- }
$ `/ A1 Q6 l$ E+ Q2 x" ` - }
! |/ T5 P G" g$ N Z - public class Options4 }/ w: J% Y9 X& E8 ]
- {& }! _- V- W* L2 k5 J" n8 ^. g; c
- [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
6 F/ B# d2 w# ^ x; h' m- b - public string Command { get; set; }4 ]: l# ^2 l6 ]$ I
+ \4 D/ X' m" S6 \# k+ a/ `+ T- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]
8 @; B. K {( Q' q' H - public string FileName { get; set; }
, `# f; f) ]; Y3 O
3 u* E) w3 E' _- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]
7 I4 A& U4 k% T - public string Language { get; set; }6 K/ F- e* Y+ R! k
; q& q6 V% M, X7 z- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]
4 z+ o8 v: ?- X' D - public string ModelName { get; set; }: O( E! {, x6 }
5 x# e2 f, Z: |( f# V) }- u5 |- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]
5 ^" e$ T, C& m2 Z% u - public GgmlType ModelType { get; set; }" f3 |2 B/ }2 o8 j7 a8 P7 }
- }. v, l: |( U- g
复制代码
! W1 G- u+ X( Z# I4 \$ o. k
* y/ k9 Y" o8 C$ m6 T }, h$ z ]# [( S) ~
7 S. T' A7 O( a+ V |