本帖最后由 shane007 于 2023-9-4 12:57 编辑
1 b6 ~0 M5 L( D- {
9 B' @; H: `& P1 s9 ?+ ] 本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,2 v. ?0 _) j2 Q+ `
使用时需要輸入语言,源文件路径和目标文件路径
1 ?8 T- v) [1 b6 e6 e最后是输出srt文件+ v: S. P9 J, U# B
0 O9 K, I9 y5 s3 t! L! y+ \1 g代码如下8 i B9 F+ S. w7 d4 X
以下这句用多线程可以增速,否则很慢) v8 A& M" z- m( X4 i
以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。5 r- e2 j+ _5 g3 h" F O2 l
6 L( ~& D- ^% {8 b' H% {
" n4 y2 T6 q4 W: t) P; ~* E
- var builder = factory.CreateBuilder()0 U0 l- z4 |8 I, i/ j/ t; `+ b
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码
; r4 o* ^ B1 J" _' K/ A! n1 L
, R4 _) L- F$ l- R. ~* M, O: y- // Licensed under the MIT license: https://opensource.org/licenses/MIT
* p, M7 [. @8 n5 m - ! g: c: ]) O+ E- X2 {$ l
- using System;! K/ f) z2 d* f: |/ {
- using System.Diagnostics;
+ Z! E2 y4 }3 {$ ]$ v$ ~2 O4 k9 a - using System.IO;6 L8 n2 |3 i# c) @- \% Y6 r, }! s
- using System.Threading;& r/ j& ~$ e0 n; B- ~
- using System.Threading.Tasks;
' q. R8 V' T. |" [ - using CommandLine;( D3 }/ b ]4 q$ H- L
- using Whisper.net;9 ^9 k8 f7 e& T# d$ Y) b
- using Whisper.net.Ggml;
7 Y/ M5 W w n2 O# b9 l: s$ x - using Whisper.net.Wave;( l+ @/ H7 y+ a+ }4 d
- , W- [4 d" S3 y& ^; U, k* H
- await Parser.Default.ParseArguments<Options>(args)! J. o2 e8 N4 C$ V4 B& H, O
- .WithParsedAsync(Demo);1 |, ?( z) w" ~4 K: D' b
- 8 H& N; G7 w% A0 t7 R
- async Task Demo(Options opt)$ z! _3 D/ j" j# h
1 V/ B% u! f" w: [% C8 Q& w, s- {' `. c% T% C' Y- s& q+ r
- if (!File.Exists(opt.ModelName))9 g3 W- N2 Z- d2 ~, E9 J
- {
" s6 L* p+ l9 [1 o- C - Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");
/ u/ {% q0 x& E' M' F - using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);- g: m) G4 Y; l; ^ N
- using var fileWriter = File.OpenWrite(opt.ModelName);
" [" s+ U/ p3 G5 U2 B' B9 F - await modelStream.CopyToAsync(fileWriter); ~' F& b; x: { @" L5 }( f9 ]
- }) k/ D& S( \. P5 a+ X. ~
3 Q5 i! @3 _/ j# f; A- switch (opt.Command)6 w* J% L; k% U8 o5 t$ t$ |. C) Y5 U/ X
- {
. o9 r$ r1 k) g% k1 e+ @ - case "lang-detect":0 `: e, B' u1 I1 V/ t# w$ ]8 M
- LanguageIdentification(opt);* m' m5 c; L0 W
- break;
" o3 U X# ]; [" X5 J7 L: J - case "transcribe":
& o2 ^2 r; `0 o0 C; w Z% z- C - case "translate":
. N3 z8 e4 l4 a. d/ l - await FullDetection(opt);, p0 q1 C4 W5 P( t2 t
- break;8 U% `, F+ S ~, x# }2 s
- default:$ a: R5 D& v4 T4 h, S
- Console.WriteLine("Unknown command");
6 a4 r+ \% `; r7 y+ }7 e* r7 ]# H - break;
: ?* W, f6 t) g! E - }* x r2 t/ ~" ~& E
- }
* a" P# f; {6 V {) N/ V+ G: ? - 9 S2 q; ^3 O) J! M! s
- void LanguageIdentification(Options opt)2 f) O, K* M+ o0 k; ~4 G% c: O
- {
# ?8 A. w# d$ A* L - var bufferedModel = File.ReadAllBytes(opt.ModelName);
) _7 G$ K. j; p3 z7 m9 |
, o+ N/ X6 X* a3 ]6 D- // Same factory can be used by multiple task to create processors.- K$ @: k& i6 h
- using var factory = WhisperFactory.FromBuffer(bufferedModel);
" \% B# h, O8 d x2 \
; i. R& X$ l2 X2 ^6 D- /* var builder = factory.CreateBuilder()& K7 F2 F& l4 F0 X$ ^- I
- .WithLanguage(opt.Language);*/
x! T' |5 i8 |9 M9 s - var builder = factory.CreateBuilder()$ _8 E6 ]9 S$ g) E( v* p' Q
- .WithLanguage("english");
4 ~( }, J; \5 g9 K9 B( N - using var processor = builder.Build();
( u7 d' h" {$ f8 W2 D* P) y - 5 q4 j, Z! l1 P) k8 B
- using var fileStream = File.OpenRead(opt.FileName);9 _; [# e; z' a" P$ n/ O
- & V& N, h8 ~8 b6 N0 W
- var wave = new WaveParser(fileStream);
M. I( C& g7 m* O5 y. j0 O - 3 L1 @6 y: t: x
- var samples = wave.GetAvgSamples();% z8 c" _2 r$ ~( w+ K. ]3 k& ~- k
1 d" e8 x2 k1 D% f- var language = processor.DetectLanguage(samples, speedUp: true);
A* i+ | U5 X1 F, i* i% v0 D/ z - Console.WriteLine("Language is " + language);7 X& D$ Y+ M- R, X
- }
3 b1 k" k$ k6 W* ^ - . Z9 b' [/ ^; o4 x- z* X, k
- async Task FullDetection(Options opt)
0 U1 l# S9 ?1 e7 r- y F - {1 P4 j& K/ I9 j; _
- // Same factory can be used by multiple task to create processors.7 N3 N9 L% b) I2 R7 H/ s m* a4 E
- using var factory = WhisperFactory.FromPath(opt.ModelName);
9 Q. ~" y) P/ U; _6 g7 \% F - 2 r/ M, l0 _' ]/ z" J& @# v9 H
- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);
+ Q4 f+ y# b( @# T6 ^. _& X+ } - Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");
: W7 x8 a- n& T. n - string languageOption = Console.ReadLine();) F7 l* l. u) Q' w$ [% A1 E. A
- var builder = factory.CreateBuilder()
/ W, l# p3 k1 k5 p* F) T1 i - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);( b+ w! W: g! K* ]/ F9 o: N
+ f/ I0 r) C( {. J4 V \5 Y) l- if (opt.Command == "translate")2 o6 K5 p- c+ S2 p' x
- {
& W; O7 j0 \) [ - builder.WithTranslate();$ L. o; H2 q8 ?2 Q. [
- }( Q. e' C$ j) ]
) R# {$ v* O3 J. Y$ U& y- WhisperProcessor processor = builder.Build();
! s) o6 H7 l" C6 Z @% } - # v* R' n2 ^/ A2 @- A3 d
- Console.WriteLine("请输入wave源文件目录:");) T" h: ]" P- ^; \* w6 O* }
- string sourceDirectory = Console.ReadLine();- ?* n/ G2 V. M( N+ o. |
- 7 _2 L' \: L9 l; H+ }9 p/ g
- Console.WriteLine("请输入目标文件目录:");( W4 c5 Y: \$ W* z! a! _4 a
- string targetDirectory = Console.ReadLine();
; B! \" o+ t; `7 y - 7 H9 V. J; X5 `' ?$ b
- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))
0 T& [3 d0 x, M& z" ~- H3 H0 y3 w1 a - {
6 }' Q7 |: w4 q q N - Console.WriteLine("目录不存在,请检查输入的目录路径。");6 m0 R7 b1 \' p M) C/ k
- return;
, I* D- d: N l! p# J - }
4 S! _! N+ J$ Z+ H - Z2 a: X3 }0 W' A' q% f
- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);; y9 r: S3 J$ O- ?9 _. \8 F
! o5 h3 X, X8 q- Console.WriteLine("处理完成!");
! p! G" @" i' d/ @, y c - ) `9 D# n) p' j# Z7 V
- }* ~- T s6 l* T2 W' S4 f) T
- static async Task ProcessFilesAsync(WhisperProcessor processor, % M' [+ S# K1 Q2 j- V
- string sourceDirectory, string targetDirectory)% H; _5 S1 i% |6 A
- {
- @% E, f3 c, [ - var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);
7 T7 J- z" C/ T; x2 v9 i4 m, k: \ - * U& `. x3 ` g, u$ V8 a/ k
- foreach (var sourceFilePath in files)
4 Z0 E& L6 i" O( L! [ - {
1 {1 Z5 A5 w9 Y* ? - string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);3 ]% p+ t+ G+ Q8 c
- string destinationFilePath = Path.Combine(targetDirectory, relativePath);# j. ^3 F$ `6 V+ U; L
- destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");, O' y$ i6 ?0 K2 a' g
- 9 _: ]( y- Z4 r5 ?% L% C) r2 \/ B
- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));
6 @+ m' y6 k; E, _6 ~. k
. B# Q& P5 `9 Y+ `- 4 Y1 F2 m& y. ]
- if (!File.Exists(destinationFilePath))6 r) h, {' L1 a' Q
- {
+ L" _8 d# k+ e$ @9 B5 n - Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");
1 j0 m2 M6 g# N+ N1 v - $ o+ W$ a5 y( q; |' H
- using var fileStream = File.OpenRead(sourceFilePath);
& R2 A" C! C* ^% P, c; I8 g9 H - var segmentIndex = 1;
( ?7 l5 }6 i- U: U" Z- d - using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter/ g t, a3 _: a" [" K+ k
- var startTime = DateTime.Now; // 记录开始时间
8 ^. U2 ~8 y- b: H( C3 n4 [
1 E7 L4 U& S! F: n7 T3 r" B4 o2 u- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))# ?: N& _+ {2 J! s! U
- {+ G" j# t- F: g4 F+ S
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");( \( t' x" k9 m. `& C1 X* P0 o6 T7 G
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
3 `# V- A& X. o5 _ - Console.WriteLine(segment.Text);
- V" v& D9 |& @. h, Z' Q7 y - Console.WriteLine();* V* f9 |$ c) Z8 D m
- 2 y3 S+ z& V; f2 {0 V2 h
- // 将srt内容写入文件
5 A1 n/ @- j+ Q2 ^' D - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");5 h, L3 i, ?" z4 `, s
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");1 E1 Z1 U6 P8 t( b6 f0 i
- await writer.WriteLineAsync(segment.Text);' W4 }7 g0 a+ Z
- await writer.WriteLineAsync();
; j h5 F( g, J - / H5 J0 t7 w! h/ `( }" C% M0 i
- writer.Flush(); // 立即保存srt文件) L, f% ?2 |" i- R! C/ d
`1 F, W" E/ n' f! K& F8 M- segmentIndex++;
) h' X( j" L9 F* ~ - }+ M, p+ f2 k# f; H( \
* U0 Z0 }! ^: ~! [% h) [- var endTime = DateTime.Now; // 记录结束时间' F7 H# @! C& e4 a. L; L
- var elapsedMinutes = (endTime - startTime).TotalMinutes;! v$ u, I" ^; t Z/ P
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");
# z+ O q7 b, g2 [+ e5 B; I - Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");
. _1 ?% B+ m& t3 `* h - }
4 Y# W8 l( s8 b - else {9 \0 o7 P+ d3 l: X1 h
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");# w9 E( B5 }; T, P8 V4 T, h
- }
- X o) ?3 l* c4 `* d2 q - }
- t1 l% ~2 i) v/ `& S7 ` N - }
; Q( t& `7 r. M* J# t - public class Options( Q- E8 J9 O) K9 O9 x1 Z# I
- {
. w5 X/ b: H6 n - [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")] n% O) ^* i! u6 j
- public string Command { get; set; }
1 [! _; O% N$ P' X, B/ w7 r3 G - ( Q, a! M2 ]+ q. H" C0 J9 `* W* i6 _
- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]
0 b/ k- F$ S# R - public string FileName { get; set; }% E7 b9 b/ d [' Z- x
- * [1 b5 Z3 z$ |5 B
- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]
6 z) t+ M7 P0 K( V' j8 I8 ~ - public string Language { get; set; }
" ~$ Y0 W3 ]; c2 p% U - v9 N* J! d4 [0 x) s
- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]8 n7 u V1 s2 I* I. b
- public string ModelName { get; set; }/ q; e& O2 D( e9 S& I9 E
- 9 ~2 n4 X Z, F# M5 |$ z O' U. \
- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]
w. R# k* `9 T4 }2 T1 K - public GgmlType ModelType { get; set; }
7 [" O4 E( t: {$ h - }
, W- U. `# x$ Q7 Q3 }+ ?9 P
复制代码 6 L! N! ~2 n' ?, r& I
; o; ?% v3 ?6 ?/ z# q5 R9 X
+ u( U( E, j6 N1 I, x' A- l$ ^& j' J6 c' v) k/ p4 z6 y
|