本帖最后由 shane007 于 2023-9-4 12:57 编辑
4 h% o* a6 F7 ] ~* l% v1 ]3 \( s9 S* ?: U
本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,
. H& k1 T5 y% \/ ^, F 使用时需要輸入语言,源文件路径和目标文件路径
8 n) V" w% e3 l3 C; ]最后是输出srt文件
5 {' I9 A ?/ f+ r4 H+ `% j7 L; x
) ~& e9 d' Q! K: z/ f4 I代码如下
" p% V1 i c1 d( R2 x以下这句用多线程可以增速,否则很慢1 t; `3 v9 F4 |" Y9 V
以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。% J; o8 B0 e: I
( }" e7 K* x. a2 t9 @
2 w2 ?4 w# {9 W2 [) c
- var builder = factory.CreateBuilder()
2 g5 W7 p, k+ B. j" E# G - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码 ) t3 ]3 h6 \8 }1 m h
3 J; x+ Q! o, n4 z/ Y! k
- // Licensed under the MIT license: https://opensource.org/licenses/MIT7 x! s, h% B6 @3 a
3 j0 Y K% i, i4 w0 O- using System;
. X) j" h; c0 \$ P$ P6 {7 Q! Y! y# E - using System.Diagnostics;
) b7 ]9 P7 N' B; L- H7 g+ D - using System.IO;
( j8 v: S. @! m9 G6 {0 w - using System.Threading;5 Z4 g$ B- z/ _8 [9 w( v
- using System.Threading.Tasks; u) x2 X; o& P B0 E3 M
- using CommandLine;0 I% v" S! Z( I8 I. ]
- using Whisper.net;
9 \: g. t9 D3 Z+ h - using Whisper.net.Ggml;
! Q7 D; F+ V" g9 C# l# E - using Whisper.net.Wave;
$ r+ j) _2 n# a5 J - , ^0 _/ ^ j4 ^7 U; C& i& j. ~
- await Parser.Default.ParseArguments<Options>(args)* \+ `' ~7 D d
- .WithParsedAsync(Demo);, w0 N2 _+ [5 o. e" P8 ^8 o, G
- ( F! R% {5 C: V2 T8 n
- async Task Demo(Options opt): f6 o: J7 A, |: c% Z% x
- 1 }# y/ I& n W3 d% Q+ s
- {( y: V0 a' f0 q9 y) E0 D( _1 V
- if (!File.Exists(opt.ModelName))
: x2 b1 I! y1 Y" `+ G7 |" A - {3 m7 g8 K) h2 |5 W& r
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");
& L6 Z% S) Y/ L# I* h - using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);
/ O: E+ a! O5 G$ X. h2 ?( a4 A, E) o - using var fileWriter = File.OpenWrite(opt.ModelName);' d6 U' b& h2 h: ^. I/ \2 w
- await modelStream.CopyToAsync(fileWriter);
3 C2 |3 Q, F% X1 L5 i - }7 V5 }* t) D/ b" l
6 x, t% d% \. o; W$ b8 q7 K- f$ b- switch (opt.Command)
# l' \. U0 C' W0 `& E - {2 j) @7 A$ o; O& _. Z1 \. ~3 B. s+ S
- case "lang-detect":. \0 D a& k/ o# [" E
- LanguageIdentification(opt);" l! e8 o7 A5 O
- break;
7 C0 \1 s k9 A: V' @9 }# U - case "transcribe":5 w- w$ Q4 r. a
- case "translate":
# ~. e! D. c+ d7 ? - await FullDetection(opt);* `" U$ ?# z7 M# Z
- break;
% ?! Q) ~: N; U1 H% s - default:
* |) T) Q( ^( f6 G1 L) B% T0 _ - Console.WriteLine("Unknown command");# L6 \# N# ~8 E7 Q& g0 p
- break;
: F/ |7 {, J: k5 g7 { - }# V& y* g+ _3 o% c- O" h- `3 c9 h
- }
5 b! F' t* O( v: [! R
. ?. {/ `' k; G& R! f( W- void LanguageIdentification(Options opt) o( Y& K5 e( P* x [' R g
- {" S$ c" g( ^! l
- var bufferedModel = File.ReadAllBytes(opt.ModelName);
0 @6 u! _7 ]- C' [
7 i0 c$ O! L! l9 A2 N- // Same factory can be used by multiple task to create processors.7 g8 F% c3 R- h
- using var factory = WhisperFactory.FromBuffer(bufferedModel);
6 u8 D1 w7 p" l" ~5 c
: V- [3 q% f2 V6 j4 W- /* var builder = factory.CreateBuilder(). ? a: Z$ x8 L+ {
- .WithLanguage(opt.Language);*/
% c* a0 Z5 T h5 G - var builder = factory.CreateBuilder()) H- w' U& K: T, A8 L9 v
- .WithLanguage("english");4 Z, ]: m# [" R5 s' Y
- using var processor = builder.Build();
0 t" {$ {* N0 c( |* \. W
+ m" O! s& e5 D8 f- using var fileStream = File.OpenRead(opt.FileName);* L# E3 ^. L, }- ?& j- F' h/ V
- & @, m% T o5 X+ p+ G( N, t
- var wave = new WaveParser(fileStream);; v. z/ Z/ G# c& z7 ~/ w: `
- 1 P4 X w9 |9 \* ^7 |. a
- var samples = wave.GetAvgSamples();- P! R4 z1 m* g6 M1 ?' u
3 M# Q+ }/ A9 y! H5 t- L9 Z0 F- var language = processor.DetectLanguage(samples, speedUp: true);! l4 c: n7 P' `/ n* b" m. b
- Console.WriteLine("Language is " + language);
/ [0 C4 R" A# i" v6 B - }
, z+ n5 C+ J: [* `5 a |! d w - $ K7 t4 d9 a, Z
- async Task FullDetection(Options opt): P$ E0 G; y8 N6 v z
- {
7 ]5 a" d F: m! h - // Same factory can be used by multiple task to create processors.
) t8 s1 E& J' x1 v6 n s# { - using var factory = WhisperFactory.FromPath(opt.ModelName);
& {0 C. b5 j ?' {# f- U
. B' j2 Q/ p) G. ]" a- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);
9 I3 p# {" k5 ]. ^5 L - Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");7 b% h( r; Q" X2 X$ I" l
- string languageOption = Console.ReadLine();& E" h5 c0 D+ p W8 ]/ O4 j
- var builder = factory.CreateBuilder()! }: B" _: n& H' G& f3 [6 O* d% g+ l% G
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
( c; x* t' F# P9 r& m; y& [+ ]
9 @6 }' t% R) R3 L7 F- if (opt.Command == "translate")
& [5 I% F2 u3 P% t - {6 o: {! V) d2 r) x
- builder.WithTranslate();
3 ^; q7 H: _# l) u - }2 x& g$ X H/ M& L, t8 @1 z! ?
- 1 H+ S- K5 {0 D/ z9 H. B3 U. a; r
- WhisperProcessor processor = builder.Build();+ ?5 Z9 {* {3 n6 R
-
. T# d( V. n2 b - Console.WriteLine("请输入wave源文件目录:");' p/ b+ b4 n) S1 M) f( L& r$ F
- string sourceDirectory = Console.ReadLine();
7 C3 @4 z! x( h$ t - ) z% _) a& q/ \0 T5 h! s% a8 {
- Console.WriteLine("请输入目标文件目录:");
$ y7 P; F- d+ K - string targetDirectory = Console.ReadLine();: Z) n, w M' w& c! @. C& G& Z
- u3 K, B, i* T4 n! s- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))) f+ C1 V9 }' o- R
- {
X4 I2 _) Q( F9 w$ m8 u7 [2 _( q - Console.WriteLine("目录不存在,请检查输入的目录路径。");
- ]! \! R9 [3 L$ N( O3 q7 h. [ - return;
4 v# }0 v2 i7 B* u9 r8 r* J - } b% z& ~8 p6 i# K9 u+ w5 l
# o- b. X) O% O1 g: a. O; G- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);
4 r/ Z0 J" i0 A# J5 ^; d - 4 [6 U% z8 T1 |) j# c/ r4 W/ E4 J
- Console.WriteLine("处理完成!");) r Q+ o# R- I, E
- / d1 p) G9 R4 z; j2 w) F& E$ Q/ }
- }
' L! B2 m5 K6 G- l' f ] - static async Task ProcessFilesAsync(WhisperProcessor processor,
3 \! S0 k0 I# C9 R6 ~" E, e3 z - string sourceDirectory, string targetDirectory). ^! i. V) j5 |% g
- {
( W* P2 d1 L8 i) }# T# q% p - var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);
& o& N" {1 ^1 S6 Y
0 h6 ] p$ K0 j1 F" E- foreach (var sourceFilePath in files)
& V* U0 }8 H2 g - {/ z' k8 }' Q2 {8 m1 J0 i7 g, O
- string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);
" T1 D' z5 Q# o - string destinationFilePath = Path.Combine(targetDirectory, relativePath);
& N& R a; ?% j* Q2 ~( q$ Q - destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");' g2 ]/ {5 S! ]8 f& E
* H) o: o* Q* y3 [; l' Y) u- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));
$ j$ o) |% A9 n
: Q: j( d0 E' Q; }6 W
+ y3 Z y! E& s. N- if (!File.Exists(destinationFilePath))8 P6 s9 v# p3 q: u* d
- {
' c( c% x3 D: ~1 C) j - Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");
/ E4 n0 `1 D1 ?) r - # \) _& v5 j4 n: X% [5 B3 ?7 z
- using var fileStream = File.OpenRead(sourceFilePath);
: z/ E2 A1 x0 L7 u0 y# K - var segmentIndex = 1;0 [5 G6 g) O0 K2 D8 j; ?
- using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter4 e' `6 F; b0 E. b
- var startTime = DateTime.Now; // 记录开始时间
4 v6 E! @7 M- k- u
, W3 {, T$ |4 U$ H9 r! g s8 ]- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))
+ \; {& {' c4 c& K' \. d/ u - {7 A" h% M0 W- i' ?; z; Q# W+ [
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
8 _# h8 s5 Q; d7 x3 X7 R1 V - Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
- c, U8 s. H" a+ B - Console.WriteLine(segment.Text);! F m/ A7 a v4 ~$ D/ R
- Console.WriteLine();
$ h ^. R6 @3 [' `' H
% m1 w0 U% t" X8 Y- ?- // 将srt内容写入文件
% s8 n5 i+ i3 M4 F, |. A - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}"); W% z) n; V, z/ I# X U! I+ _3 @
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");9 M* q2 F/ F/ X% g( N+ Z8 L
- await writer.WriteLineAsync(segment.Text);6 D' k* g! ^" _, P
- await writer.WriteLineAsync();
$ K& M8 {9 F* c4 s$ j - 3 _" f# @5 V7 }, N
- writer.Flush(); // 立即保存srt文件8 C3 j* {3 n) t Z
& ~, C9 I! b T3 J7 x& g- segmentIndex++;
. ~, A- T3 x9 }! T- k - }
* n6 X1 v$ |; I. I
2 L# I2 W" A3 x/ A% Y& ?4 D- var endTime = DateTime.Now; // 记录结束时间; { z2 W0 e6 |( g
- var elapsedMinutes = (endTime - startTime).TotalMinutes;
. k, v. k: E: _! n# Q% C - Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");
3 p# a5 d; ~* w0 a - Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");
# u, Z5 N# B$ ^4 r5 a) |- L - }/ t |% J) N8 k4 e3 u; y
- else {
) A3 V; l3 [- S( ~( q X - Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");
8 C9 P# L# ] e3 u6 n - }1 ~: q* \' N9 n; R- k5 G
- }
1 a T6 Z/ r7 ~5 N @ - }
- k& M* l( e, z' n# V' D2 u& b- h - public class Options) |7 u V8 d4 U) q
- {4 Z# v' [$ E( K1 ~( F
- [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
( y$ [& _6 }9 l2 u) L, I9 n) C - public string Command { get; set; }
6 P# i% w5 a8 E/ ~. M* O - 9 B1 j: `( v$ F6 ?* V6 V9 I
- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]
" L* f$ O9 s6 D, d" i - public string FileName { get; set; }' p& Z+ v" F& k
- ) x7 C! @/ y. e5 C, j* r M& @0 o
- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]3 q% S% ^: l, n4 @
- public string Language { get; set; }+ ?' b; p( `) W$ [1 d
6 n: v/ n- \" V- L9 c4 ~6 f4 \- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]& Y1 ]1 h4 d- h6 P, h4 t1 W6 D
- public string ModelName { get; set; }9 ~9 {% o5 Z2 A% G1 o1 \& ?$ m
- " ~3 A6 W" M0 f
- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]9 }, s. }. p, z3 _1 }. Q
- public GgmlType ModelType { get; set; }) b! {" Q* P1 F5 c5 s& x- K7 h
- }& p0 i! \; Y, Y4 Z5 ?) Z8 h
复制代码 % g& h% G% A& U) N) m* k
0 p' M% J8 Y+ l% S1 J: ]" u
9 Z$ H# R( w4 _4 }/ H( [
) x# [- U# m- s0 t1 t- x |