本帖最后由 shane007 于 2023-9-4 12:57 编辑
( l! X! ?) u: G( l3 i9 Q2 \* u9 i6 e+ x- k; ]; X7 b! i% j: j' y
本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,
) l6 G& ~8 d& y- n0 R/ M 使用时需要輸入语言,源文件路径和目标文件路径
- H# E9 k5 N+ V1 H1 s; ~最后是输出srt文件
2 q2 z. B6 x+ T$ l3 h/ L( y! n+ ^/ }5 X# m
代码如下2 n" x8 a6 h) G) d
以下这句用多线程可以增速,否则很慢: B/ _0 m3 C9 e7 J n7 r" H* }
以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。! _, h/ r2 Y' I* Y5 }5 X1 V
8 U- {4 r; T: h% M, }/ f3 `
0 k8 n Y8 o4 Q. L8 |! U- var builder = factory.CreateBuilder()5 W- B/ U# R% r! a6 ^
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码 ; C1 B% g3 v" u* x& F/ g
: b* E$ J2 @; G M5 }( k- // Licensed under the MIT license: https://opensource.org/licenses/MIT2 N) M) X, v! J0 [: S/ n) L7 D
6 |" z( P' P9 C e: Q- using System;. i/ t w8 J, }8 R
- using System.Diagnostics;/ F9 O/ K0 ]4 B
- using System.IO;
k; m3 ?* h/ z. z7 E4 M9 N/ Y - using System.Threading;, O4 E/ a% G5 {( n# i4 G6 H
- using System.Threading.Tasks;+ r% M" B. [4 T9 z) q0 T* W
- using CommandLine;9 N4 D0 i" _- w- m
- using Whisper.net;5 L. A0 s: }" p
- using Whisper.net.Ggml;: b! k7 M5 z1 ?- S6 @
- using Whisper.net.Wave;+ X- Z7 k1 u8 ? m; @
+ }; u6 d9 G# D! c, l- await Parser.Default.ParseArguments<Options>(args)
0 p& {0 j4 U& n7 y: e( v) L5 N - .WithParsedAsync(Demo);5 D- q) T. x8 c# u. i
9 g, F6 b. F3 a- S- async Task Demo(Options opt)
2 K3 `! `+ O8 _6 b0 d
) F# V. L8 L1 h4 G$ n9 Q- {% O& f; u0 Y: y
- if (!File.Exists(opt.ModelName))5 f* N2 t- K/ [: M) s( A7 |9 s
- {# H- j7 o) d- O7 U" J2 S, Y1 E
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");5 |1 t3 M: ~; l
- using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);
P9 q$ a7 f; b( K! T - using var fileWriter = File.OpenWrite(opt.ModelName);6 H# V. j9 W) K9 o Y; m6 Z
- await modelStream.CopyToAsync(fileWriter);
+ s4 Z* z. v0 z6 x+ i4 L - }
8 ^8 s5 z& k1 Q% h* I
4 Z. H2 @2 T# B5 F" Z- switch (opt.Command)
; s! ?$ l- J! v' P7 l W7 A - {
* @( A) B: O' H - case "lang-detect":; m( ~" l9 I3 {* S4 @& l
- LanguageIdentification(opt);; T/ z# B0 C* B9 a2 r j7 ^& O
- break;! X: b9 g' ?3 V: J( j4 { S Y
- case "transcribe":
( m- @7 [9 I$ A j& d - case "translate":
( X( ^: [# z; Y7 u' X) g1 r - await FullDetection(opt);. y* r) _0 x& k8 a" {( s
- break;4 ]: Q6 [2 ^! s0 a9 U! K6 ]) b& c
- default:; q3 q; n. L$ y7 D# u( I8 L4 \
- Console.WriteLine("Unknown command");
+ j9 a+ n; }) \# L - break;
! B% m1 h% Y, q4 W - }: t" |- v% w4 F" k* F6 O
- }6 X0 D) o* M& X& b
- ; O8 {; l% M( f7 t
- void LanguageIdentification(Options opt)! \# W* m G' R( {& h
- {; m7 _6 o; m( e
- var bufferedModel = File.ReadAllBytes(opt.ModelName);8 V/ }* p: A6 _' \
0 X+ e. X9 I8 M- // Same factory can be used by multiple task to create processors.2 Y, y2 ]+ I0 j' L4 H! p
- using var factory = WhisperFactory.FromBuffer(bufferedModel);3 B8 d; m) ?+ r( A
. E# W- c5 k& W# D& n3 E% ]- /* var builder = factory.CreateBuilder()
' h! _) j1 d+ N& u' _ - .WithLanguage(opt.Language);*/ m& m' `6 j. ]9 K' q! h5 i7 e
- var builder = factory.CreateBuilder()
3 U1 h, g5 I' g$ h0 G - .WithLanguage("english");
2 p2 j" @# k. A6 |% p - using var processor = builder.Build();' h$ c8 ~9 H, }# |: X: ]
- q/ a$ ?) a0 \- using var fileStream = File.OpenRead(opt.FileName);
3 j6 L2 y6 j6 v9 Z8 `' ^" L
( I) t" G+ ^/ g" N- var wave = new WaveParser(fileStream);
5 p; {: n& C. i" [8 e6 [4 x; o - $ R W8 ^0 Q5 j* t
- var samples = wave.GetAvgSamples();
# R; ]3 s% Y: f0 O, M% i6 I. b- h
' a. N" O, u9 \5 O- var language = processor.DetectLanguage(samples, speedUp: true);6 x O' @' Y' q: v0 Z( O. @
- Console.WriteLine("Language is " + language); u4 l% r; r0 n& k" {. r
- }
- o+ C4 M G* t& c6 x3 | f2 q& Q2 k
+ I1 A8 v4 o4 E2 J/ n" Y- async Task FullDetection(Options opt). z: s; {6 f1 e8 g) ^2 ?- r* h+ j
- {/ d$ i8 t/ F% V0 H# c
- // Same factory can be used by multiple task to create processors.
$ k9 s; c L$ X) U: K; Z1 X - using var factory = WhisperFactory.FromPath(opt.ModelName);7 @7 C3 K4 |9 W" e) t
- ( s. H: P( r$ L! _" `* Q& Y! Y& n
- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);# o3 w* R% D& S
- Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");
: X6 e) N% c# F. w - string languageOption = Console.ReadLine();
8 ^; m: K# C. d4 O - var builder = factory.CreateBuilder(), c% W& j# W$ e
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);0 [3 \! Y3 \0 y
- ! m* K( e6 M3 d6 P: x
- if (opt.Command == "translate")$ Q! b+ ^, {! l' Q& ~" J2 M- F
- {" e! A$ W5 r2 H, U3 ]$ I
- builder.WithTranslate();
+ }. a1 E7 W$ F1 | - }
* i% ^) L4 z" _8 { - 3 w4 v- k8 s7 f5 W
- WhisperProcessor processor = builder.Build();5 w' ^ k' z9 o& u+ J0 r$ Z: p+ e/ c4 `6 h
- & u1 J" m7 J! Z% q
- Console.WriteLine("请输入wave源文件目录:");
( g6 M* Y# W9 n2 G" ` - string sourceDirectory = Console.ReadLine();2 p H6 U! C, A; n, V
7 _) a. V/ }* E0 A, H- Console.WriteLine("请输入目标文件目录:");4 C0 g9 s, r7 F) q2 c/ I
- string targetDirectory = Console.ReadLine();
* _# G; f/ q- Q! c' x1 r
6 n3 I( [- f! b6 v- ?- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))
; ?( `7 ?& a- e. C - {8 l, g; J! Q4 }# N: z1 K
- Console.WriteLine("目录不存在,请检查输入的目录路径。");
3 j3 f) q6 X6 r3 K$ b - return;& I* T% A. N, b, |7 O
- }5 Q2 R U/ S' ^/ O# x3 `) E
- " p% q6 z C2 U! u5 _, j8 }
- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);
6 [* B. y* q9 S* e; n
' H- b$ T* f0 ?9 B- Console.WriteLine("处理完成!");& @" W/ w C! w
- ) r9 g9 c8 B! \) h! W- d
- }& I3 G; J! q$ g' w, E
- static async Task ProcessFilesAsync(WhisperProcessor processor,
% I4 ~# e6 |7 \/ h$ s/ ` - string sourceDirectory, string targetDirectory)
- s# E; m ~" J+ X - { n( o. ^8 G" {4 t* B1 z
- var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);
5 z- c5 \6 I9 e, L9 j4 M. o1 R& c - 2 g2 S4 Z# i8 a+ J
- foreach (var sourceFilePath in files)& v9 B* L7 {& C* @6 p% I
- {8 F1 ^/ z0 O; Z7 E4 Q
- string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);: z8 t8 q7 z, \% C3 T
- string destinationFilePath = Path.Combine(targetDirectory, relativePath);) G/ ?/ ]8 D/ D. W
- destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");
, ~! o8 F5 r' x: j9 E- J# r - w$ {& O/ k1 W: s+ p8 J/ L
- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));6 ^! w& s$ W& i( p7 @) x0 Q
C. U. c8 e* h! i& j2 e$ {- 1 G, |3 [6 f; f- h& u& n
- if (!File.Exists(destinationFilePath))0 n7 s" X0 ~+ s; a0 Q; w8 X4 E/ W3 F
- {' z, S# L! e7 U& j8 M( t, \6 S5 v
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");, Q0 T, G" O& |9 q
- * _" y9 y& B6 u2 J5 o* d
- using var fileStream = File.OpenRead(sourceFilePath);! l6 s1 W* E: d! l) z7 ^
- var segmentIndex = 1;
1 }5 \, c9 r+ y6 f5 [0 w& F - using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter
! u, w4 @* F' J& c% x$ p - var startTime = DateTime.Now; // 记录开始时间: A, {: R7 B2 I% O# @1 l7 o" q" f
- 8 s; }. p: o7 R
- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))
) L1 t7 V' ~8 ^$ g - {8 i9 [* C* N) M/ |: v4 o
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");% O) v. Y3 _9 J7 M6 U0 V
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
2 b0 V4 {' J7 |& C3 Y/ s' q* `7 [ - Console.WriteLine(segment.Text);
0 q7 ^6 L; I" E" y& o$ m; _ C - Console.WriteLine();1 X3 a7 K5 k/ q
- , v0 a) x2 V0 f3 T" @; A
- // 将srt内容写入文件
: O1 J" S$ W' z2 f5 Q& Q/ ^$ }- c - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
* h. L: m% W5 H$ y0 d. J - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
3 w# |( V4 u- |$ k - await writer.WriteLineAsync(segment.Text);8 p: }9 e$ b' r7 U0 ?( U+ b
- await writer.WriteLineAsync();
0 k$ n/ f7 b' G0 E/ i - % Q X4 |& [; a4 s
- writer.Flush(); // 立即保存srt文件! W& A$ \8 K, q; j' h& b$ o
3 R+ \/ J0 k0 G* `( k- segmentIndex++;
. L( m& d# Y; N6 ^6 z" E3 n% y - }
+ n9 _$ X- I7 Y& S7 K
6 h4 U3 L( H( j- var endTime = DateTime.Now; // 记录结束时间) m+ S. a/ M3 I, P4 T3 _
- var elapsedMinutes = (endTime - startTime).TotalMinutes;3 Y- }7 C$ i, u( A
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");( Q: W. U3 f2 E/ v8 u: X6 F
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");+ V& a% U- a4 ]5 C& G
- }
, R8 S0 ]3 u8 x# [' V6 T - else {2 M" s6 N8 {; J- i9 N" G& R
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");* k; `2 X2 D, L# v
- }( {" Y" Y# M, b: e6 B
- }) \# N, {; ^5 h
- }
7 E; q* g2 ~2 `- m" N - public class Options
1 t; D8 o* i4 u* y/ i - {6 b8 A. c! |) l$ ?( Q5 N
- [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
' ]. S8 F$ S8 w - public string Command { get; set; }( w6 j* V1 o g6 r, q8 {" V
- . Y. y( {$ l# w' n& o- J1 m
- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]9 ^, W3 s+ ]$ L3 C+ K* b C
- public string FileName { get; set; }% v4 E: @4 s' m1 Z4 E! ?
, T$ h# c8 I! w" f! W- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]
9 a. d1 J M7 C6 g9 }$ ~# }8 |2 B - public string Language { get; set; }
. }# A5 y A0 r; Y
1 X6 d4 E6 X* c9 |% Z- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]2 E z, B* c2 Y1 Q* l6 s% P: z/ l
- public string ModelName { get; set; }
0 ~' ^- I( O3 s* O - ?( M. d2 V8 {6 P$ V; p/ a' u
- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]
8 ?) p7 b% H% O( I- j9 J - public GgmlType ModelType { get; set; }% n" r0 ] @/ H
- }6 i$ \9 I" ^$ D2 H: i8 e. Q
复制代码 : {# f' r& F! W9 b& z3 Z
/ \1 E/ Y' H5 L- J: R# R3 Q
5 H; T( n5 q- s7 x" y! ^8 h& q
% B9 y3 m, z8 K- K- X- ^% R6 ?) V
|