本帖最后由 shane007 于 2023-9-4 12:57 编辑
+ Q U k w- ]: ?$ Y$ O) n
! l' e/ J5 e4 w: Y+ r 本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,9 ]4 [- k8 J1 L
使用时需要輸入语言,源文件路径和目标文件路径# T8 O2 X0 Q5 Q7 e% w
最后是输出srt文件
' m6 p6 _1 ]+ C1 j
c1 j* A/ b i2 o代码如下 m8 K; ~% h* f3 {* ]2 `/ E
以下这句用多线程可以增速,否则很慢
' `& k+ d& [2 r4 t- Z1 A; y以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。
7 _7 r) a; ~, v# A0 \6 z7 ~0 \/ M4 |8 d! i4 H
7 C f4 F; p9 h, ]/ v2 R5 V6 s/ A4 B' }
- var builder = factory.CreateBuilder()/ ^' Y' m$ g; G8 v
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码 $ y9 `6 `6 K( J$ f2 H
* F8 X4 p$ q' u; k( Z0 G* C: j1 a- // Licensed under the MIT license: https://opensource.org/licenses/MIT5 b; W U2 h6 k& h5 }: b6 a: H1 q
- V% ?! B- l6 B- r% ?6 R- using System;
9 _ Y, m x7 r2 \- M+ C$ m b - using System.Diagnostics;
; F1 d' z: D& l7 e - using System.IO;$ I" n+ `5 a9 K( \
- using System.Threading;8 m$ X, b1 K6 e0 t* G( t9 X5 J3 w
- using System.Threading.Tasks;
6 v- v9 E0 i9 V - using CommandLine;- d5 @0 v% Q' Y8 V$ q
- using Whisper.net;3 S/ v! |6 G a3 c: |7 a
- using Whisper.net.Ggml;1 m1 Y* K# v4 V1 @! O" d
- using Whisper.net.Wave;
2 m2 ?8 n' F6 K, N9 M- b1 x
" }' o% c- {5 T- await Parser.Default.ParseArguments<Options>(args)$ t4 k9 ~# C$ q$ w6 q1 K
- .WithParsedAsync(Demo);
- n/ A2 T1 s' I) }' u - 6 J/ V9 K( G" [/ w0 K0 p: W( F# z
- async Task Demo(Options opt)
0 y" [3 D1 q* P9 z
' v7 o- ]9 |% ?3 U7 z, w- {) u1 J8 w5 b* s. Z! p# w7 o, r
- if (!File.Exists(opt.ModelName))& i/ z4 Y- t3 A" h7 u0 o, m
- {0 d$ B1 [" y; }; N. G' ?' V$ c5 b. k. U
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");) ^0 h6 v: I6 _2 a$ E+ F+ x
- using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType); Z; T ?/ q! D0 R! [
- using var fileWriter = File.OpenWrite(opt.ModelName);& H9 d6 l) J( R
- await modelStream.CopyToAsync(fileWriter);& B/ s# J4 _; r) D0 Y' e, {" e
- }' _. W6 g/ C) x- {& @7 |6 O/ u
- " ?6 K: ?; }& G7 r
- switch (opt.Command)- u' c: `+ P# v3 X
- {( P; E3 i1 A/ R& D1 |2 T$ D5 [
- case "lang-detect":
4 _. S& z: E0 [ - LanguageIdentification(opt);' w- @, O. d5 H- w
- break;
. D6 G& `- w5 l2 {$ W1 x2 F. k( ^ - case "transcribe":
; Y1 {) p" ^! X# e - case "translate":; N/ j+ F0 z; O
- await FullDetection(opt);
& E5 g8 l h9 D& c4 I; z - break;
" C/ r: [( [0 E - default:
1 B( `2 Y% X- x. } - Console.WriteLine("Unknown command");
5 A0 d3 N ^( S6 [6 V; Q* E - break;
1 B, g1 }3 p% T6 x8 p - }
0 S3 v j" p0 H4 t' M - }
, a% X1 U J1 o [* y P
5 I+ r0 R. b+ f# D: f$ O" W- U- void LanguageIdentification(Options opt)
2 |/ v# q1 a9 R) ^1 d+ O( n, X - {
- i; V- X2 B, T! A' ]8 } - var bufferedModel = File.ReadAllBytes(opt.ModelName);
- W M; L8 f) b. H! D( ]' p
/ F+ i8 C+ p/ ?2 L/ R' i! G" o) S- // Same factory can be used by multiple task to create processors.
0 Y, b% v4 {7 g) S: ^4 p0 R- e - using var factory = WhisperFactory.FromBuffer(bufferedModel);
% `3 c, k% n. y$ a' {
% k! M/ U, J5 o" s/ f3 u- /* var builder = factory.CreateBuilder()
0 v* j* }3 L0 t; c& a' M, y - .WithLanguage(opt.Language);*/
$ O( M' V; Y1 Z, N( t. p" @ - var builder = factory.CreateBuilder()( b3 d7 C/ q# c- k$ H6 D4 q
- .WithLanguage("english");
3 l, ?5 h1 Z3 Z# X) | - using var processor = builder.Build();7 y) C/ E! ~- z2 X2 E4 E4 w( b
- 6 W# l' H, \ b+ a- P7 f! f
- using var fileStream = File.OpenRead(opt.FileName);
- x6 p: M5 o s/ P - ' o; M z, N3 K+ s
- var wave = new WaveParser(fileStream);5 d; q# L0 W( ]% f9 }
, |$ }8 o3 G! g- var samples = wave.GetAvgSamples();
6 C7 j' p6 N9 G) \& ]7 `0 I$ \" H" ?( t
% p% ?, r4 v; j0 V- @, N- var language = processor.DetectLanguage(samples, speedUp: true);( b& C- k) c5 ` T. z- ?& f9 c! ?
- Console.WriteLine("Language is " + language);" n9 Q7 g3 A, {) E
- }; f& M& @; ?9 |+ N8 l, S1 N
- J. A$ y0 h6 [) C* }. H& W- async Task FullDetection(Options opt)
/ J$ P% b5 Q+ O$ U( S. j - {4 W9 x6 L( m9 q. d( _" G
- // Same factory can be used by multiple task to create processors.
! Q( ~* z5 @. l; j2 R - using var factory = WhisperFactory.FromPath(opt.ModelName);( ^1 J6 i0 t# W& s
5 Q0 P/ Z: D3 t- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);# _/ b. u# T, g9 @$ B
- Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");
0 G( A+ S# d: u: y3 c- v# i - string languageOption = Console.ReadLine();. G& T9 i B2 n& J3 V% P) l8 }; B
- var builder = factory.CreateBuilder()* L4 z" x4 D+ V' i: b- x1 Y
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
7 m* A- h- |( i1 P# x( O4 ] - " p b v0 {9 Y# z* b
- if (opt.Command == "translate")
8 c1 U, M) G7 t! V! u& J/ ^ - {4 p. x/ v& t* e8 k
- builder.WithTranslate();) E' D& {% W1 {1 B
- }
# K& b. k7 w+ H& g' N
: a0 f$ b6 s1 ]8 Z! L- WhisperProcessor processor = builder.Build();
4 \% d: Y, l( G6 m6 v2 r - ' g; L7 Z9 O4 m; t
- Console.WriteLine("请输入wave源文件目录:");
% Q5 N& G- y0 k - string sourceDirectory = Console.ReadLine();
) b8 a/ D/ b$ {- N% j4 [! P7 h - ; t) A* c- |5 G* K( @1 u; k9 S1 W2 w
- Console.WriteLine("请输入目标文件目录:");5 S7 e6 E u' j& t: c7 t$ D2 Q8 Y
- string targetDirectory = Console.ReadLine();
) S) U: S( o* ?6 L% e4 |% @
( J( r6 {% F0 L, i$ I% G- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))
# x" x. B+ x* }6 P2 S - {
b) ~ x: d* o6 u6 ^ - Console.WriteLine("目录不存在,请检查输入的目录路径。");& A' Y* B# G: F4 [3 b# K
- return;8 ~1 f9 u$ d- M8 p
- }
g1 l. ~" ^- d- C7 e
4 v# d9 Q1 k, R+ Y- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);
1 P4 _* T! q7 u+ } d - 1 Y) G6 j& _# P' a
- Console.WriteLine("处理完成!");, S& q+ @( c t- n, u+ s* ]! A
- 8 E% i8 H# x2 W7 i' z
- }( E% U, Z- w' O6 E
- static async Task ProcessFilesAsync(WhisperProcessor processor, 3 y. g3 A* _& L# L" g+ ^9 p" v
- string sourceDirectory, string targetDirectory)5 G1 F* K H+ W9 A; W' @7 P7 g- p
- {
" o% h8 ^' s, {' F+ p* B - var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);/ M9 Z$ \3 m3 k+ V( U; E2 B
* I7 Y2 k# S) e0 O* K" b, p- y/ y) P- foreach (var sourceFilePath in files)
- f& Q; N0 Y" S; o# X7 ]* B - {9 D) n5 f. R+ R4 v% n
- string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);# r) G/ K3 b8 L" `! N
- string destinationFilePath = Path.Combine(targetDirectory, relativePath);5 `3 [! z' k4 A1 H. x
- destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");7 ]' B/ `/ S/ }& [9 g. |$ Y
- A6 J2 Y1 F7 J; M
- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));
4 E0 U! I; Z/ K0 Z$ \ - ( z: B: q. Q' P! f
- ' t3 u* d( [0 R- I3 \ U
- if (!File.Exists(destinationFilePath))
/ s& \3 c4 T( [" J+ J4 d - {) U- c! T6 s8 }
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");
m% @5 s% ^; k
8 P$ T' J8 w. g: L' T8 F# w" }- using var fileStream = File.OpenRead(sourceFilePath);
6 ^* ?% X9 R5 v# j - var segmentIndex = 1;1 L1 x2 p8 K& G' k2 Q4 D k. x
- using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter
, m$ b" [! d8 J7 _" Z8 X* J - var startTime = DateTime.Now; // 记录开始时间
' j, [2 X7 M* V& Y
2 t& U+ m3 N: h2 J; ^- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))
% R' y9 ~4 @ Z/ ?' p; F; j - {( T9 ^3 @8 O9 ]' d# `. `+ D
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");3 z9 k' `0 v( H, W5 V# O. g
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");. E" W" n, `! e! ]
- Console.WriteLine(segment.Text);- V* n F B/ O9 ^, Z+ o
- Console.WriteLine();1 g8 o; Y1 B0 F
0 d0 z* e& j5 `3 G. q* R# D- // 将srt内容写入文件2 x5 F4 ?* }, K8 s' C% p
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");' o2 u* @4 w! p
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
3 s, n" v0 s. Z% I5 @0 } - await writer.WriteLineAsync(segment.Text);
3 |4 t, ]- H& s1 v, {- C& h t. a1 { - await writer.WriteLineAsync();
- e/ Y8 B, a* E$ ]4 t - * \( l( ?) |* F- Y
- writer.Flush(); // 立即保存srt文件. T, N9 ~' f. N/ r2 I. z$ o* |
, N( [ }! h% v) w/ P- segmentIndex++;" z, n9 \$ L& C z) A
- }
8 m. y: D% P! U8 h; i$ ] - " c0 l' _! G, m4 Y
- var endTime = DateTime.Now; // 记录结束时间
$ d( i- \! W) U! c - var elapsedMinutes = (endTime - startTime).TotalMinutes;, @0 d. Z7 C0 P) R
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");
( `% H& d3 Q4 R7 J - Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");* { ^- `, a- k1 d/ E
- }* E, [" b: X# B9 Q: C
- else {
# w: s) E6 }9 o - Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");2 I7 H0 v c4 W$ b6 b% b
- }1 L. g* j4 s8 a) v1 ~
- }
: V+ p5 L# P/ T- ? - }
) p: b7 C: H/ ?6 u1 J1 X7 C - public class Options
2 x- w, F! W* L1 a+ [ - {
2 C$ o8 Z( j% O6 I9 `/ s, P - [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
4 O4 R9 Z" W' h# @% F2 D. D - public string Command { get; set; }
0 X. v. d+ H( g6 Q1 Y7 y3 t2 v - 4 T* d! a, J' p4 v _ I4 S
- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]
3 V6 C3 J( q3 A( w- Y - public string FileName { get; set; }5 N# A5 q9 c# \" M
- 1 H I# t- l- F& Q( G7 y- {4 i
- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]
- U9 ~- f C$ Z* _ - public string Language { get; set; }
5 i4 d1 a# n& [( e: K
% w% ~3 V. @/ V) E- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]
" `# j+ U$ E0 q' L - public string ModelName { get; set; }
/ j1 H0 ~ B0 @4 U1 P( [
3 E' f9 W1 u3 E3 J( l- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]
h ~6 D% s' V/ m: N6 \ - public GgmlType ModelType { get; set; }. v8 y5 f# P4 ~% [9 m9 P5 u" {
- }& N3 d8 t- y- M6 l
复制代码 0 _* j# ^0 d I Z: U* E% M! z
! s, i, Q2 P, C5 O, h8 H) K# E
$ `$ B! q; L# M$ H1 j) o: y/ C3 z( s9 S. \0 f( Z/ t
|