本帖最后由 shane007 于 2023-9-4 12:57 编辑
/ L6 G1 y( C5 D, R0 G3 R; V- O7 m/ w
本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,% V, ?( K. r" |1 ] ~+ g8 U9 G
使用时需要輸入语言,源文件路径和目标文件路径
: ~6 \( Y' H9 l最后是输出srt文件
3 L6 `; C5 a5 `0 X
& [2 }3 ~3 v! V* ^/ ]$ b代码如下
% S5 b: |- H% I' q7 u- t0 Y以下这句用多线程可以增速,否则很慢4 n [4 B0 r7 m
以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。
6 P& h7 f% j( c$ u* h5 j1 S4 w# O* c, g& m
" q: V" t$ n; D& \
- var builder = factory.CreateBuilder()
B. |# p; ]- S' ~- h8 B; k& M - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码
% p( R* F9 y9 F$ V; p& B: e; W. F S5 z j6 X7 G
- // Licensed under the MIT license: https://opensource.org/licenses/MIT9 ~$ J, n/ f7 W. }# e1 {/ c
- 2 ^2 A+ E0 O% X$ S$ s9 Q* i
- using System;$ O2 g5 s0 t. ?. w8 i
- using System.Diagnostics;
4 w; V2 m O- |. B" |- Q$ _& D9 h- w - using System.IO;
) d( v% J% T3 x8 K. M - using System.Threading;
7 T7 O5 C" a0 T; G - using System.Threading.Tasks;
# B I1 K' e# p - using CommandLine;
4 w# u4 g' |* O/ n1 N7 f - using Whisper.net;
, }7 M( H' d2 D9 n" i+ d - using Whisper.net.Ggml;+ W, E+ K5 q7 x1 X# n
- using Whisper.net.Wave;" I3 j/ `5 K, B h4 @' I* X
- 2 Y, m% K3 w' F4 L8 h
- await Parser.Default.ParseArguments<Options>(args)
( T5 k; V+ I8 u1 _2 W - .WithParsedAsync(Demo);% p% t9 F: g' ]4 ]
! Q& c" V9 M" y- async Task Demo(Options opt)
+ B( u0 X+ [$ `! N% A2 i+ X4 z/ J( B
" e8 }8 r) t& s6 b- {3 w2 d) I/ g6 ?4 J7 m! Y1 R* t
- if (!File.Exists(opt.ModelName))
/ x1 k6 Q9 m( n* q! { - {
1 y+ \5 y( O' p4 _6 R$ M$ y; N - Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");
5 D) _8 [* S3 l; u6 `& P1 Y/ [ - using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);/ H# U2 J7 X& T! U; c% H; _
- using var fileWriter = File.OpenWrite(opt.ModelName);
: `0 O( A; y7 X4 q2 I - await modelStream.CopyToAsync(fileWriter);
! l0 j8 U6 ^' F/ q$ h2 h: _+ H, _. s - }+ R( l- b, ~- x4 P" d
- 4 | o4 T# I1 `* ] s
- switch (opt.Command)$ @4 e& X! m& r4 s% |% ^# |
- {
' P/ A0 Q" s. I4 ~ - case "lang-detect":5 y, M3 I0 j6 G! a, D4 _* E
- LanguageIdentification(opt);
/ R* g+ B7 v c8 i - break;8 u- q- \% V9 o
- case "transcribe":
% ]# `: f T0 D1 w- @7 w! S5 w - case "translate":$ N# Y+ o/ n _$ z" \- w9 R/ u
- await FullDetection(opt);
5 s% p2 V1 ]4 K7 U9 U2 b; _/ W - break;
5 \( G/ x9 i( [6 H - default:9 _" P2 x7 o0 d/ @
- Console.WriteLine("Unknown command");6 C% s8 R" _! {4 H0 S
- break; C/ l4 W! b& H0 _
- }( J8 F* Q) }# Y1 u
- }
+ F6 ]9 L6 N! \. C( j8 e
# A) Z2 Z: B$ Q6 e O* \; |- void LanguageIdentification(Options opt)( @/ q- y/ i; L0 _' F, W
- {
`3 X, {. B2 P# R3 X' O; H* ?9 j - var bufferedModel = File.ReadAllBytes(opt.ModelName);
& {! t( [4 x7 `. A
0 \& Y: d( M" ]: x- // Same factory can be used by multiple task to create processors.
9 e; |4 l1 {6 T E3 N - using var factory = WhisperFactory.FromBuffer(bufferedModel);
) d/ | C h( G8 H8 b$ s - ) g8 K/ D% I3 k/ A& R Q5 n) C
- /* var builder = factory.CreateBuilder()
2 A3 B- d c' W# U. |3 I - .WithLanguage(opt.Language);*/% B+ B6 R$ u8 E; E! l/ ~( q! J
- var builder = factory.CreateBuilder()" o- P7 g- C. n) D# I
- .WithLanguage("english");
( O3 _/ M* J# u2 a/ R* R9 z - using var processor = builder.Build();& D1 R Q* u0 R$ [1 n: v2 J7 \7 ~3 J
Y5 w1 G' C! \8 y: {8 r- using var fileStream = File.OpenRead(opt.FileName);
, P8 F7 f! ^- Q2 T1 O4 o( i
" P: ^: j' g4 {0 n4 ]2 w0 t7 Q- var wave = new WaveParser(fileStream);
% p' N& u$ N4 M, C - 5 o- A. d: q* T2 ~: G7 {
- var samples = wave.GetAvgSamples();1 S, s9 X; i$ S+ z8 ]* K6 q f
- ' L: |8 \8 Y k" S7 L
- var language = processor.DetectLanguage(samples, speedUp: true);- R @: g& }8 L
- Console.WriteLine("Language is " + language);' v0 j3 l+ ~3 G1 _; l* ]$ R
- }
+ `# B+ k6 u! @* B8 C* R
" o3 T% l/ N' z- t) l; L- async Task FullDetection(Options opt); o- q+ j5 @* W6 ?; n! _
- { K8 w- T9 ] k" F h
- // Same factory can be used by multiple task to create processors.7 S7 E" A V3 |0 v+ O/ }9 k9 q
- using var factory = WhisperFactory.FromPath(opt.ModelName);
" B$ _6 m2 w: q - # g7 Q4 h: V% `9 J
- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);% u% w& l: Q$ ?' j1 _
- Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");
4 B, Q" v$ s8 n) t' J - string languageOption = Console.ReadLine();
1 S7 Y3 U% a4 `( K - var builder = factory.CreateBuilder()
# k) p( Z9 A$ V/ B - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
. a9 i% L l1 o1 F) q - ; g1 f' v. X0 o8 o+ h
- if (opt.Command == "translate")! @+ A9 e- ~5 T
- {
: i1 J! y) D5 N% q$ R" D - builder.WithTranslate();! K4 A5 m; o k; G& O& e6 v
- }9 g2 \2 O, P( N9 q+ l7 B
- 9 [7 j* C' c+ h( G
- WhisperProcessor processor = builder.Build();
& h& c) N) K( x; c - ( Q8 a m; N- y" E, k
- Console.WriteLine("请输入wave源文件目录:");
. E2 X( P, e8 o O b - string sourceDirectory = Console.ReadLine();
, g0 N4 P8 X- o6 Y5 j - % ^, B4 i1 A8 \4 d" Y3 V; H. a9 L
- Console.WriteLine("请输入目标文件目录:");
! {) j' t" I2 U: O# D" I* t - string targetDirectory = Console.ReadLine();- [* g( p( f$ B( l7 g J( K7 [
- : S" d7 t1 L0 z- ^2 o ]
- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))6 _( H+ i/ {9 D
- {
$ Z0 S# v6 T0 u+ ?5 c - Console.WriteLine("目录不存在,请检查输入的目录路径。");; Y5 h# s8 `3 X0 q8 T- Z
- return;
/ X2 S+ x% R8 N3 h1 R" F/ b - }' u6 h$ i- A. t+ W
- 0 n- A. {1 w5 `9 `9 F9 y
- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);+ R% T- Z. V' U; S
/ {" |0 M" m$ E& ?/ g) ^- w. Y- Console.WriteLine("处理完成!");
8 z0 x7 m: q* m( F - 4 Q4 l' x6 }8 }2 @
- }0 m. h& F& [$ P% i5 [
- static async Task ProcessFilesAsync(WhisperProcessor processor,
8 y6 }2 B2 M# U9 V% H' w* [ - string sourceDirectory, string targetDirectory)
0 R+ j* Z, K8 q - {( Q* r" v# \8 ~, o; u5 X) O& q2 Y
- var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);: Y% D5 `- ^" {7 @
- ' v2 G0 w, D0 c" Q9 [
- foreach (var sourceFilePath in files)
" z; J5 D+ N: X" U! c1 s! {+ @" g - {
4 X. H! | G5 k: F6 z4 |. V+ g - string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);
4 D* _+ d" D: l' i( G) e, h - string destinationFilePath = Path.Combine(targetDirectory, relativePath);
) d: ^( e) J- W7 t - destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");+ x( a9 @! S B
- 5 y6 g1 D* S) F y1 v# b$ S
- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));
) X4 w- K/ h- I- q
5 Q1 L$ Q6 ~2 J4 v; X- c- A
/ ] E% D' i8 K" ]- if (!File.Exists(destinationFilePath))/ h- W; H" T" |) Y8 o6 g9 y' Q# l; i
- {
3 m# v/ F$ C S& r: `; F0 x5 P - Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");% W) l% l# R" c% h
! s# n5 i; }3 O* n9 ~# e) d& h8 E- using var fileStream = File.OpenRead(sourceFilePath);4 v6 O8 d% F$ X! P) J8 L" O. d: |* S
- var segmentIndex = 1;
% Z( ?( O$ s/ C. C2 w9 L2 K8 d: W+ a0 e - using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter e$ }, \1 G, s4 r
- var startTime = DateTime.Now; // 记录开始时间
+ T |3 b# S( W/ g
: |9 c" ]0 B. ~- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None)), g# c$ _: G/ z, C) i% a
- {& r1 I1 S2 d8 ^( z
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");, h$ W9 z# ?' p$ A2 {/ a2 h
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");: B4 W& `3 p6 h7 X
- Console.WriteLine(segment.Text);8 u/ N% J5 r! b* \. C
- Console.WriteLine();
& |: H t/ q1 H2 h - 4 I6 G( a) E/ @ q) o" V9 ~
- // 将srt内容写入文件! k( d8 q* M' @: C Z
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");6 N$ U) |8 M+ {; W4 q" w8 J& ^
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");+ A V; v, e: _6 ?
- await writer.WriteLineAsync(segment.Text);
, E& J8 ?, X' |' v9 | - await writer.WriteLineAsync();; T& Z# j) G* Q$ f
- - t& F6 q3 c% c1 \" h+ p* Q5 I
- writer.Flush(); // 立即保存srt文件
. @/ c. V) {- w Z2 H) y - 9 m; [2 H9 v$ l g9 j+ j
- segmentIndex++;
- b; U! O) Y" X8 |7 B - }
. b$ j" ], @" g% {/ x+ Z6 L - * ]4 [' p( {* j5 `* a7 M% x
- var endTime = DateTime.Now; // 记录结束时间3 c& v7 }* K/ Y# r2 |, s
- var elapsedMinutes = (endTime - startTime).TotalMinutes;
\) o5 b1 a; O6 ~" t) p" B- O& p - Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");
7 C% w2 x; M `$ b0 g2 | - Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");
& g4 m' ~6 P0 N6 V4 r& E - }4 F6 k6 _; g3 W u
- else {6 k: j' |6 ~. k) F' ?6 u; {
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");
, I- ~. [ l4 }8 o- p6 P - }7 c. C, e D& i6 ?" H
- }8 {1 k/ Q: T& H+ t5 y# H7 m# p
- }! r+ L: Q/ b! K* n3 U8 @# A
- public class Options* Y; U9 \" N. J/ K$ Y: Z) r
- {
4 V+ x9 R* P4 T+ v - [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
: @! y* H/ o0 G - public string Command { get; set; } }7 ^0 }" j; k5 o% g( m/ k% D
- 4 d/ E: y9 H k5 P! f
- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]4 d! F! p9 _, R2 q n+ X
- public string FileName { get; set; }3 d4 ~# S! S0 |" ~+ _* B& K0 ?
- J- S3 l0 O6 f# b: ^- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]
* V1 G6 V9 }9 T) n% X - public string Language { get; set; }
- b5 `' H7 g+ J, Y: U
9 P: J4 {/ U3 q0 q5 L- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]
3 j, M) y6 u: p/ ^ - public string ModelName { get; set; }$ `9 p3 _, Q( W o1 N& d( m
- # D" Q( _% S- A, G- Z! f
- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]8 h% x2 G% r4 v1 v( O
- public GgmlType ModelType { get; set; }
7 g8 w( W7 P# R6 I' b/ F/ ~ - }
& P3 J7 \9 H! S, i' V, I
复制代码 8 [/ h! f7 I5 t8 ^& d0 }
$ O" X# X1 W9 @' L1 G$ E1 `. f2 y
! O+ ~0 F# L+ G( r2 t3 ~# _- N' E4 r$ D4 m2 P# m6 ~0 f9 J) }$ V
|