本帖最后由 shane007 于 2023-9-4 12:57 编辑 # h, R" ^* `. j& t& `/ u3 w% d$ F
1 I- D/ Q4 a& r" b: r+ G S6 s" W
本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,
6 s z' s) u+ v; t 使用时需要輸入语言,源文件路径和目标文件路径! k. r& M$ d# G! K
最后是输出srt文件
) u0 |2 e& |+ g/ A5 R8 Q" r
9 t4 v1 ? q7 o0 W代码如下+ o$ q7 T$ ]9 {3 @( w7 G7 l7 T
以下这句用多线程可以增速,否则很慢3 v, w% z8 j& I& O% a4 R
以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。3 D% A$ B1 q+ S4 b3 C: s
; r; Q! ` H' r" c6 S3 K) T5 _8 x6 M! U5 [
- var builder = factory.CreateBuilder()
$ G- F9 ^: |4 S& F% h" e; H3 q! L - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码
/ y; r" w. l) g$ {- I2 ?3 J! z
/ Z( P: a" x; v- // Licensed under the MIT license: https://opensource.org/licenses/MIT) q @# v8 `& o2 N' |* f$ X
9 M% k. o) C/ ]# l4 K- using System;+ E- A* ]4 J4 u, i& t- J7 x' w
- using System.Diagnostics;
8 C9 d& t, y0 r4 z) P - using System.IO;( w' N* H U X
- using System.Threading;: h3 |: J8 x, o$ I( r
- using System.Threading.Tasks;
# }! N9 H6 |8 M1 s - using CommandLine;# V- o; c' r$ j M8 n
- using Whisper.net;7 N' t: D" j8 q) _- a. ]% R
- using Whisper.net.Ggml;
& y' N- G& x; K - using Whisper.net.Wave;
' J7 ]& B/ V' O' i' U6 D% z - ) q3 m" a% `# F- [- a4 w
- await Parser.Default.ParseArguments<Options>(args)
) h. }4 t2 w% }9 q4 ? - .WithParsedAsync(Demo);
$ Q; X( T) y" h+ B% N! f0 t - ' Z( n' l- G: W
- async Task Demo(Options opt)
. b0 c( u" R" H" }; h1 P! F - 9 E0 |+ L) a' T# P
- {
1 X& |( w( j, g" x4 {' p" b4 F! J - if (!File.Exists(opt.ModelName)); f. n0 \0 [" P: W) L% c
- {3 K- a5 \6 h1 E5 x) V2 [1 T
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}"); n( N6 C4 c( q% U4 n$ D
- using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);; z; Z" g0 y: w, Z& x! D6 s+ }& L
- using var fileWriter = File.OpenWrite(opt.ModelName);1 y/ n- T) I% |' @& K8 Q: S. Y
- await modelStream.CopyToAsync(fileWriter);
0 V, u' r0 b9 W4 v0 R, {) M - }7 {& l' B" c$ {7 h3 H2 z
/ U% {$ A4 o6 l- switch (opt.Command) h9 J& d8 F3 G) a3 R
- {% H' Y/ a' ~5 e
- case "lang-detect":
1 D" l; X* X8 n( Q6 @! M3 T3 d1 U - LanguageIdentification(opt);
2 g6 U; f/ n( ~$ h6 @, n - break;
7 D8 X3 w3 B: w+ D8 T. ^5 d% F - case "transcribe":
$ I& b) g- O' s# p# n - case "translate":! y. B% h S* ^ Y9 o4 \# s
- await FullDetection(opt);
3 |8 R! u* m* ~ - break;# C+ o ]% t0 K$ T, u$ g$ l! a' m" c/ ^
- default:5 D: ]! |5 z1 r3 z& j
- Console.WriteLine("Unknown command");9 n% O" b5 N# s, d
- break;8 t. T9 L0 }3 g% Z! i4 Z9 C
- }+ i; i- q* o; I4 g4 g
- }
7 b: H; Q' |0 d
# M+ a; }/ o$ N5 u' o4 s- void LanguageIdentification(Options opt)& v# ]" t! w4 T% b f
- {
$ I) B" J+ b5 v! c8 P - var bufferedModel = File.ReadAllBytes(opt.ModelName);
: ~- b; |! s7 N. u. ^8 `! ~; F$ i
% ]$ a) O1 Y' H6 f- // Same factory can be used by multiple task to create processors.2 W0 G9 ^1 q. X; {! w& q
- using var factory = WhisperFactory.FromBuffer(bufferedModel);
. \- w* z: r0 s) z T* R* D! V - # P0 e$ n9 S5 j8 Z' @ B6 H
- /* var builder = factory.CreateBuilder()9 E; p5 a) m1 J6 J) P# d
- .WithLanguage(opt.Language);*/
/ @8 h6 B1 E7 Q7 o6 r! ^ - var builder = factory.CreateBuilder()
8 J7 E0 ^ J4 N7 y, ^6 w - .WithLanguage("english");
, r' O9 R1 F) j5 i0 l - using var processor = builder.Build();# e& [ ^: |4 G6 k+ h6 Z
8 w L, u" Z% S3 _4 U- using var fileStream = File.OpenRead(opt.FileName);
4 Z# o( d* T9 r$ T* b& e - - E; b6 J# i# ^ y* _ W
- var wave = new WaveParser(fileStream);! P% U6 |3 p. t2 Q4 n; n- N
+ b. U7 g9 Q8 v. N1 S: j- var samples = wave.GetAvgSamples();2 D5 r& [( e# x. x! U
- : J1 e( ?: q9 M' Z* S `5 N+ y( [
- var language = processor.DetectLanguage(samples, speedUp: true);
# z ^# s* V, ]! ?6 ?4 L - Console.WriteLine("Language is " + language);) n z" b' R0 x/ h( Q1 _
- }; @# j( j4 o7 E* s, K
- 0 W% d" w4 }6 J2 b+ b' w& ?# l
- async Task FullDetection(Options opt)+ U& I. l, h- I2 P
- { Y! m" y0 z9 e- S
- // Same factory can be used by multiple task to create processors.4 k3 T1 A2 y1 ]5 g9 c( x0 E' R
- using var factory = WhisperFactory.FromPath(opt.ModelName);9 \7 V! ^/ M$ r( P* o5 j% f7 V
- " S' {; _. r" U* l# c+ F2 v
- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);8 @1 s3 L; C; x: S; B
- Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");
9 f8 v# A5 v" K h - string languageOption = Console.ReadLine();
, m+ D/ L! Y% {6 {7 x7 C - var builder = factory.CreateBuilder()
% e/ s4 |+ ?) i - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);& [+ c" F( U$ ]3 K( x
8 h! s6 K# b/ X8 q0 C" T- if (opt.Command == "translate")
8 [! r) Q( B) P, b- q( a; ~ - {% c5 o1 U; U- }% n/ S0 z( I& x
- builder.WithTranslate();
' T& G: }8 E$ h1 G0 `+ R9 j - }
, |* u6 o9 Z. O& ~9 b
, O4 c8 n2 @0 v! i9 Q( @- WhisperProcessor processor = builder.Build();
! X0 X! U) L- f4 ` - ) u! ?' w& t7 s$ c: D) ~; m
- Console.WriteLine("请输入wave源文件目录:");
9 `! g1 [( ^- c& g ]5 e J - string sourceDirectory = Console.ReadLine();; T. k8 s: s" y% \/ M$ M, ^+ D
# A0 P8 C/ ?5 ]# V! B- Console.WriteLine("请输入目标文件目录:");
" _5 C: e- T0 d- u - string targetDirectory = Console.ReadLine();: s' K9 V8 ?+ ^. F8 B3 u
# _2 [6 u1 ^: \$ U4 x- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))
- j8 O- W3 o( _+ p, A - {. t/ |7 L) v% g7 J. @* G
- Console.WriteLine("目录不存在,请检查输入的目录路径。");
8 h- L2 u3 m' O3 ^+ v - return;
8 g1 O9 g; v- d" L4 _; A" { - }1 h5 K$ ?; n' b& ^. B
! t' g: n+ \( X3 z3 v; M0 H/ O- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);
% L. G- p, A6 C! E- d: q
: {! ], A4 W' Q- Console.WriteLine("处理完成!");
( K7 Q+ Q5 ~3 C6 [+ p& d4 U
* i2 E. f/ C/ V- R Q. w- }
7 F& Q9 Q/ a5 c) m - static async Task ProcessFilesAsync(WhisperProcessor processor, ; R; H: \3 X1 W) _7 F
- string sourceDirectory, string targetDirectory)
O, W& C9 _: v' ` - {8 m" M$ V! Z, j4 l4 F
- var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);
4 o& g5 j$ x% E! E
" ]% n/ F7 l3 b- foreach (var sourceFilePath in files)
4 B( J' L7 l- s: R1 R - {$ U! N! Z* J: V" d
- string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);% K x7 G3 z5 p
- string destinationFilePath = Path.Combine(targetDirectory, relativePath);
6 _( D# o" q# \8 z& m- I - destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");
, p$ w' B# q" F' e/ G' }' x
3 ~1 a! Y+ @, m* x! Q* g- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));
: G* j. P/ L7 ~' k+ ]! F8 I/ G8 m m - : S# W+ I) c8 V; o& a* m' f' ]3 {
9 ^6 f7 |6 ~& _) P5 T4 J% y9 B: p! E: F- if (!File.Exists(destinationFilePath)). f8 G9 l( [- G2 D1 K Q
- {5 Q, N' t" P* e- m# f( i8 Q, r
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");
1 w& g* Z6 K8 e, u - - p9 X. {* c9 K* b' P1 B: Q# I+ ^
- using var fileStream = File.OpenRead(sourceFilePath);* C' f( n/ W; U9 A V: s- ~8 `0 G
- var segmentIndex = 1;9 p, L) z. [. W1 c7 w2 H
- using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter5 m+ q |! V' `( P, V
- var startTime = DateTime.Now; // 记录开始时间! t6 k7 G6 a4 J% X+ n: h
- 2 t2 g) ?) }3 h7 d5 x& Y3 V
- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))/ _( \- E$ Q) ]* B7 D2 @6 n& N
- {% s/ X( r! [6 B8 D+ x6 J
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
( L. Z p$ n1 l- @ - Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");5 X$ P9 j+ G- c; H# \: L% ~! ~9 n
- Console.WriteLine(segment.Text);& s$ e8 W0 }! N+ Y* u6 H7 O, i6 @2 l
- Console.WriteLine();1 u3 E0 d' T0 N% Z
- 6 F+ ]8 X& P: X/ G1 m/ T* q
- // 将srt内容写入文件) P% a" N. R$ V) a( q1 T
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");( O! f, O3 O/ w5 ]
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
# m' K1 u! M% u6 R - await writer.WriteLineAsync(segment.Text);
: u4 w! I, G. Y( y - await writer.WriteLineAsync();
. k$ b* v! e+ r* q: ]/ |0 e - 1 [& V' B/ \! t+ p; J6 [
- writer.Flush(); // 立即保存srt文件
# g( j, ?6 S' ]1 E' V
! e) v- d' r: T& w& i9 p- segmentIndex++;: ?: s- M2 Q" y" F6 u3 o7 G' m
- }4 V8 k& X* D5 s7 w
- ; C0 W0 F. X/ A7 ?
- var endTime = DateTime.Now; // 记录结束时间! v t( Q4 W. B [* \, D
- var elapsedMinutes = (endTime - startTime).TotalMinutes;2 J; t! @( ?' _& f& {! C3 p
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");" }! o2 O; H' L; f3 _
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");
& \7 ?* K m0 h+ [2 T% j - }
* E" I0 |& ?3 K - else {0 ~) x8 `9 g: V9 y: W1 O
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");
( E+ H* G! Q1 X1 H" E F - }
" \2 g! ^ Y3 K7 _ - }# j5 X' O5 [2 l; A2 a* k
- }1 Y2 @5 V1 h1 R2 C7 r
- public class Options9 w; N4 u- J9 ^; ^5 q1 b% s
- {
4 G& w2 h7 B! D$ E - [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
$ s+ u& B/ L& A8 Y% y5 ] - public string Command { get; set; }
* G# V3 I1 Y+ ?" q E; w. h* q - ( C+ A0 Q8 c/ ]* b
- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]
4 E1 a8 Z+ O. ~. R. v/ ~* n - public string FileName { get; set; } e& w0 d3 Z' `0 f) o/ U, K: k4 n* U
4 g, y. M( k/ G& U0 n! y- Q- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]
, m9 w6 c' m# C( Q4 U4 w - public string Language { get; set; }
2 W' o8 E( K- G# E' T* H
8 ^7 i, s' A6 z/ @4 f- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]
) h/ l4 k1 l4 P9 H/ Q2 x - public string ModelName { get; set; }
2 Q' r, _2 A% i/ ^5 I& J, L. Y5 m% T
* S* n% H! R) Q9 |- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]
# L: {! P0 d/ S6 Y# S - public GgmlType ModelType { get; set; }
2 r/ O- o0 a6 _3 u {+ { - }
/ C( t3 K" S. ~9 s
复制代码 % S' [; M1 I: Z
# K/ B% q% ]$ R0 h/ o6 r
% f6 ]) Z: E- w. D) ~2 v
9 `4 C4 N5 Q; A# M! f% }8 f" ? |