本帖最后由 shane007 于 2023-9-4 12:57 编辑
- e) {9 l8 |- Q0 C& a$ G
, Y8 R+ r- U" I5 {2 h2 Z% _5 ] 本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,6 P: @' M0 C5 }6 Q% C. Y' z
使用时需要輸入语言,源文件路径和目标文件路径
' @' D% W' F0 |6 ?2 k |最后是输出srt文件
0 y! d! s& E- l! Z( ?% j
# |& O1 F( a) o+ i+ F. j代码如下
6 z) J# v7 Y3 w2 \2 z/ B% ~以下这句用多线程可以增速,否则很慢
4 D& m' n8 L; R- T以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。) o) m* H: }7 L1 x1 E
) l& n( y0 q5 r& C
. u$ z. }8 x T% o6 m% Y8 ]" o- var builder = factory.CreateBuilder()
/ k" S6 v3 P" X; Y: u! @5 o - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码
6 J) x5 f- M, I6 ?+ b
" @2 s9 \7 K, ~4 J$ E% g- // Licensed under the MIT license: https://opensource.org/licenses/MIT
) r* X$ g/ M5 x/ V
3 ^# s0 S8 b: w. G* C- using System;
! G3 L% U4 ?+ w: T# J' U& @; }2 L - using System.Diagnostics;
/ Z+ M- _: [2 E' H* x4 w% g - using System.IO;
- U& k* e+ n# h* Q" X! [ - using System.Threading;( u J! l! [3 A* P
- using System.Threading.Tasks;
0 \+ y$ }" k3 x; `% g- u - using CommandLine;
; m7 ^9 D* U6 ~* |9 L - using Whisper.net;5 `1 Q; e7 z, O! z
- using Whisper.net.Ggml;' e( h' S, M( k5 o
- using Whisper.net.Wave;
7 q5 i! S3 o* f- ?7 p# a' t
) e3 {$ a6 ]: Z- b; `; [4 m- await Parser.Default.ParseArguments<Options>(args)
& w2 O- d6 @, y. X5 t - .WithParsedAsync(Demo);
" n$ [- B/ {7 X3 U1 R/ Y9 p - . }) }- \% U) e) ]
- async Task Demo(Options opt)! D8 @+ @& u% a( U8 y
6 ?1 }4 Y4 w2 \7 p- {
E" e& L! N9 } p/ C - if (!File.Exists(opt.ModelName))
2 U% Z; ^ z/ k# I+ I% D: x - {' v( Q% X; g. h: O4 l- @
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");$ J [( o2 M# Q) ]- p- W
- using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);1 y# f# a g, D: O' e* Z
- using var fileWriter = File.OpenWrite(opt.ModelName);
$ i' p7 q% b( y4 u+ Q - await modelStream.CopyToAsync(fileWriter);
" F2 C/ w( I1 }! \1 o: D7 E - }' R5 y V# g* s. n. @+ b
1 `7 v* c9 N. E0 |8 D- switch (opt.Command)
: _8 X% U7 h5 Z. J9 _& k - {, L: s( W! |2 ` U4 U
- case "lang-detect":
! Z6 R' k. _9 T" i7 ^$ y- D - LanguageIdentification(opt);
/ I+ d \) K5 y - break;
( Q! Z* n# u8 V0 ~2 ~! ~ - case "transcribe":
; j Q! X+ O" F7 N9 |/ ^% f- R - case "translate":9 N0 V8 a4 `- J$ r
- await FullDetection(opt);$ f0 e7 ], m, h
- break;) d- `5 D' \% [
- default:
. U2 O6 o/ k8 G% H* h - Console.WriteLine("Unknown command");
2 O+ u6 J K# R. M - break;
+ n# F1 e6 Q- R% v# r, W4 l! n2 K* z - }
" _/ r) m* d8 c% N+ o' k - }
( T* d0 o( g/ o. v' O
3 E# y4 Z t& }% b9 n* I. P' C- void LanguageIdentification(Options opt)
, K3 d; \6 C$ P2 D - {7 S9 ]" O. D/ b( B
- var bufferedModel = File.ReadAllBytes(opt.ModelName);
* G7 {4 }, g" e - 4 `# A2 w: i v# Y
- // Same factory can be used by multiple task to create processors.$ G0 a6 Q/ c) z
- using var factory = WhisperFactory.FromBuffer(bufferedModel);/ E! E3 ]: l/ A+ e" w
- ; I7 ]" v1 |, I( B7 W: a
- /* var builder = factory.CreateBuilder()
) Q; m. w9 I" s/ o6 R1 L - .WithLanguage(opt.Language);*/8 ~' e( M& n7 q4 f3 Z6 n, i4 J9 z
- var builder = factory.CreateBuilder()" i, N0 G- C% B3 @+ y! j
- .WithLanguage("english");! x P0 K0 A/ S, I+ g
- using var processor = builder.Build();; s, T! n6 m5 X/ x
- # y/ p Q4 y. p% V7 Q. L1 a
- using var fileStream = File.OpenRead(opt.FileName);
, J, O! L2 ?# |6 R0 H( V3 {0 [
$ r% p/ d. q; `/ Q- var wave = new WaveParser(fileStream);3 H5 L9 j4 t* O1 Y. A' x
- ( u6 [' W0 [: U) q b5 b
- var samples = wave.GetAvgSamples();
7 q5 M4 E1 M' }5 K
$ V* G; f# k- f+ h/ m# o: L. K- var language = processor.DetectLanguage(samples, speedUp: true);
$ V4 `9 k1 V/ W - Console.WriteLine("Language is " + language);" \, D: d0 n" J) a) H+ m+ n8 s _- w
- }
% L$ ?$ p: d+ s7 D0 }5 S - 1 k; ]6 X" s* q0 M
- async Task FullDetection(Options opt)
' ?8 d7 a3 G+ a K9 u9 }- j - {$ \/ \4 i" G4 H2 b- I/ w+ N
- // Same factory can be used by multiple task to create processors.
% H% C$ K/ H4 t" J% f1 [# @ - using var factory = WhisperFactory.FromPath(opt.ModelName);% P2 H$ W4 D! t/ n. m- E9 K* g
7 r9 u7 r& e2 F0 e' g! F- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);
3 P" v6 b) V; r! T- F$ a8 E' N - Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");
- X- o5 @6 u) `- S% M5 x/ Y7 v - string languageOption = Console.ReadLine();, q% y* O Y! l- @: o
- var builder = factory.CreateBuilder()0 R% t, I3 B; v3 k
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);$ o1 T; b3 T+ D6 i3 q! X4 V8 V
- # H* |* T% }0 ^, ^
- if (opt.Command == "translate")
3 V+ \# B# }- K5 M+ X - {6 W+ |: Y8 O* `
- builder.WithTranslate();, m6 U+ H* }6 Y0 ~
- }+ o5 s' s2 H+ z0 i7 f
- 9 M/ P+ I7 G/ Z+ k5 p9 K
- WhisperProcessor processor = builder.Build();
! [' U) v) ]* N5 _% y - 3 d3 u Y- l" i4 ]
- Console.WriteLine("请输入wave源文件目录:");
& o" l0 N6 S) f/ U - string sourceDirectory = Console.ReadLine();1 J$ v# b. u; f; i/ e
- . w S+ q& S* W$ A1 ]: Z' ~6 J! R
- Console.WriteLine("请输入目标文件目录:");
* S8 b9 l$ T' f - string targetDirectory = Console.ReadLine(); M% Z8 V5 G, s
- d4 _3 y6 \& y6 ^7 ~% Z8 h# D* l- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))" o6 N$ G! @4 o/ A' x& @
- {' n4 ^2 h8 k' N1 p/ [
- Console.WriteLine("目录不存在,请检查输入的目录路径。");/ w) E$ }! ?# ] u
- return;2 f. H- Z7 T6 v1 Q2 [' z3 }: C
- }
# D& S! h% V3 V9 O' V7 l
3 }' v5 }+ p& N4 L. d- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);
7 h; J% B# H7 H' ? - 2 ]1 T, y" R! V9 {0 E' r
- Console.WriteLine("处理完成!");6 V) J4 _/ y3 q
- 2 U: e" u9 {: n1 @ e. N7 ]5 F
- }
: l& V& x$ W# M( w; S6 X* z - static async Task ProcessFilesAsync(WhisperProcessor processor,
3 D4 ~ m$ {1 c# Q* V; V - string sourceDirectory, string targetDirectory)
* o/ j. D: G3 |& P+ o3 r - {
& m+ T& ]0 b L# I - var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);
0 y1 `( J- ?2 H4 E+ _) [ - 8 H4 I( p' \( f0 I+ ^$ E% E" Y
- foreach (var sourceFilePath in files)$ j4 Z4 F% Q/ e* w! O+ u% S8 j$ G
- {
- E8 R7 d9 q& \' D - string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);9 c3 C% A3 B1 F% [/ f
- string destinationFilePath = Path.Combine(targetDirectory, relativePath);
- ], o. R" a: W5 Z3 I' U* } - destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");
: }6 s# j$ ]4 Q' F/ I
$ `! d# a8 @; i- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));
+ d; f7 Y, `7 Q0 ?4 _/ [
9 ]9 ?' E% ^/ f- m4 Y- I) N4 r, }
- if (!File.Exists(destinationFilePath))' m1 v2 }3 R8 I' {
- {
+ w- f# H- ^" r, M: F. p3 F1 U, g; z! } - Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");
' E: D+ I$ r# M& w9 \. i - 3 w2 V2 i% U6 {, b, w" d
- using var fileStream = File.OpenRead(sourceFilePath);
/ V ^$ p" g5 |! s1 l# ` - var segmentIndex = 1;
, i9 [5 [& R3 [0 D s - using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter3 j& ~+ p8 h: d+ p# H
- var startTime = DateTime.Now; // 记录开始时间
& b& j) l% Y2 Q ], S4 c! p
- F! ?) K- F9 k, |& q1 T- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))5 Q+ o) c5 ?" l# w3 N
- {4 z1 p) q% \4 ?! _. P" X
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");- a# O7 l: C; w( E0 |
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");$ \7 { C5 g5 C6 N
- Console.WriteLine(segment.Text);
6 P1 O/ ~/ o7 d% c - Console.WriteLine();0 p4 x8 Q0 }/ i4 X" q2 K8 N
- 4 U+ ~" c# O- @
- // 将srt内容写入文件! K- K4 V2 ~6 _" P6 _. y1 c% d
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");# T6 D7 m8 {9 R8 R% r& d% J
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");, J. s/ G9 x0 A A
- await writer.WriteLineAsync(segment.Text);
$ x5 l0 F; w# q0 V& R' r( i - await writer.WriteLineAsync();; M1 W! O3 ~( P' c+ B% I9 n
9 m3 P9 F# t$ f; e, z- writer.Flush(); // 立即保存srt文件
2 s( p" P* H L- D2 q2 I; Y: ?
. S6 X. z4 |* ?# T7 \. m( W- segmentIndex++;
$ q" x- x( e ?. ` - }! [" J) v# y, f; f6 E; x0 p* D
- 0 W t+ M9 {( J: R$ k
- var endTime = DateTime.Now; // 记录结束时间
I8 u9 ?6 F. b& o! c" @$ \ - var elapsedMinutes = (endTime - startTime).TotalMinutes;
* E8 P! R3 s# W/ q - Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");# x' C) z2 z0 z4 Q, k
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");& z; A. ^+ T1 G
- }
" I# _! ~6 L/ g5 _: p- @: c) e3 k$ t - else {
( o' g2 R0 v" @6 j - Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");
S9 c- k4 I k8 Q - }
4 F7 x+ \# R# h! @; _7 }" w" [ - }
2 t6 n4 q+ [1 A) ~7 }) g* c3 w' ~1 T - }
& I; ?" W$ J: s! k# t+ h% i - public class Options) i' i% a$ n7 M; ?3 _& I9 y$ Q
- {+ e3 I) x4 f6 M D1 x, |$ A( _
- [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]: I9 h6 }" x7 W- `
- public string Command { get; set; }* v( n+ c8 ^& }) J
- ( k- Q' X3 F# [8 J. d8 Z, ^% k
- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]
W9 S8 t/ n5 L1 l0 |) U: l; K - public string FileName { get; set; }
+ e, q8 A8 d/ A x9 r/ r# } - 1 g# F, L: o: }- m$ H
- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")] J. h( O' e6 }# {* E* `0 x
- public string Language { get; set; }
$ }5 g# { l5 R - 9 c/ p; Q7 B/ S( K9 h
- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]0 \: j; X* \) ^6 q' U- ~
- public string ModelName { get; set; }3 ^( U& f' j# `: N: l2 J
- 0 u% l" A1 E4 W- W) S$ F. x
- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]4 }1 v% Y0 ]: l( a. Q4 c) O
- public GgmlType ModelType { get; set; }. O, b5 ^: G" D8 h
- }
! B ~) Z0 V- B0 `* P
复制代码
. W" i$ }5 O4 r; e9 y& G7 D" M0 b* e) Y; u; E4 a
( \: c2 [: b" G0 ]/ w" a8 V2 C3 N4 r' e6 M5 f
|