本帖最后由 shane007 于 2023-9-4 12:57 编辑 Q8 z. C* k5 }% B& W2 }
) Z2 w; k8 E7 z2 Q3 c! b0 c 本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,
& c& f6 C5 o* S# N 使用时需要輸入语言,源文件路径和目标文件路径0 @0 ]# _& N6 v; ^- {6 s
最后是输出srt文件
' O9 y7 ]1 J) _8 |2 w% s% v( X1 s, j) d( d) s: S0 ^0 Z
代码如下( F& E1 h; k9 J
以下这句用多线程可以增速,否则很慢
! H, Y: B- t8 Q# J7 h以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。
! f% s4 Q5 n3 G
. U" m* p! {' l( b0 D
- Q" W0 A+ X" `6 Q2 {- var builder = factory.CreateBuilder()
$ u) V& X+ |+ n3 p' z - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码 , j( c9 P5 ^! }4 U) f# {- Z( h: n2 ~* M
% U7 m$ p/ \$ T
- // Licensed under the MIT license: https://opensource.org/licenses/MIT+ y1 |! W0 z5 t. h
4 \: l2 R' o( Y7 G3 q- using System;
& z8 K3 R* L: y" Q v, h6 H" L" ] - using System.Diagnostics;- J1 n% D3 s7 y. s
- using System.IO;5 `3 I: I+ M- Q" P; H
- using System.Threading;* x$ C1 J$ x: g* A+ D
- using System.Threading.Tasks;/ k7 Y3 C3 Z9 v8 o9 o) |
- using CommandLine;
1 f, R6 Q# P* ]: p, C. Z- R - using Whisper.net;' @( g1 f. [, o2 a$ p* V
- using Whisper.net.Ggml;9 Q& C) f% u5 B, }) O
- using Whisper.net.Wave;
% k( v& N. o. T8 E& M7 X0 |- j \ - Q4 w( E1 h. I2 l
- await Parser.Default.ParseArguments<Options>(args)" {$ Q; f' O+ E, |- s# w
- .WithParsedAsync(Demo);
' J0 M2 W' C6 o( C$ @8 y - : B( U% e. K; W8 ^" ^' y+ ]
- async Task Demo(Options opt)
/ K4 i+ Y6 n3 C/ I - 3 l& Q+ }( ~0 }, T u V6 Z
- {4 {0 t0 U7 [& l# V# @' l$ q6 V
- if (!File.Exists(opt.ModelName)) B( D, ?* Z/ @. b$ w/ @ I$ G1 M2 k
- {
2 w5 N/ S! ^+ X9 }. k" m8 R - Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");
5 B$ \3 ]0 `( z/ P2 h, m7 q - using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);! b) T5 K+ T& a+ E3 F2 d) Y; G4 R
- using var fileWriter = File.OpenWrite(opt.ModelName);
5 B9 {4 X' ?; R9 x5 C - await modelStream.CopyToAsync(fileWriter);
; d: ]0 y n( x2 U, P2 n i - }, E, A0 R. e1 ]
- 1 W. @, `6 L7 ~6 o
- switch (opt.Command)8 } y- k2 l8 j4 r
- {+ x. \ a! B; e
- case "lang-detect":
2 f6 H0 E2 i( J - LanguageIdentification(opt);- {) ]7 w' G5 Q
- break;) l* E5 F9 r1 i
- case "transcribe":: Y) |3 O4 m) A1 k
- case "translate":
2 D3 o7 Z) X- p7 b n+ z - await FullDetection(opt);$ x7 G( S; f. n6 \# `0 n9 p4 T
- break;
4 ~) ~8 c: Q; g - default:
, H- Q+ J/ a' {, {3 E6 D, ~ - Console.WriteLine("Unknown command");
6 k; ~, \ S/ E, J9 L - break;. u0 n# t9 ^! H* \. k
- }' t2 j$ N1 _( [, D( k, Y# K" L
- }' }: |, e& R! z* a
- * O6 `6 c! M' ^4 }6 ]
- void LanguageIdentification(Options opt). \" _/ P+ R* Q, b* I
- {5 l9 O$ N9 H) \2 Z$ C) e2 t+ g
- var bufferedModel = File.ReadAllBytes(opt.ModelName);, B) E- x7 G! m8 P
1 t7 z8 |! c, l w5 D- // Same factory can be used by multiple task to create processors.6 L4 O: c; s" T1 j l1 h
- using var factory = WhisperFactory.FromBuffer(bufferedModel);
+ l/ X/ a( s) Z/ M* F$ W/ O - " f, x1 A" `! s9 [8 F
- /* var builder = factory.CreateBuilder()* W3 Z, Q; ]2 x
- .WithLanguage(opt.Language);*/7 g4 w* F) l4 e( H, l) j* b
- var builder = factory.CreateBuilder()6 k; Q% R6 ~" f r5 @ f* k
- .WithLanguage("english");3 z& x9 h) o7 i; a# w% t. L
- using var processor = builder.Build();- R ` s: b. F. S" m% E
- # ]3 A$ ]/ g2 |* i
- using var fileStream = File.OpenRead(opt.FileName);
8 c9 Z" j; }1 R/ D8 b# j ?
- q6 S4 U: n6 f8 z3 P- var wave = new WaveParser(fileStream);+ }- W# F# g7 S1 r+ a- J
- 2 Z8 `, V' A8 W' ~% z. W: W
- var samples = wave.GetAvgSamples();- Y, r5 i+ g* ~
- ) W; Q+ i# o! d( [1 O$ u* \
- var language = processor.DetectLanguage(samples, speedUp: true);
1 g" Q$ W( ^& M, C/ }: i: @# G - Console.WriteLine("Language is " + language);
& X, L% y0 P- k( k( G; l - }1 `6 p7 V: @. O% Z! |
- - b& z+ v7 z: k8 v4 ^
- async Task FullDetection(Options opt)
4 m& c# p/ M8 g/ ?% k5 d - {! l8 W6 n) f$ X. d }
- // Same factory can be used by multiple task to create processors.# c8 {" I0 G0 C" X% |) F* S0 F- \
- using var factory = WhisperFactory.FromPath(opt.ModelName);
$ o7 e2 _4 C. \: M5 s ?/ j2 t - ) _, p& y- {) n
- // var builder = factory.CreateBuilder().WithLanguage(opt.Language); z2 d3 [2 s" A% N
- Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");
5 x* [/ `5 H5 J! E8 L" Z - string languageOption = Console.ReadLine();3 J0 Y- v! u! u2 X, |3 \
- var builder = factory.CreateBuilder(): o+ ?" t6 L; N9 t
- .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
- M( l" [: R d4 P7 G
! \2 D5 P+ F9 r9 }" L5 S- if (opt.Command == "translate")6 W" ?- x: D- i0 T# f
- {
$ Y ~8 X/ \7 d0 N - builder.WithTranslate();1 r1 w" K% `. y5 P
- }
6 k- r: A2 \/ P2 y - 7 s8 x3 X- C# D6 v: m/ M' r
- WhisperProcessor processor = builder.Build();
# s7 J1 ` J6 p6 L$ m: F, h -
1 A0 s7 |$ z$ N- |1 K - Console.WriteLine("请输入wave源文件目录:");
) E; e# }' W3 G - string sourceDirectory = Console.ReadLine();
- P {; A% A" x. i. Q, f - ' v/ V1 l' J9 g1 P% |
- Console.WriteLine("请输入目标文件目录:");
% K0 p+ D( R: C0 y( h. |! r \ - string targetDirectory = Console.ReadLine();0 N" P" y5 `" L- l. ?* z. ?
- . _5 _8 J0 u% B& [+ @
- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))
& ]) _0 H0 v7 U6 G - {: F9 j. H4 P: U7 D# @ V" ~/ i
- Console.WriteLine("目录不存在,请检查输入的目录路径。");
, o( J$ h& y0 o! S$ D- [) M5 h* w, Q - return;. o! Y) S% N, f9 Y6 Z- |
- }
6 Q8 `! d7 u: P" J m
1 I# A" E0 L7 @0 S) r+ ]- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);7 A" x4 p5 @. v9 a) l, e/ d6 ~
- 7 C5 I! j7 Q+ K1 Z7 \2 Y
- Console.WriteLine("处理完成!");
4 k. s* y& L6 B% z7 U, t - / q) W' S7 S' E! W! `4 ?3 @
- }
) J/ `, \6 C; P# C' u - static async Task ProcessFilesAsync(WhisperProcessor processor, * Y! Z& r! i$ J0 N. R7 R3 J2 O% ~
- string sourceDirectory, string targetDirectory)
( j% g/ S0 U5 R - {2 r( }+ `7 Q! k1 _3 b, h. x' d
- var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);) m2 P' F0 W) ~1 \' n
- : @2 V" E0 y9 O
- foreach (var sourceFilePath in files)! k9 R6 B' h) ?4 D7 f0 {1 k
- {; W' ^6 r7 o) b4 [
- string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);
" x4 \. w) d7 e: M - string destinationFilePath = Path.Combine(targetDirectory, relativePath);
+ A9 ?1 c! S$ b5 F7 i( H - destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");
& q# ]2 d1 u7 g7 ^/ w8 q! X% q - 0 \1 }& t3 u, d& M
- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));" m6 j9 x: ^: O) I ~# n$ r1 V% T
, r* n/ m9 @+ m7 _$ h
9 R6 H; L% I. u; ^( f8 D4 }- if (!File.Exists(destinationFilePath))7 z* r: O. j5 }5 ?0 v1 t2 q
- {) [, q k3 z1 D/ c
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");
& S$ z* ^7 |& |4 i; J. ^ - O: e: z" ?% L' I' J4 [
- using var fileStream = File.OpenRead(sourceFilePath);! `1 C/ _ I, o6 o9 Y$ X8 A
- var segmentIndex = 1;
6 n" f+ s: ~7 o7 L7 c3 f( s - using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter+ O7 e& Q9 x3 G* n) K0 j
- var startTime = DateTime.Now; // 记录开始时间" m. z. N' b8 e# m2 y& D9 {
6 |" v _3 t/ U: i6 r5 _* t- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))
9 P" N2 G0 u3 Y- P - {6 \0 X% i6 g1 y7 R S
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
2 u$ N P9 C! |. X$ {% }6 P - Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
$ [$ p L) a$ `% B - Console.WriteLine(segment.Text);" g/ B# B \- F3 f8 d+ ?/ d, `2 D
- Console.WriteLine();1 Y8 @3 z/ u- L9 f- q$ ~% g; q
( c& h# E$ b9 ]1 @, `+ H8 U- // 将srt内容写入文件 a7 b P9 B' n) E) }
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
" a; F5 ?0 Z" E+ @2 E6 B; A( O" I O - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");4 n$ N7 N& e# K9 j
- await writer.WriteLineAsync(segment.Text);
' m( Y y) R+ r2 J# F - await writer.WriteLineAsync();! U' D& {7 ?+ L. u, b
- 7 f' A8 G8 r/ ^& s) r
- writer.Flush(); // 立即保存srt文件
, z. e" l. G3 T8 t2 R
# E h2 e7 V5 Q- segmentIndex++;, Y1 J' z, {% m
- }1 t6 M" ~* f0 s& D& k& P
' W( @5 _8 @+ j: f/ w% B- var endTime = DateTime.Now; // 记录结束时间
. f) p7 S% i* h: L- M- t+ }# s - var elapsedMinutes = (endTime - startTime).TotalMinutes;
f0 g+ f! H- y8 `3 C+ i2 s - Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");
; m/ Y, }4 c1 ^5 q1 h- o+ Z8 [ - Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");
2 E6 W2 Z) |3 Q6 F - }
% ?7 |! s# o- [+ h5 f - else {
$ n H+ }+ N( {4 g& W8 S9 x - Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");
4 @0 R- e6 E; u2 D - }. o8 f* G+ i1 x& T/ n
- }
3 V0 A. g- Q# J& T" r7 z- h - }( V- i- y+ k/ \; x
- public class Options7 t+ `/ V' E9 ]) u
- {0 @0 H. Q, ]0 E" n* L; d: P: `# P# ^
- [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
- R( j6 D5 E) R/ X/ S- ^7 I' M - public string Command { get; set; }
3 ^+ e* b5 V# S7 A: @9 j
5 b7 G3 t& w1 c- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]
7 Z7 {9 M& c# t: z) J" @$ S3 r# [ - public string FileName { get; set; }
# T2 U# n3 }' \: y - % B% c: I8 Y$ n; r. h0 i: r: s
- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]2 Q) a3 ? y$ U' H6 O( P! i
- public string Language { get; set; }' _( d3 H# b$ R" H$ t+ \. p& g
Y7 F7 x$ `# }6 }" E/ `- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]3 }7 V" E: ^9 q0 T7 ~$ W" A9 I
- public string ModelName { get; set; }
9 {+ n4 q) ]$ \: U - 5 m, O% p, W+ ~0 q x L2 P
- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]$ w' i& @' l% u! w, P4 E
- public GgmlType ModelType { get; set; }
! e0 O. g+ i4 g - }
& d3 j# v, C& e
复制代码 ) f* h) @8 b5 Y5 b, ^- c7 p6 O r
. D+ q u: c5 E6 a) n; t- W
( z9 C. T0 T i5 Q# A+ g
! a" I' F3 U1 J" R0 |+ B |