本帖最后由 shane007 于 2023-9-4 12:57 编辑 / I( M# n8 T" R' {0 L( Z
8 L f0 _4 x- Y! u
本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,
1 n6 d& T# i0 X% R: S2 S+ `( q 使用时需要輸入语言,源文件路径和目标文件路径' S! x- Y' r( s$ w# {: C' X
最后是输出srt文件
6 R6 m5 g9 j! j0 b" a/ o. t8 q3 `9 `% @& {
代码如下# j& E! ^8 R$ p
以下这句用多线程可以增速,否则很慢9 B% C( B- x: h4 {5 b0 a- a; k
以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。
: w$ k. R) n+ `( J. c4 C. J O- v/ \/ c) z w
6 j2 |( W/ `% j6 s9 y1 n
- var builder = factory.CreateBuilder()! s; L4 S, D- v Y; O+ g1 d$ H# Q
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码
, S( n- c& [* V9 z
% X$ b1 j6 S# p) H2 A& @) K- // Licensed under the MIT license: https://opensource.org/licenses/MIT- V3 f" S9 ?9 ~( h7 G0 B" J
- o$ Y. O9 u+ [) M! |+ N
- using System;
$ E; n9 p- f4 y) x h& j - using System.Diagnostics;( z/ k$ B' W) K" E0 ^) Y- e5 M, [, R
- using System.IO;
# @& E% t8 [! A' E' x$ p! T - using System.Threading;
. Q" \5 X! k1 Z3 z - using System.Threading.Tasks;% q2 U2 r# P: r& c) i
- using CommandLine;7 I* y* {% J0 S; ~
- using Whisper.net;
6 j8 o* \$ J3 r% ]) K - using Whisper.net.Ggml;# I5 @$ R; T3 h. Q7 G) i. ?
- using Whisper.net.Wave;
6 e1 _/ ]0 x8 ^' c% V8 X" v1 n - 2 I2 Y9 X8 X2 w d1 c o
- await Parser.Default.ParseArguments<Options>(args)- M- c# I9 a) x! e, Y+ {6 d! e
- .WithParsedAsync(Demo);& M4 E! J' W' A$ E! `/ |. S; H. d
/ I( w3 m$ r' N7 Q" I5 m- async Task Demo(Options opt)
% n4 k+ r/ I/ M& R$ S" N: {* |
( ?& D3 b* C, Y3 k# O- {
x3 ~" J. c* v! k$ O8 ^ - if (!File.Exists(opt.ModelName))
# {$ f2 t: N/ V" ~& F' Y - {
6 r/ J D( o/ R - Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");" W4 |" o3 {" F: z+ T1 \* X, r) t
- using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);- }% U) h! t' I+ u: ?
- using var fileWriter = File.OpenWrite(opt.ModelName);
( E3 v1 C4 _( G( z# Z - await modelStream.CopyToAsync(fileWriter);
8 W1 S X6 Y' m* q& Q( X - }
/ o9 R# o& `8 \" x# P' e - # ~; N+ C+ k. B! k! H1 @
- switch (opt.Command)$ ?. d( t. V) y6 G7 Q( f7 i) }
- {
2 I! Y/ ~ k: m - case "lang-detect":
3 A, f* d: i0 U3 k2 E8 s8 D3 w - LanguageIdentification(opt);
6 u0 V1 n8 Z) t9 U S - break;1 h+ T$ P, @+ A+ k) e
- case "transcribe":
& V* q7 [& R' F - case "translate":
K$ Q" R m! y - await FullDetection(opt);
, `9 q- p3 y! X& E1 o - break;
6 v1 s# C+ T. ^2 ^- p5 R' X - default:* {& [% m: \" z' z+ _
- Console.WriteLine("Unknown command");
/ X3 w% y6 j+ I4 O2 p' ?0 k - break;* u& i ]5 m( {; n# O7 F( k) L' X
- }
& j5 M0 t6 H! l4 w - }
" C0 G7 K6 u* b c
9 @8 t; C, [2 J- void LanguageIdentification(Options opt)
6 N: E, r+ s% S# M) V - {5 a9 m4 z3 [ m# f j( V+ U& V- a
- var bufferedModel = File.ReadAllBytes(opt.ModelName);- [. i5 o; g8 W# ~. \7 y
0 B, u; o* h8 t- D' d8 l- // Same factory can be used by multiple task to create processors.
" m* O* U" f! F4 W0 L - using var factory = WhisperFactory.FromBuffer(bufferedModel);" M0 v |- Z2 x; D( s
- 7 n6 {0 n) ^8 k- w$ A, T- y. U8 q
- /* var builder = factory.CreateBuilder()
; V2 @, C5 F; t - .WithLanguage(opt.Language);*/8 k0 |. K. {( I W5 Q
- var builder = factory.CreateBuilder()# M+ j$ l: ^8 m, |1 Q9 Z/ Y* g
- .WithLanguage("english");8 z- }, b& z& E' O. w5 Y
- using var processor = builder.Build();
" Q& v2 `+ v- r
' y, y+ Q+ T& i/ d1 N ^- using var fileStream = File.OpenRead(opt.FileName);
4 P3 b# Q9 b# V( ^ - ' ?. Z, b, S" j0 ~: p
- var wave = new WaveParser(fileStream);
X) b9 B' F2 ?6 U1 f @$ z - ! b6 `+ N: m/ X0 S% h5 |9 r
- var samples = wave.GetAvgSamples();
" F' x: K: U# K8 P! \6 k - 5 s4 Y1 y( m" }) N% P2 ^. h! t
- var language = processor.DetectLanguage(samples, speedUp: true);9 h+ t4 x* z- f' ]5 ]: k. E6 T& q. Q
- Console.WriteLine("Language is " + language);
; _6 \; Z- `8 }, |& u$ A - }
; j" W6 X/ q, Q - ' B5 ? ~% |& p: O! }- `, y8 B S
- async Task FullDetection(Options opt)
5 P% G0 S1 S& j8 {& c( r5 X - {
; w, ?. O+ Q) ~ - // Same factory can be used by multiple task to create processors.9 u' w* e: x$ H# [/ D
- using var factory = WhisperFactory.FromPath(opt.ModelName);" F, j3 z4 }# b; ?* h
* z( F8 l, e$ ?; w- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);
) R8 y) m) ^) T. L6 [+ d# w - Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");
: k: @, M% l' y. m - string languageOption = Console.ReadLine();' Z: W/ r: O; Y% H* `4 \
- var builder = factory.CreateBuilder()1 A" D: z0 A/ l. k. j
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
/ _$ m7 r. X# j) a! v1 Q- B - W! G3 K7 x- M7 t
- if (opt.Command == "translate")- j$ W( {1 h M4 a
- {
! `- i$ d/ [8 K$ Q - builder.WithTranslate();: s! T. Z2 }+ w, n9 ~% w$ @) f
- }
7 `8 c( C, \: k m
5 o/ m4 B' [+ V; l8 h; ~- WhisperProcessor processor = builder.Build(); P; q) W8 [8 Z* h* r
- - D$ V2 R/ q4 K; ~6 b2 N4 [
- Console.WriteLine("请输入wave源文件目录:");- T( _" y* m# C: O; J& d
- string sourceDirectory = Console.ReadLine();% x2 n+ ~9 O Z' c; `) ]
- : O- l& s" ?$ y
- Console.WriteLine("请输入目标文件目录:");* L& e5 l* ]9 n' e3 y
- string targetDirectory = Console.ReadLine();
9 A8 {# a: q0 }" t+ m9 J
j4 s4 s/ |1 ?; T6 X- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))
/ P+ R! r: n* N! G/ i - {: L) v; x2 D, U% t; y. c
- Console.WriteLine("目录不存在,请检查输入的目录路径。");8 {: s! W, o N6 n0 Y! [
- return;
" g2 j6 F6 U6 u5 e9 z: @& z5 M' [ - }+ ?8 j7 v7 J- Y+ K8 H& G; ^+ J3 I
- 3 w* {, {: g4 B6 I1 ]1 p! C( M
- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);4 v2 o' g, |; Z r) F: `
! m- L- z. {# ?8 u/ d, d" }- Console.WriteLine("处理完成!");
% f/ R. H# k( T! P7 p2 g3 k - ~1 L' | y* @) ~: l2 n2 W' ^
- }
5 { B3 L& A+ k/ u3 R - static async Task ProcessFilesAsync(WhisperProcessor processor,
a7 t# t* P' u: U5 U' r - string sourceDirectory, string targetDirectory)( Q; l* q3 W! a# W& F$ Z
- {6 |$ n: Z5 E0 M2 X. P
- var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);
: g- |& g8 u5 Z# K3 u9 [3 S
1 z5 i% j! I0 h5 C4 T, s- foreach (var sourceFilePath in files)( w& u+ _3 R' p; _! E T. Y6 R6 t
- {, `# x4 b9 X: [
- string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);
2 ?! j1 Y3 L: `0 T z& ~: i - string destinationFilePath = Path.Combine(targetDirectory, relativePath);! @5 A X, O2 C0 f
- destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");
; h' k4 w6 O. T
$ J" `% h5 W0 i; ]6 Z" L7 \7 y- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));& R3 [$ H. D% O* R: M% m+ \3 h
- 0 A6 B$ n. U3 h) ~' [
; J! b% V5 K% \3 O- if (!File.Exists(destinationFilePath))' p" j1 N/ c( E" c7 \
- {
! E& a$ i c+ a2 Z" U/ i - Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");
& A" o4 i6 V% Z ?
2 S) K( P. D1 U/ b- }) y- using var fileStream = File.OpenRead(sourceFilePath);
6 a9 J, A# ^7 c, w; L* O5 l7 W - var segmentIndex = 1;% E0 v! q u& r* @
- using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter
+ N1 k( Y- g2 L/ ^; E: B - var startTime = DateTime.Now; // 记录开始时间
7 I8 j6 m% o5 N# m) }6 }
* s; s$ X; f) B- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))
/ o1 A3 k/ d( C& { - {
" e; S5 {, K2 Z1 R) U - Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
$ o7 i8 D# w2 ?( j8 P* d8 r# u4 p - Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");8 |) W" V( ]* v# J8 p& s
- Console.WriteLine(segment.Text);/ w6 `( T4 H& q, t/ y
- Console.WriteLine();; E. e' m4 P1 N, D9 z: a
- - T# X2 a8 c, A; X- Z! T
- // 将srt内容写入文件3 N# d7 }" _5 l3 _# }
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
4 t6 c1 Z6 v! g6 W' o* j( C - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");& a( k' u1 v. ] l1 _
- await writer.WriteLineAsync(segment.Text);( @# x( k/ E. t( C t) e. z
- await writer.WriteLineAsync();# C8 u' T0 ~+ |
- * r1 }2 ^; L# J" K- [, Q
- writer.Flush(); // 立即保存srt文件
1 c4 B5 S7 o+ m/ W - % |$ o( p# p; s' t
- segmentIndex++;
) K7 T8 D% t7 q4 X+ { t3 R8 x4 F - }! t2 p9 G9 `3 }1 C% R Z3 q
* E" _% X% k" f$ _% P5 m- var endTime = DateTime.Now; // 记录结束时间, W9 @. g5 \! Y* r7 X1 ]
- var elapsedMinutes = (endTime - startTime).TotalMinutes;
/ n: A/ }5 I) }1 _8 _. M - Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");
% Y$ C, W- t! o' X" K' V! G - Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");# @7 e0 n* l* `5 `
- }
1 f. A1 t% T+ y - else {
. f1 N6 v7 G" G# B/ q$ s7 o - Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");' f2 H0 r5 n3 N! S8 p2 }& z) V) h
- } }7 g& O3 r' `! u
- }) { e2 x/ d' ~2 W! t& o7 \
- }
! v! y$ g' B, K" K - public class Options; W6 Q5 _& V( t5 a% e
- {
3 x; j- E' P& O% Z w - [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
6 Y1 O; U* p# N& I - public string Command { get; set; }
7 q& o' U- Z3 G - * |) r3 K1 @& U6 b) {
- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]
% X6 \- p% e! u6 u: {& d - public string FileName { get; set; }
7 V. i' j- s7 w - 8 ~1 m. y# B8 {2 t
- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]6 H2 N s, x( H* V& X" s
- public string Language { get; set; }% R7 D- Y1 P( ]% `( K3 @
7 r' M2 N0 [& G: q2 r' ?5 Q- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]" ^7 ?5 i( i* H+ f6 N! i4 u
- public string ModelName { get; set; }
; q. q, G0 }8 _6 E( }- o' Q" t - # g& R7 g, S* @# e4 m" h/ o
- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]8 a# q4 V! |$ C
- public GgmlType ModelType { get; set; }+ y0 ]) `2 t- k/ k
- }
! d2 x8 o# w/ d$ V- R5 H6 v
复制代码
5 h* ? k# Z& ~; u8 N$ T
2 H5 g7 ~* u/ q! ^+ Q/ S2 Z# ]+ q3 H! P4 E2 J
* G: s3 g2 d: M! L% J
|