本帖最后由 shane007 于 2023-9-4 12:57 编辑
8 `5 G( o8 S6 q; D* n) _( N, K# X1 J5 e0 S6 z9 A% ^
本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件," A7 A' w* Q# u9 Z9 S" j* e
使用时需要輸入语言,源文件路径和目标文件路径
. K( P( F" P* Y最后是输出srt文件9 x( g) I' v5 l, p6 b
6 k: `$ ]& \7 A
代码如下- n3 v$ \& O1 q" W7 n8 Y. Q
以下这句用多线程可以增速,否则很慢, n4 r# t. n( O8 F
以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。
& q* V' p; e7 p2 h( n
1 K) h( e1 I. m9 b0 i/ I! I4 P4 l0 S, Z8 p3 f( d
- var builder = factory.CreateBuilder()
0 Z1 p4 m2 z* B: s2 Z - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码 3 V9 D% j1 U' E
* K- o0 \9 P* E+ C' u: z- // Licensed under the MIT license: https://opensource.org/licenses/MIT
1 c1 x: {+ R+ M2 x% S( {/ t
5 u0 ^3 N, b8 C$ O* G1 Q- using System;
: M( Q: f- w9 K, I2 } - using System.Diagnostics;
! f; ]! g- N7 c+ H - using System.IO;
/ v7 j$ Y- e5 N - using System.Threading;
& L; U- D! ]3 A) A - using System.Threading.Tasks;
7 T, t6 j g0 x6 M - using CommandLine;
8 [$ A3 z- c0 v5 R( B, { - using Whisper.net;5 }; W) a% @& f; q) T+ O
- using Whisper.net.Ggml;( y. O6 X, F% _; x. @+ c
- using Whisper.net.Wave;
d. o/ _( E# ?( k: {. r - * `; ~/ A, O8 w( p$ I
- await Parser.Default.ParseArguments<Options>(args)( ?. e- v9 N6 a7 Z, F- N. W
- .WithParsedAsync(Demo);
% @1 A/ c! W+ d; P' V# n - 1 w3 @6 b7 ^4 D" O3 o. X2 W+ G
- async Task Demo(Options opt)& Z, F2 F/ I4 W- v# ~# }$ |8 u
3 }/ B8 w) [: I; N) w' g1 b- {8 Y! N& z# I/ N ~! o) Z+ r
- if (!File.Exists(opt.ModelName))
' i) ]! c# B# u% H - {
+ p7 W3 e% D* ]* b5 J. G9 N - Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");
) i: ?7 T3 l( w7 J0 O* D - using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);
) \& j% r, R# D) o2 m4 s# j" [' ^ - using var fileWriter = File.OpenWrite(opt.ModelName);
; U1 C5 {5 d; A; q) p2 U# a& z5 { - await modelStream.CopyToAsync(fileWriter);3 g* k3 N% g3 W
- }8 I6 I1 }4 x3 `% v
- ! a( K+ C3 h. ]2 y& A: M& c" N3 u
- switch (opt.Command)6 O! D e" Z" F# V* w' c
- {
' Y' f1 @, S; r, j) [/ t - case "lang-detect":
" K( o1 n3 G: C& c, w0 M& X9 n - LanguageIdentification(opt);& y0 Y$ U/ i- i) u4 N" |
- break;. O# H1 z: J# y9 i8 n9 Y
- case "transcribe":/ B Q" Q/ G/ t2 }# `" U
- case "translate":
: p) o& F0 F! C# C - await FullDetection(opt);
4 j4 u) i5 |, V+ I - break;7 I- F+ T; Q* h3 W, X
- default:/ e5 U! R" F; C m3 \
- Console.WriteLine("Unknown command");
$ e- \' j: u$ E' Z$ s3 J0 i - break;
0 ^- z% L5 K" y8 w4 L/ Q! M - }
3 U! @* c5 j* g8 x - }
( Z; z3 b) d& o( u
& L: {( j1 K. L1 q+ o; l8 a9 V' c- void LanguageIdentification(Options opt)
3 G0 L8 `; v# _. n( L' Z - {
/ K. h7 } m# [7 X1 h2 A) f - var bufferedModel = File.ReadAllBytes(opt.ModelName);4 [$ a: _' E$ w. m
. O' r6 |0 h' }1 i% M" K" C$ T8 H1 F- // Same factory can be used by multiple task to create processors.# G* [: g) [) A) r
- using var factory = WhisperFactory.FromBuffer(bufferedModel);
& Y% O* o0 H# }9 D, N
' |& p# l+ B- y- /* var builder = factory.CreateBuilder(): s7 \9 D W4 v' S! i
- .WithLanguage(opt.Language);*/
5 V( f: N" v( b+ a- d0 M$ D3 ` - var builder = factory.CreateBuilder()
6 X5 R2 f$ g' }5 ` - .WithLanguage("english");
: J# G" I$ {1 p3 p/ r" J9 S - using var processor = builder.Build();( E x+ w6 f- v& n& Y
; A' Q5 L& L, Z7 o- using var fileStream = File.OpenRead(opt.FileName);
+ Q0 ?. X0 r+ V/ c
8 X4 U$ R# `! l' o7 u$ n* }- J- var wave = new WaveParser(fileStream);
; B' I. q; O9 {& L2 Z
, I. `5 }- z) n& F- var samples = wave.GetAvgSamples();+ m+ c; S" N$ \3 J
- e c% r9 H+ J W% W* c+ H
- var language = processor.DetectLanguage(samples, speedUp: true);
5 g- } X& y8 d8 H" } J" \ - Console.WriteLine("Language is " + language);
, n, G$ P- B& O1 y' z - }* j; {; b- C- ~# l1 ]6 u
- ) G5 `: J, ?* Y- T7 ^
- async Task FullDetection(Options opt)2 z$ [6 j! j( a% i8 x
- {& h1 }' A/ |+ l ?
- // Same factory can be used by multiple task to create processors.
; X* M" l+ K: X5 Q8 P8 T - using var factory = WhisperFactory.FromPath(opt.ModelName);% T/ x; Z: |9 C! G1 b% m- R
- % E# m* o x+ ~. S, q
- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);% U* ?7 g" s% V9 a. o8 N
- Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");2 M) H( ]; }' ?4 o: @8 X
- string languageOption = Console.ReadLine();
1 C9 @/ d1 z, A0 z6 P, e1 u - var builder = factory.CreateBuilder()4 V9 v* T8 [9 n: M3 v5 {) D
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);7 c4 G" ^/ b; N0 a9 \5 a: T/ ]. K
- : `: Y B6 J' Y* B: @8 F
- if (opt.Command == "translate")) Z: k2 ]; ]0 y8 L
- {
1 g3 B# Y V1 L* s( k - builder.WithTranslate();
9 e- V' s* q/ i# k) F# J6 o - }+ q% E" R+ ~# c, W4 I1 S( s+ S& j4 z
- 6 E2 j: d5 k8 M( x% F$ M; |
- WhisperProcessor processor = builder.Build();* ~7 q H- T6 ?, a- G
- + _ C+ |- l. U6 A- r. R7 C6 K
- Console.WriteLine("请输入wave源文件目录:");4 D f! D8 k! ~) Z2 n2 r: R
- string sourceDirectory = Console.ReadLine();7 k9 P6 F0 v9 F+ O' N+ ?
0 _0 t$ a8 b; g2 l& Q( S- Console.WriteLine("请输入目标文件目录:");
V& s1 v' e3 b* z2 Q* d - string targetDirectory = Console.ReadLine();
8 p, N2 t: s1 v. B/ _
& b- v y. m3 L, }; z& \- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))
: y7 n# d8 g- @4 W0 V$ u/ d - {' A0 ]1 D* k4 f# R( ] E* n) k1 f* C
- Console.WriteLine("目录不存在,请检查输入的目录路径。");9 }6 }3 g, u4 V+ X7 i. S- e
- return;- c- W: C5 K- W8 V5 j! \6 l$ j
- }$ A+ P- z* z8 }4 G, d! _; A
: j3 |. v' f' q, S' @- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);* p/ ]. D- g( Z9 N1 O
) L7 k2 X4 A+ I- w- Console.WriteLine("处理完成!");
/ J* y: R0 B+ E$ p4 V+ m
4 y$ j3 f( f: i D" n7 [, f2 U% O- }5 _$ T( |7 @7 _( |3 ~
- static async Task ProcessFilesAsync(WhisperProcessor processor,
0 [% R+ ~8 Q' R+ V( L4 x - string sourceDirectory, string targetDirectory)
" z" N& ?0 F% |+ Z1 _1 i8 |& i - {
$ `$ e7 H$ O' n* k/ W$ n6 P - var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);: e: n- [( n6 P* J1 y
% s, Y. a3 \$ D9 @4 T" k# z- foreach (var sourceFilePath in files)( e/ F( a( _2 }8 r3 A; c2 {
- { E% a n3 g# p8 C9 I$ _& y
- string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);
% x: m& W! `: X& z, j$ Y - string destinationFilePath = Path.Combine(targetDirectory, relativePath);1 }, j6 h- F9 k! w
- destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");
& [1 U, X' i j" U - 1 U- y3 {' } n2 n$ S
- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));
8 j( j6 r6 y: G4 V2 x3 I6 O - {- e8 I& f; J; a) M
- 1 S9 l7 C! Z- K& L
- if (!File.Exists(destinationFilePath))) c) H. J- o/ O9 l- l j9 d$ Z& i' O. F# U
- {$ b: z c' T9 q3 J0 I! Q
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");) p; `4 @+ ~9 h. u6 x
- 0 P" c8 G4 U! Y5 r6 A5 e
- using var fileStream = File.OpenRead(sourceFilePath);4 T9 D/ B0 M0 f6 N
- var segmentIndex = 1;. r8 o V8 q, t6 d1 m
- using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter
7 c* V C- b( K$ T$ u* o3 |1 q - var startTime = DateTime.Now; // 记录开始时间
8 p" W( F5 E5 j" B' {1 \
5 R( K7 `' c1 e* u% m3 ?! f% N; j- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))
/ M1 M3 J) v' @, @ - {
; p8 F1 _& }# z6 L5 s4 o6 Y9 X - Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
2 v* f% [+ w- T! n - Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
: B# P1 Z# M e* a) O( Y% M8 ? - Console.WriteLine(segment.Text);
* ]: \9 `7 ^5 X# q% K2 Y! a - Console.WriteLine();$ q9 F' a0 _$ x4 A% |
- " |& p4 s6 l4 ^0 U
- // 将srt内容写入文件
7 Q$ T" t- R' S- a; l. B, z2 c - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
8 Z8 {! j7 Y$ K( o+ C - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");; A: r6 F0 Y; k6 y
- await writer.WriteLineAsync(segment.Text);
3 J" r1 r2 C C& {; T" p - await writer.WriteLineAsync();- M! @+ S3 G1 D' @
- % c/ x! u; t$ a/ j# M) l) A
- writer.Flush(); // 立即保存srt文件
m% U: S: V) { - : B u$ H% X( J& c1 r
- segmentIndex++;
2 [7 a' N9 x* ]/ Y2 L( y; R0 ^ - }
2 M8 H$ t4 E- i, e( [- J
+ y, x( i7 T1 ?4 `0 f' |$ s6 g- var endTime = DateTime.Now; // 记录结束时间4 R& ^! }; l* B# q" O( A4 z, s
- var elapsedMinutes = (endTime - startTime).TotalMinutes;
l3 M8 F* ~: r4 [ - Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");
* {7 Z5 v! O% a' i/ y - Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");
$ d( W5 A0 V8 r( }' z# S8 X - }
4 h+ b' ?! D' _( G/ N7 }/ X8 X* f - else {! v6 _9 `" H% p9 Z) T
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");9 x( a# V) d8 I
- }; q7 p X, [& F7 G
- }8 o7 `8 q# t$ s8 H% l
- }
) C+ W+ ~# r7 }: m1 D8 Y; n d - public class Options) N6 _+ S8 o( i& u, g# x1 W
- {
. h( m: R& b: ?, Z: V6 I - [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
/ `# o/ {% ~4 w8 e/ a3 u& I - public string Command { get; set; }, ~# t( z# a3 B g* I$ C6 R
- 4 Q7 J% [$ W& J
- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]7 W. }+ v2 \8 D9 v7 P) |. {
- public string FileName { get; set; }' U7 U9 f" ?' P, z' m
& y5 C; U& }. }* H- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]
6 h4 C- Y( O }: M( S- w - public string Language { get; set; }, b: ?9 B& R3 x8 N! L/ d) T6 C: A0 w
- ' ?" E) T- ^. _# W0 u8 v: ^
- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]! B8 N) a2 ?/ K! W
- public string ModelName { get; set; }
# }( M$ d* T3 M$ B$ _ - : n/ ]& l/ k+ V4 L- |. K1 Y7 U
- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]
3 b% Y x) M0 G& e - public GgmlType ModelType { get; set; }$ \$ o, I8 q& E* }" e5 ^
- }/ t2 w& [" t* T+ }& D# C4 u
复制代码 ' n, _' e( R I, O) `: _
, x* h. I) S% e
' z2 k9 t; `- G, x4 H2 n7 @% f$ X
|