本帖最后由 shane007 于 2023-9-4 12:57 编辑
# v; C Q/ i1 R. \' l; X
$ ^ c( Z8 E) z U0 s$ n* E4 a2 ? 本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,
9 D# u9 l% F* Q% W 使用时需要輸入语言,源文件路径和目标文件路径, t/ w5 U4 @+ Y4 V5 V# C% Z7 @; o+ _5 v
最后是输出srt文件; H" H7 e7 A$ k- z' M
3 V6 I, b4 L$ s* G, }% o+ j7 n
代码如下
7 F4 ^( \! K; t6 u ]以下这句用多线程可以增速,否则很慢
: C0 p7 I3 c& u以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。
" [4 ?" \/ {- Q, C8 O+ e
3 D" V3 o& r* U: q1 k7 l) w) }8 K
- var builder = factory.CreateBuilder()
4 C- y6 \1 E" O+ }; [2 w8 Q1 I - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码
' z, m1 R: C o. W2 H/ z
1 Z# p0 f1 f' e- // Licensed under the MIT license: https://opensource.org/licenses/MIT9 F- w) j% y* E! E/ ?: N
- ( x4 C+ U. @4 ^4 J& A7 S; A
- using System;$ i5 x& V$ A+ s; B5 z y
- using System.Diagnostics;: c) J" ^5 w2 F( ^" C7 A
- using System.IO;2 y; d& ^, X$ H; Z9 b
- using System.Threading;' }4 S/ e- V' m3 F/ ]5 m# t ^
- using System.Threading.Tasks;; U7 e, n5 c; [: l" E4 I! i. G
- using CommandLine;# }, x7 \' ]" s0 b9 o/ e
- using Whisper.net;; i+ b1 {3 O! w! }# w" V
- using Whisper.net.Ggml;9 ?/ }7 a! [2 v2 k$ H ?; g
- using Whisper.net.Wave;' \3 ^9 D8 ] u: Y) ^
- / s. }! e0 x( j0 X4 n
- await Parser.Default.ParseArguments<Options>(args)
* Z H) A8 U R o- J - .WithParsedAsync(Demo);
5 ~9 N; g' m' G/ z- f0 j - ( }5 C/ X$ Y. T% c) e' k
- async Task Demo(Options opt) A* r+ k( _& s1 @
; Z8 j) r' J+ R. D4 Q x9 x/ Y4 J- {1 |$ {5 A: D" Y- J2 O2 P2 I/ m; @
- if (!File.Exists(opt.ModelName)), B: T- K% X0 j8 U9 z4 T
- {. B& k: b# O0 Q3 F) u$ A
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");
+ [) K' ]7 @, @# k: Y5 w7 u3 E - using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);" K" s6 S6 Q' b3 C8 N) ?1 U
- using var fileWriter = File.OpenWrite(opt.ModelName);/ e, F: n1 c7 i' p5 K7 N) p
- await modelStream.CopyToAsync(fileWriter);
w# a0 h1 B) F: c% G9 T - }
- t) ~5 F& y9 S; X - $ O* `9 T" B- e$ L( J' L
- switch (opt.Command)
( F. L# P3 e( p7 Q3 r% U - {7 s! V$ I, Q0 X+ B" n
- case "lang-detect":
- k& z6 e* [# l1 a - LanguageIdentification(opt);
& a5 @% t) [4 A2 M0 a5 }, d - break; g$ f+ } v y' u* [+ x" T
- case "transcribe":
* \' g2 i2 Z: h - case "translate":* g! T' R6 X7 |7 |8 U" G
- await FullDetection(opt);7 ?* S' x X6 X, ?" M8 B( ~' O1 J0 d
- break;
]$ e8 w& ~" F( a6 r" C; h - default:
/ p3 z6 g g7 y0 p - Console.WriteLine("Unknown command");9 G" s2 ~2 _) e
- break;
2 I% B) {3 z% T- C. ^ - }, q! S( ?/ R% e9 a5 a4 T
- }
$ m( [% [, J$ z; {" G; m+ c! H" d
{; M' r$ ?3 u% B* ?, R; c' Z- void LanguageIdentification(Options opt), Z N: l0 W2 r( w
- {$ d2 ^( T, ^$ _3 ~
- var bufferedModel = File.ReadAllBytes(opt.ModelName);% c7 N( p, M0 e2 @4 t7 ^- l% M
8 E6 t$ l7 k' Z# h9 ]+ |0 J- // Same factory can be used by multiple task to create processors.. U) L5 w t7 g* m, p w
- using var factory = WhisperFactory.FromBuffer(bufferedModel);
* f5 |& q# C; u; [
8 L! B! Y: h' t3 h: {- /* var builder = factory.CreateBuilder()
! ^/ p6 j* l' o - .WithLanguage(opt.Language);*/6 c2 ?, Y+ m9 N% }, E
- var builder = factory.CreateBuilder()
" S6 I, w$ i/ Y6 Q6 \$ e - .WithLanguage("english");
2 L% L% ]8 h2 p# ?% l - using var processor = builder.Build();
0 T% g4 t+ N$ d4 G( N! U
4 S' B9 ]8 N% |# f! k! W. N6 p- using var fileStream = File.OpenRead(opt.FileName);
+ T! [- ~. O/ l& X# d3 B, j3 @4 H - 5 v- V5 v% j7 \% f0 `3 Q# f
- var wave = new WaveParser(fileStream);' J/ T3 k6 r. }# U& U6 `) V
. L- s% y* }. ~, |. |% ~, r- var samples = wave.GetAvgSamples();1 B! L$ |# L6 R( F* _( h
. o% N# X2 p7 m" k- var language = processor.DetectLanguage(samples, speedUp: true);
) F4 D& Q3 n! x4 j H - Console.WriteLine("Language is " + language);
" d+ @% o4 P* b2 c! g; B - }* `9 p( G" f2 A1 d
( \5 I3 R& G% h7 y- n% U6 U! }- async Task FullDetection(Options opt), T2 m+ ^; z$ ~1 D1 d+ s3 c
- {
/ R6 G, w+ v* p/ I: R2 r* W2 i0 h - // Same factory can be used by multiple task to create processors.; w; W7 g: ]- f0 G/ Y3 V! _& ] x2 \
- using var factory = WhisperFactory.FromPath(opt.ModelName);
" x/ k. G) a$ s0 Z5 t( c
. m2 o! T' I5 y1 t5 z- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);8 ]( z8 k2 V, T7 D" w7 C( @
- Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");
8 \/ L. i: k2 G! g - string languageOption = Console.ReadLine();
' M7 I) T- }* x. Z& m9 X - var builder = factory.CreateBuilder() p. s; v* Z$ m" F
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
( t4 r/ ~4 H; M9 N - 6 s- ?9 Z1 m, P! M2 `
- if (opt.Command == "translate")
* J* ?7 ]4 U9 d" d7 |% ] - {. A% ^- c# |! O' Z+ G
- builder.WithTranslate();! I: Z$ T$ F Z+ {
- }
5 Y/ K0 k: [" [5 S. z+ \ A
; ~. m! c; s3 I/ F! `- WhisperProcessor processor = builder.Build();' U, r* `0 m! s" n$ [
-
/ Y! f, R8 w* x" j - Console.WriteLine("请输入wave源文件目录:");/ _4 w% N+ R9 D8 r( U2 V: n9 m
- string sourceDirectory = Console.ReadLine();
$ |3 E2 q3 _# L# X7 t; Y
5 i" Y- q$ k4 L7 X- Console.WriteLine("请输入目标文件目录:");
- L) O: Q: p# k* g9 q0 j2 N - string targetDirectory = Console.ReadLine();
/ _' D4 I k$ V- Z9 F, L1 C; D - 3 ~* [2 k) }$ F& z2 ^1 h
- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))
; I$ G- a- T4 Z5 o, Y4 L9 U9 u% O. L - {
6 k g) s9 b, b' f6 P - Console.WriteLine("目录不存在,请检查输入的目录路径。");
! Q8 ^1 N& B/ `+ J - return;; H0 O+ ?. v4 L: R u
- }
" y$ ]& }) X. R1 q
7 x1 ?7 y8 c( g- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);) G. n, U' s0 Y, u2 I
- 1 i& q e4 H& b; Q! A2 r- l
- Console.WriteLine("处理完成!");
, x/ p6 M1 a& P; A# r# Y: D
& f9 z1 V5 Q1 V M1 { {% e. l$ i- }
" L( m3 _+ C1 |0 C2 k Z - static async Task ProcessFilesAsync(WhisperProcessor processor,
- f- e& [5 t* A - string sourceDirectory, string targetDirectory)$ M9 z( N- l3 n: P
- {
+ d) x8 o" w5 w" t8 m - var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);
8 d3 }3 X, B* J( N1 T9 H5 N
! S; J: E+ p6 L- foreach (var sourceFilePath in files)
- B* c- a) M( L" X7 o$ t - {1 H% g7 r% w+ m' U0 _
- string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath); ^) Y# K' r, E5 w; h& `7 ~
- string destinationFilePath = Path.Combine(targetDirectory, relativePath);/ T# j2 H1 [0 `8 f: J7 d& X6 y( ^& w
- destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");( d7 A1 L( D7 ^; u
3 e9 I2 W/ @6 {$ k; j- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));* y; Q$ d* f M8 m; ^( [
- % ]6 P+ Z9 K$ V) A, a6 P
( z* y$ n) E, z2 [% O7 p- if (!File.Exists(destinationFilePath))
! I w* U: P3 b4 R+ Z' H" n - {
' ?% B: ^. I" i* Z# W - Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");
/ b0 F- ^# {+ S0 A4 T! t& x2 C - , b, F; x& N& a" T
- using var fileStream = File.OpenRead(sourceFilePath);
; E% n0 U) _4 t4 I) w - var segmentIndex = 1;
, Q$ G, C7 ?) o/ A) h - using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter/ s0 l1 }' N. P6 P7 M2 ]
- var startTime = DateTime.Now; // 记录开始时间
3 t, y& H& R% G5 p$ F3 i6 J: e& D
$ T& G6 V5 y' q2 S4 I- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))4 D3 |* E6 w; a+ U4 C2 S
- {) y2 Q3 M2 L2 L( ~( z0 s; U7 m) {8 A
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");5 H9 Z4 m! b+ w
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
7 q0 k6 R/ v+ |; |2 Y - Console.WriteLine(segment.Text);5 h# o/ F/ I0 t6 p5 L% r
- Console.WriteLine();
& j U$ u% W5 \! c
! z: u) ^ M, Z- v G- // 将srt内容写入文件
& Y; v4 T2 P, S3 I6 C& Y - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");" a$ A4 F' ^- ^- N2 E
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");1 Y X2 m2 R% E# I. w N& |$ ]* x: \
- await writer.WriteLineAsync(segment.Text);6 F2 S9 A0 R$ B2 a' e' P6 p0 g
- await writer.WriteLineAsync();+ c( d7 K+ i A% F+ M3 O9 K
3 ]/ }5 W, h, G' B- writer.Flush(); // 立即保存srt文件' R* c( a& b5 O2 Q! x" B4 ?8 |
9 Q8 ^. }" Q; U- y/ w; J- segmentIndex++;, L) F2 b J- p1 T9 E
- }
! t# P: J8 {( ?* u8 j3 T
9 v3 m2 ^5 _0 [- var endTime = DateTime.Now; // 记录结束时间
# }5 s* D8 J: o" | - var elapsedMinutes = (endTime - startTime).TotalMinutes;
( X9 s q) q ^! C: J: M$ X! b - Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");2 W5 `' c) n2 a8 k
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");! X5 u& l' D/ J; p5 L/ f
- }
2 c& ]% s! a& _$ n& k - else {
- Q: O+ P( ]5 t8 [% h0 B$ R - Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");
! l5 Y8 v" J& }4 w( j4 t0 H& o - }7 k, k% I. |9 V' Y" K
- }
1 Y6 b, p/ o3 G0 `% \) O& M2 U - } J' F: X" ~7 d% @4 S$ }
- public class Options6 a+ k/ B: G& C+ G' w
- {! e \/ _( _# Y$ G# n& a+ o; M: [
- [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
7 G+ d0 j1 G. ^, o - public string Command { get; set; }
7 H. p) d; ~+ k; B) P - / ^$ D0 j2 k" r. }: h$ u& T
- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]
- N6 }# Z% F! J+ Z - public string FileName { get; set; }: p9 }! g; d- z z# \
- 4 `, d9 L; A- o* W, y% Q
- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]
+ f0 o4 S7 J4 P3 f( s - public string Language { get; set; }6 y0 h! a* i" \
- 3 w. G3 _9 U6 L( `+ G) N: `8 _9 ~
- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]. F a4 N; o, Q" r/ F# V2 B# R4 k
- public string ModelName { get; set; }
0 o* ~. e5 l5 M0 L) b! X6 `
2 p# e/ l% `8 F, g5 h! l) R9 R) \8 [8 n- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]
6 ^; S! Y4 Z0 [) e4 L) e - public GgmlType ModelType { get; set; }& C2 Y8 T% \) R$ V& m. k& m& n
- }" P% O3 ^; x5 x- T1 K7 i
复制代码 " m. t( ~5 s/ J; o4 W8 _7 k
7 C9 s+ U* M' U, v
. e" r5 K! K* M9 O. ^
q* W: H- _3 D6 ]. I. h2 g8 M# A! Z |