本帖最后由 shane007 于 2023-9-4 12:57 编辑
: Q% H/ D( ]+ X! R0 [3 L! y) |/ i2 f4 l
本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,! a, d" ^/ x' b5 U, p( j# O
使用时需要輸入语言,源文件路径和目标文件路径
: ~1 U) Y2 W) \' Y0 W' p最后是输出srt文件
4 _1 w3 ^2 }# [
" J. N5 X6 \* b, @ \1 T7 e& H6 c代码如下) w3 d* @5 B9 k* t
以下这句用多线程可以增速,否则很慢8 d+ g+ g6 h$ T z$ ~3 U8 }- g
以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。! P" Q( m' {, R& T* N* }7 h! @
/ K& p+ G) s: q2 C
& [& h* r3 C8 e% B7 D* a
- var builder = factory.CreateBuilder()
$ y+ F" n: p' m% C& C% u7 k0 v - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码 1 w0 I o. A7 {! d+ Y3 p
- _9 I. Z7 }: h8 b% f! p4 Q
- // Licensed under the MIT license: https://opensource.org/licenses/MIT9 K, t0 I6 x c2 {
- 8 e- F @5 j; a' y! ]5 O) W) t4 s2 \
- using System;
) B7 ]2 ]) w& }% o - using System.Diagnostics;
$ Z. ^* D, t! m! X' L$ b - using System.IO;
- k; S' ~, i0 v' U; b2 \ - using System.Threading;
: q& I; B9 Y! r" Y; l. l% B* h; q - using System.Threading.Tasks;
$ X$ k# L8 j& x0 R2 J - using CommandLine;
5 o7 E; T" e) e: p! b" S) o% k - using Whisper.net;2 H: w/ I4 {( C" C& }
- using Whisper.net.Ggml;4 A5 W% n I4 P1 m% a
- using Whisper.net.Wave;* \3 D1 ^' {/ k- j N* b& F a
! j9 |% _( f: }8 e) [3 b3 T- await Parser.Default.ParseArguments<Options>(args)
* z" K. S% o V% l8 V - .WithParsedAsync(Demo);" b7 W2 ? f% n
- / V0 u8 Y# N3 m7 S3 o4 d& ~- I
- async Task Demo(Options opt)% t* J+ G; w& C( M! p1 }. c
- & B9 e4 [5 g; V, U! U5 U
- {
0 ]6 B, X* s& Q$ Z6 @ - if (!File.Exists(opt.ModelName))" x0 u, ~4 S7 z, K
- {- r2 d5 P2 N O
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");/ ?- V2 ?) m% u8 h" B- b
- using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);9 p" b2 @, u8 \1 F+ U
- using var fileWriter = File.OpenWrite(opt.ModelName);
" {% P; {2 X4 t; L& O - await modelStream.CopyToAsync(fileWriter);. b7 I1 c3 Q; i& o: V
- }; @) R8 N9 K" g
- 9 e, {3 `, e q' `1 O; W) h6 r2 @
- switch (opt.Command)( P$ F. b, ]0 L6 {; g& a
- {
0 e& g0 f9 G+ b0 Z4 |& w - case "lang-detect":
0 F$ `1 a# n( }4 S6 \; o* B9 [ - LanguageIdentification(opt);' z" K" [% ]9 ]$ L6 k$ x- T
- break;7 U1 o' ^3 o- @7 n9 p
- case "transcribe":
/ s8 b( K4 a- d$ x - case "translate":; h0 B/ t% g4 n" e7 }, `& O
- await FullDetection(opt);
5 M5 W+ @. r( }+ e6 q - break;% q; }' H* s! l. Z* N2 t
- default:
5 b" o0 S/ m; A( ~: n7 U$ r6 x4 }/ C- s - Console.WriteLine("Unknown command");
6 [$ s% A+ C2 A! M1 F - break;
+ G; ?( j' A! x( o - }: i* }% u5 W2 |4 d8 l
- }. B" q* I" R/ Z B5 N
! J8 @1 ?' [/ H' }( a }4 i- void LanguageIdentification(Options opt)2 D' p2 P0 W1 @+ }4 c* {
- {# r0 n. c- W" h3 q% m1 [
- var bufferedModel = File.ReadAllBytes(opt.ModelName);' ], s7 p% W! V& E7 B
$ s& R: }4 M! U+ n0 Y7 V/ S$ }- // Same factory can be used by multiple task to create processors.
5 n* C1 i( B9 |6 \; c* Y - using var factory = WhisperFactory.FromBuffer(bufferedModel);" i" P" ^1 j, y* E
: s/ r: n" c$ L4 Y! g6 O9 O- /* var builder = factory.CreateBuilder(). h7 i+ i: t8 D3 n' @0 }+ n
- .WithLanguage(opt.Language);*/, Z3 K6 }1 K! \* r3 T$ |! E/ _
- var builder = factory.CreateBuilder()' \. Z5 J) V$ ^! }
- .WithLanguage("english");
" k* y+ ?# e4 @: e: c5 ] - using var processor = builder.Build();' R$ y' ^2 t, A6 C$ ~3 i( D
7 Q# K" X/ H3 @1 U. u8 B" c( f- using var fileStream = File.OpenRead(opt.FileName);
' Q, f7 h8 V* g0 U3 e; d* b6 @ - 7 }' Y3 F# f0 h6 _7 @0 [
- var wave = new WaveParser(fileStream);. E4 Y: T- ~' {+ y5 o( t4 \; G
- 4 O" ~" I, x' Q- t) W* v
- var samples = wave.GetAvgSamples();
- [! j/ V; q$ ^5 a; y
1 g2 G9 K' W/ |7 q7 i) X& [- var language = processor.DetectLanguage(samples, speedUp: true);
2 X( }( e# p6 d - Console.WriteLine("Language is " + language);
5 i9 f, a4 I$ |& L' }; Z9 d - }4 g/ i, I0 o% l1 C
; ?3 T2 e2 q0 @! i }: l3 g/ F8 u- async Task FullDetection(Options opt)
! f; w: B+ S% ?5 S3 O. G5 A - {% M1 N$ o0 ~' K( r. y
- // Same factory can be used by multiple task to create processors.& B& i" D8 _! Y+ C; u, p
- using var factory = WhisperFactory.FromPath(opt.ModelName);
: k- R8 M x" G3 I6 J
! j4 e0 x7 i. r, ], C" ]- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);1 R+ u6 b( ~% L/ p7 n* s
- Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");6 P1 L; D/ u. g' N* _: Z
- string languageOption = Console.ReadLine();
6 b9 K" E) B9 ~7 Z - var builder = factory.CreateBuilder()
# {& R: {3 L7 Q) @7 Y; R! H - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
; n: K) m0 a/ b0 f5 ~! y( h - 7 ^# ~# i7 S5 P/ p
- if (opt.Command == "translate")# l* l4 `4 f) Q# x6 u' P
- {
0 {- t9 n6 l1 p2 A; c$ l: S0 c - builder.WithTranslate();
* J8 o$ J9 s7 Z, S - }
" [1 W( `6 P% N5 I9 D. [
" ]+ b" i4 i, o% x6 ]) {& F- WhisperProcessor processor = builder.Build();
# A9 u, C c& F/ ? - * S0 C3 |- D! O# }8 W( p
- Console.WriteLine("请输入wave源文件目录:");
# F! a2 e' D( c2 b# C% i: r; J0 r - string sourceDirectory = Console.ReadLine();, B/ s% H0 N+ a/ e
- - E- z! e" c0 l7 D' l# `/ ]2 i
- Console.WriteLine("请输入目标文件目录:");8 ~9 e4 c$ F+ D: l/ ]' Q* X+ d
- string targetDirectory = Console.ReadLine();. R8 P; L0 h. x
- |4 ?- w8 Y' r4 i4 i- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))& j9 I% _' ~/ X# D' O
- {) [* M! h5 {2 F2 r4 A% v7 Q
- Console.WriteLine("目录不存在,请检查输入的目录路径。"); \. ]6 @5 J. S) T5 c; c
- return;9 O/ [& O' b6 {. P9 V' I
- }
2 x' X: t+ `* i6 p# O; k# a; P
- B9 x2 B/ G3 z0 _6 Z4 W- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);
# M1 G/ L' @3 H# E$ f
: w& Y+ U7 e& x" @( y- Console.WriteLine("处理完成!");+ N# m, Y- L/ B
- U: {3 f+ Z7 j- }1 k: ~6 m* Y; Z
- static async Task ProcessFilesAsync(WhisperProcessor processor, , E0 I1 Y; @; \! Z- j: X* @6 ?8 ?
- string sourceDirectory, string targetDirectory)
# V) ~9 c3 ~' x5 I) ~ - {
: \* c! b, d2 \" A$ F, I5 g - var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);- R5 u" P. l5 J8 c% ]$ n
- 1 y/ q8 t: x4 Z# p
- foreach (var sourceFilePath in files)
1 ?) M. p j7 |1 F6 }# C2 ^8 E - {
% k) K9 f) r, l$ x% Z$ k - string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);
9 Y! W0 U+ i" N: A+ M - string destinationFilePath = Path.Combine(targetDirectory, relativePath);
- d+ q$ r5 V7 ` - destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");7 k) D, ]$ g/ G4 D
- 5 `* M3 Q. r, K
- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));) X+ P) A8 g" Z% J
1 q2 o: j6 Y. f' z: l- - I' |! c0 I5 d1 |4 f
- if (!File.Exists(destinationFilePath))( f# ?0 L1 P( o0 S
- {
6 }( H4 c3 D; c, j# Q1 G( Y - Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");
5 q: b2 l' G# _; T - 6 E+ x# I6 C; i4 Q* H
- using var fileStream = File.OpenRead(sourceFilePath);* `( e6 e$ c8 W5 V) c3 l Y8 J. O: k
- var segmentIndex = 1;
- y0 x# F9 \. b$ {5 h! y- c+ M. W - using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter" E* ]: ~) o! B! X" t; U
- var startTime = DateTime.Now; // 记录开始时间
5 c, M- t8 B1 S' O- n" }" W - 4 f0 ^. U# I6 w4 q, R" O
- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))
2 }' Y: U6 z4 u/ h0 ? - {, d8 B4 z7 t) _3 r% v' U
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
) U/ a# Q1 f v) I9 g9 z - Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");/ B3 ?0 a" F; s# o, S* v. f# z6 M4 I. ` ^
- Console.WriteLine(segment.Text);
: ]2 _0 Y1 t; }) o4 F( b8 [ - Console.WriteLine(); G" g, W( u) C r% n2 L7 t% S& f4 \+ A
4 y/ c1 d9 a; ?/ _+ y( d8 B" ` y- // 将srt内容写入文件) J2 E8 W# R" R5 D( `) O: f" P7 I2 s; O" @
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");4 ~+ u0 M7 F. `! k! G/ K
- await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
1 {) _3 b9 [. x# D$ [6 [% g: x1 c - await writer.WriteLineAsync(segment.Text);
' I+ J, p G ~2 Q8 I" O& \. | - await writer.WriteLineAsync();
: q, w/ t% N3 n! I8 O/ ] - + @4 g% ~( T; y+ }4 D. I: J" ]
- writer.Flush(); // 立即保存srt文件
4 c9 r J: N# a3 C - 7 {5 @, v5 M; `9 \
- segmentIndex++;' v( V7 \: }; ~- p0 o: z$ W. I
- }. h+ I; \1 I* b6 e
- + ^7 s+ ^! @0 G" O' v3 N: ?
- var endTime = DateTime.Now; // 记录结束时间
1 z7 J( m# K9 q, _# N - var elapsedMinutes = (endTime - startTime).TotalMinutes;5 }$ v; a f9 w: c4 L9 G1 W2 ^3 X
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");+ g; A6 W& `4 `2 h7 O
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");- p9 l4 M/ v' H6 w% x: M# l ~
- }2 K. \4 d8 y* N% E& [
- else { {0 N( A; h7 w- d2 m
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");2 L% ]6 s, T# x4 R4 C
- }
; M: l1 W# |( r+ L/ @ - }+ \$ K0 l' d7 _/ u
- }# j0 D1 \' K2 n4 B |' T4 a, `
- public class Options; r7 Z4 y. U6 M7 r$ x0 `
- {8 ]% D+ ]0 ~: }0 _
- [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]3 ?5 J; h% D7 |2 v2 x# {: S
- public string Command { get; set; }. b2 G* Y- R3 u3 K1 H
( _+ a' O) N7 }+ r5 |- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]! o. v. |8 J% h, X+ W& F
- public string FileName { get; set; }7 b4 t7 j& ?- M
- 7 N" I) Q; Z" x" h. B. k! Z
- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]! j0 y: O5 E4 b! G
- public string Language { get; set; }
4 G) [+ S4 f5 ^5 G; y" d; S% k3 |. _
# b; A; s& M$ n1 ]) e8 b- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]
# v3 G; g( y) c L - public string ModelName { get; set; }. t5 S3 M! w6 M9 x
- ; Q( \9 U8 A! A2 N, g
- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)] [0 g$ S! ?( s' m) q! w
- public GgmlType ModelType { get; set; }6 }# f* g z! e) @' e2 X! h
- }- `* _; [8 n% c5 H" m- k4 }9 w
复制代码 R1 v Q* P) o; r2 t0 L) o t
" q& ?& c9 x. l4 g0 _6 ~ z) j
2 n& j& m4 P: y
8 x1 H7 s; @& i& j/ {$ P- _% _ |