本帖最后由 shane007 于 2023-9-4 12:57 编辑 1 t! R; h7 M! D5 q* r
; ]$ d- W, W( c8 u+ M' q! M
本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,- ?6 x& d$ K9 V, l ]) V
使用时需要輸入语言,源文件路径和目标文件路径
3 k! i {$ ~& N/ B% u% o最后是输出srt文件* W/ w: m% X, s; U8 G# s' B
7 O5 h' G4 S6 w1 f/ Y3 ^. X
代码如下$ [ T- x! v$ ?6 O! @7 l
以下这句用多线程可以增速,否则很慢! {0 ?, A) C5 _
以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。
7 T3 x! v3 t) U: |6 D9 o3 E
2 W* Y4 @0 P/ Y* T; N! b3 B! P# c: g/ V2 @. ^
- var builder = factory.CreateBuilder()
! v% U* C+ y" f* | - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码 : D% Z1 Y& p# c& H8 X
! j1 z# @+ E: ^: Y, y$ V% x- // Licensed under the MIT license: https://opensource.org/licenses/MIT/ \9 R: @+ I" c) A
# ^6 `# `. ], F c7 w, ]- using System;$ u6 f1 |6 G, E3 S; M
- using System.Diagnostics;/ A6 L, _: I! q
- using System.IO;8 N3 d/ w: @# ?* Q+ B# z) n
- using System.Threading;5 }6 O$ `7 U+ D7 G" t
- using System.Threading.Tasks;6 M0 {( g/ @8 a( e E- @1 R
- using CommandLine;2 m" ?# g$ P$ m) V! M& u
- using Whisper.net;
8 j, ~! x% \/ e- r% v7 \ - using Whisper.net.Ggml;
1 O* S/ T% J$ G* ^3 K3 l - using Whisper.net.Wave;
6 h* o/ T" ~. a% r/ Y0 x - ' Y/ G, y' A7 q; Z
- await Parser.Default.ParseArguments<Options>(args)
: p5 W$ f s9 x5 \' R* v$ i - .WithParsedAsync(Demo);2 `' b, n) t5 J w' p3 k; ^" \4 z# [' s0 }
- & y* l4 j; i) V1 P: u
- async Task Demo(Options opt)
% E( q5 b/ ^5 ~% w/ n, _
7 L: X; H4 |; A9 e5 V4 b. o- { s4 c2 l9 _, a2 l! X+ z+ s
- if (!File.Exists(opt.ModelName))
* D: F( w) }0 y4 V* m; e - {
7 y7 W3 r2 z; a7 y5 J, Z/ F2 L. X - Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");9 { N' f7 a+ K0 t
- using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);
8 A; U" y1 w2 `+ d" o+ H - using var fileWriter = File.OpenWrite(opt.ModelName);
, H! y) n9 x% q% b- | - await modelStream.CopyToAsync(fileWriter);$ L: @3 T2 D6 W! q
- }- w; r& a* h7 d' [8 z, r8 z
- # u! s8 l# U- w- g( Q9 ~$ o8 L$ p
- switch (opt.Command)2 `+ d& o& F6 q6 g- G
- {' k) }2 B/ H: ?2 t7 P
- case "lang-detect":( b( G7 E7 O0 _4 }- c
- LanguageIdentification(opt);4 n! Z7 `( y& p) W
- break;) f3 y- e1 x& @) ~! O3 ~4 ^: X
- case "transcribe":: w3 W9 S e1 M* P7 `# H
- case "translate":# H3 D& F( {3 U% E* |% ^
- await FullDetection(opt);) d4 a' K; c& p% q3 }, u9 L
- break;6 C4 f9 u9 z# L* k
- default:- u/ W& R" Y C# d% j x
- Console.WriteLine("Unknown command");
; d+ _' S4 H' }: T7 M! w$ k - break;
8 w$ G' w2 i/ {& f$ S - }: x) N7 j* Z, I) u4 l+ c
- }
& J# V3 X6 Q+ r4 T
7 V! T/ |- Q) e8 i' \8 [4 Y! B, V8 Z- void LanguageIdentification(Options opt)
5 [0 b# p: P2 m! E - {0 ], ?. z0 v2 x2 d* {, D
- var bufferedModel = File.ReadAllBytes(opt.ModelName);2 G' ~/ A! J' V( ?7 S! _
" C2 ?' C, `: Y! H; Z5 P/ k/ } W- // Same factory can be used by multiple task to create processors.
9 `' Q$ p$ O& D+ } - using var factory = WhisperFactory.FromBuffer(bufferedModel);
1 z) M9 K- a! N, A" n& p1 v0 F
2 M% n1 u8 }6 A; k& c3 V- /* var builder = factory.CreateBuilder() ?% M% L& _/ ?/ x' V; {" k/ E
- .WithLanguage(opt.Language);*/
# k$ d0 L" Z* d p. [ - var builder = factory.CreateBuilder()# T5 N! {: l! J& _9 g; K4 `
- .WithLanguage("english");$ }7 G% f! b8 T; p: \* i- K
- using var processor = builder.Build();3 M1 B2 a' L9 z0 _) d
- / N& ~; y. }7 O) K0 r: M
- using var fileStream = File.OpenRead(opt.FileName);
3 h# r: t0 I5 ?! T6 q1 V
; y4 g3 q( Q/ _& V- var wave = new WaveParser(fileStream);
8 @- A% s- @9 M0 y* Y
$ _* J9 P' H8 [7 } H9 I- var samples = wave.GetAvgSamples();' M+ P q8 [% R' w: o& A
+ |& q8 `/ m4 R- S- var language = processor.DetectLanguage(samples, speedUp: true);
+ z- c* |2 \8 A- s$ B9 J# h9 ]$ v - Console.WriteLine("Language is " + language);
$ E+ [1 w" C/ a* Q - }
1 e) v# a, N# \4 t- S% c1 K - 2 ?, R, z; B" [- f1 a; B
- async Task FullDetection(Options opt)
* w' f' A% P+ q; f- |; t - { k9 A0 B( D6 f( k; O7 Z
- // Same factory can be used by multiple task to create processors.; M" ^7 c+ }) K0 Z0 x2 `1 w
- using var factory = WhisperFactory.FromPath(opt.ModelName);
: y ~% w; k0 M; W1 K$ d3 e: p - ; G/ u+ `+ @' d. J# W: J' H# K
- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);
3 C* r; ?( Q4 `$ f, g6 a - Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");
}) o+ S" I& T- b - string languageOption = Console.ReadLine();5 O( u. ~' {& V! _
- var builder = factory.CreateBuilder()" ?1 o4 J _0 ] u/ K" a9 W3 f z7 R
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
+ l7 c( U/ ~% b2 C - 9 r( z6 @/ V' T3 Y$ t& u" f1 U' R
- if (opt.Command == "translate")
. m; f4 A( W% t/ ]" M! q. T/ f - {
' d: ^8 O# O( Z; L) v( }9 e - builder.WithTranslate();
+ L& }$ k3 b4 s# W2 { D - }# C! Y" T& {+ m+ `
/ _" w! h* `! ^- WhisperProcessor processor = builder.Build();% P7 Y7 \9 ^" y- b R) [
-
/ s/ J8 m7 e) `# v1 q4 P - Console.WriteLine("请输入wave源文件目录:");
9 |8 I d' `/ \! ]; @9 E - string sourceDirectory = Console.ReadLine();& N* G& J" U. x- }6 D# d6 s
5 h3 h6 K6 p" I3 p' e# @) w( @- Console.WriteLine("请输入目标文件目录:");2 W: k! |! k) w7 u- r1 F! Q
- string targetDirectory = Console.ReadLine();5 e# L6 w) F6 g% `5 [5 X7 P
; F$ \$ E/ @- @) H# x: W# Y0 ?- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))/ [) r9 b T( t) I, t4 K/ Q8 B
- {
' \: Q1 p( _- r8 G - Console.WriteLine("目录不存在,请检查输入的目录路径。");7 L0 @" h2 x* F
- return;
! y0 Q) j% j5 ? - }
4 R) W5 i. M/ L/ X - ( E) o9 @; f, ?: w9 a
- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);
! {7 N: U4 i: D- S- z - 4 H+ F' Q) r% s8 `- {: G
- Console.WriteLine("处理完成!");
2 `% P3 y- F7 V/ B
3 [9 @& u4 ?8 h( c4 R k7 J- }; }# ]2 l% }" X% T1 Q$ B. b
- static async Task ProcessFilesAsync(WhisperProcessor processor,
8 M8 Y8 S0 f2 h - string sourceDirectory, string targetDirectory)
4 s7 P" T! K, J5 n( n - {
' g+ P1 o" N! ~8 b& u/ G - var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);
' X0 ~. b: [2 h* m k8 B
8 \, z* `8 C: Q% A- foreach (var sourceFilePath in files)
# E; p6 v) n1 }. H2 M" ^ - {. N) T% @; }. N! ?
- string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);7 m0 l8 r& J2 ]& y: ]
- string destinationFilePath = Path.Combine(targetDirectory, relativePath);7 ~4 @' _# Z. A" B! Q" G% F
- destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");
& x5 _5 L1 x* E' U- M; G - / D; o3 n) P! y+ n' \
- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));! k3 ]/ v' n1 `% Y2 d
- 7 ^8 z) d, h5 d+ F% A" M# [# r
- 5 G1 j5 _) ~8 M' m. A
- if (!File.Exists(destinationFilePath))
! N& B5 Y9 S5 f8 f C( { - {1 _8 T, M4 B6 M1 @
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");. E* @8 z3 q/ L) r: R; ^+ |
6 w: i" E- k% D. c5 T- using var fileStream = File.OpenRead(sourceFilePath);
0 ]! T& R+ o& Z; K# M# | - var segmentIndex = 1;3 R. X6 c+ f9 S+ f
- using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter
; f& ]# e# T$ z+ _6 D `; r, o - var startTime = DateTime.Now; // 记录开始时间* X! O2 X4 n" [( t& t y
1 b, a: j2 A" l2 ~" \- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))0 B, q4 r( \5 F. u6 u/ y+ m) Q
- {8 s* q( v% H2 c
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");& Y' b/ _7 U; _6 K4 }% M3 `
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
6 }2 o9 T' x4 t* k; q1 A3 u - Console.WriteLine(segment.Text);' T3 a& j; T+ B" j# ~& T
- Console.WriteLine();: b' S# s& J6 `: ~1 `) ]) P* z, G
- 1 p! B& \' S2 w" n; k
- // 将srt内容写入文件
- j; q C. S+ t9 ~ - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
6 U1 y/ [3 A/ O9 k Q" u - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
- F- D7 e z* q9 h - await writer.WriteLineAsync(segment.Text);
1 j: ?" E1 @' V3 q0 R3 @& q - await writer.WriteLineAsync();. T: `3 V# |$ u! S
- $ j) F; S- V. o
- writer.Flush(); // 立即保存srt文件
: u/ s0 N0 z2 P$ H4 y - , l e7 E5 a9 u" v- j9 \
- segmentIndex++;
7 T4 p5 W) p6 H+ m4 B - }
# w/ C3 w6 D" L+ W - ; b8 x; U, q4 e1 x' P' l8 a
- var endTime = DateTime.Now; // 记录结束时间5 e* o0 m8 |: v: r
- var elapsedMinutes = (endTime - startTime).TotalMinutes;
6 e% J8 C. A9 q D0 z - Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");3 Z8 K2 c4 W9 U( _
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");
6 c$ h( @! O) u - }- L$ V1 d5 k# t1 E( P5 ~' f4 c
- else {" d# W% {* ~8 M6 U% Q, H0 b% p9 U
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");+ u4 o: \& z c* [; W- K
- }8 ]% O% s% K4 U1 ^7 r
- }" y# G6 C+ F8 U1 Y# x. Y6 `0 {
- }
; l" V$ \3 {% n& O - public class Options
6 Z" p& z& K. X9 v r b9 v3 W - {
1 \2 i0 U% L; G! i) R6 W+ L& {5 J. [ - [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
, z( t, Y: s& P: W; l2 ^, p - public string Command { get; set; }$ G9 P; ^: f8 P p+ r
- : n4 _' ]4 {/ e- h! @" `, h
- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]
- ]! t4 R5 V; m# U) e/ C - public string FileName { get; set; }# i, q) {. T' _5 E9 u4 S6 n& n. h. r
- 8 d' o7 Y- B! r$ l2 B) @
- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]/ e* F4 a7 ~+ u; D
- public string Language { get; set; }
" `5 S! h8 m `: h, O2 C: \% l
: { Y7 v# E& Y' g: W" k0 ^- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]
q# `4 a& f* }& v6 }; I - public string ModelName { get; set; }7 ?, I& l0 H. ]- [
" f" ^, V- c* m# E' @- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]
5 Y+ A4 }& s3 _" `9 H3 f# z- j - public GgmlType ModelType { get; set; }2 I5 f! F6 Q5 S& S
- }+ r8 O) y, n; S2 X
复制代码
; \7 t* @& m( P/ v ?/ ?: c* e" o
0 L9 M6 n2 S2 r y: p# N
2 _0 i0 ?' e4 b! t
- z2 e9 J/ C' e. c |