本帖最后由 shane007 于 2023-9-4 12:57 编辑
8 K' P. \) k, u9 |& P8 ?
5 F8 h) J* b/ |1 o! J" e 本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,
; y( a6 u$ @ n5 b$ [( B$ v 使用时需要輸入语言,源文件路径和目标文件路径/ d) a0 N T h7 z) p0 v
最后是输出srt文件( K' [7 o6 M' e2 M! `
, v$ k R) v- _- J代码如下, u) T% Y6 T o4 s
以下这句用多线程可以增速,否则很慢
2 |1 S4 I$ @0 `0 C以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。7 R$ b6 L0 g J. t+ `
% V2 ^: `, g; I. q6 n+ P
$ n/ x! k" Q3 l% V* P! q- }- var builder = factory.CreateBuilder()
/ \$ I% s% R0 B& i3 q+ ?8 Z - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码 * @* \0 f2 V- K* X' p2 A
! c1 J5 o. ~6 N7 `1 j
- // Licensed under the MIT license: https://opensource.org/licenses/MIT
1 j2 g$ C6 E# P+ `2 r1 G
4 l0 U1 K+ d7 M0 n( I. P* N+ k+ r- using System;3 [: w* Q( [% f" z
- using System.Diagnostics;( k$ f5 c( s9 H$ l7 V- e v& a8 n
- using System.IO;
9 _7 J6 c L+ w7 q - using System.Threading;- U. K% M; n' G
- using System.Threading.Tasks;
( g6 }5 d0 e& K - using CommandLine;
) e% ]9 }/ F5 x - using Whisper.net;0 @; w6 |- h/ x* l" h! D% r
- using Whisper.net.Ggml;' c+ q- v V, m$ @5 D9 @
- using Whisper.net.Wave;
9 R: {4 u. O) b) ?% V0 o# v
) _. T$ ]% N! E- a6 m$ k o- await Parser.Default.ParseArguments<Options>(args)& s# I+ k; _% M, d
- .WithParsedAsync(Demo);
/ T2 |* q: |: c6 N0 ~6 b" Y$ W ^& a
# L4 e+ f/ ]% t/ m2 ~' @( j: |( {- async Task Demo(Options opt)
' H# b. g) ^$ Y, F8 Y - 7 z) L$ ~, A- f/ ]+ M- Q
- {% C% y* F+ d, K# K4 h8 W
- if (!File.Exists(opt.ModelName))# k* \2 B* e: e6 f; |% e1 j' U
- {8 d4 ~2 P4 c% x9 ^4 f' B
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");" ]) c& ~3 M8 n1 B) e" ^# P
- using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);
# W3 i" |2 O$ h - using var fileWriter = File.OpenWrite(opt.ModelName);
9 ^( Y6 m* y2 Z& Z" i7 B" [ - await modelStream.CopyToAsync(fileWriter);
a4 ]( z2 N3 I1 v - }
( @) W! S2 g- I# e+ F! ~, C$ C5 W! s h - . t1 E; q' A5 s- W! {
- switch (opt.Command); X+ \. b; K& L1 Z5 u% w
- {
8 [% u/ M: W5 D( D a% o# s2 Y' W - case "lang-detect":
6 w: d% O/ r3 @% W$ C$ \5 j - LanguageIdentification(opt);$ c6 {1 E# A8 J: L: ] B, v
- break;
I# m; M1 j5 I# k - case "transcribe":
2 }2 \2 }5 G1 X6 k* Q - case "translate":
: C6 o$ ~" P: ]2 P - await FullDetection(opt);
$ M/ A9 F, T- G, r - break;7 [' t2 R) u# q" i
- default:2 `) J4 x1 c. ~* m" Q
- Console.WriteLine("Unknown command");
$ q! R; z2 w2 C/ v/ ~; t% n - break;4 K$ }1 t& M2 Q
- }
) M2 t7 x4 O8 M2 J- w z - }8 u& v2 Q" H) ?# X
- 6 H* a2 h: X+ Y* c$ F5 F$ |0 q. _* J
- void LanguageIdentification(Options opt)+ D2 s9 K; ?+ P9 ^
- {
6 i' V& r) _0 E9 \& U - var bufferedModel = File.ReadAllBytes(opt.ModelName);
" X! I( ?! E9 W& U( n6 V
C: c% l" h1 A( o: H* j- // Same factory can be used by multiple task to create processors.
6 }$ A1 e4 Q$ e8 F0 U; T - using var factory = WhisperFactory.FromBuffer(bufferedModel);
' Q+ Z* Q# d, A$ _! M; J; E& ~
/ Z, g7 J, d$ o0 u- /* var builder = factory.CreateBuilder()
- L* u/ ^- `9 H' R( g) P$ t" l - .WithLanguage(opt.Language);*// T- n) o: f6 [+ S1 F7 Z9 S; f
- var builder = factory.CreateBuilder()
/ y4 o+ n; r4 H9 T- U - .WithLanguage("english");# D1 e& U8 @9 u1 A
- using var processor = builder.Build();
! s0 e( p S9 g- T - % J9 I0 U6 C! b8 T
- using var fileStream = File.OpenRead(opt.FileName);
1 w6 U" q/ k8 m1 f$ V
8 P; Z i9 R2 g) a Z- var wave = new WaveParser(fileStream);
! y, n* b( p5 ?0 x9 @6 O! A1 ]
3 H" d& p V+ W j& n- var samples = wave.GetAvgSamples();% m* E, x/ K: d8 }# }- g
2 N) U, s: l* Y7 o- b- var language = processor.DetectLanguage(samples, speedUp: true);
, o* v4 \) S& F7 |. v; F6 q- ^ - Console.WriteLine("Language is " + language);& ?' F# x& ^2 H( c% e; q
- }
* Y2 ~' s! \4 g7 v
! O0 N" \- P0 S- async Task FullDetection(Options opt)$ Y' v9 _3 X% `/ z9 I5 E
- {3 t5 O u" V# n- I
- // Same factory can be used by multiple task to create processors.
* y8 U8 @' s& i' E' C6 a - using var factory = WhisperFactory.FromPath(opt.ModelName);
# S9 W/ z* B- ?) a
, P' ^' ~) A, C% Y6 Y% ~8 O. u$ k- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);0 y8 H4 Z4 x3 |: x) B5 |/ i# ^9 a
- Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");% h- m6 ?! Y6 b
- string languageOption = Console.ReadLine();
. X- d3 |0 G+ r3 ^- ` - var builder = factory.CreateBuilder()
& D5 N/ g( V5 X: b7 [% L, | - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);$ T' E; m! c4 {3 e
- / l+ v9 Z+ I% }) O3 b
- if (opt.Command == "translate")
6 Y% t' ^& ^' [! y6 g! _ - {
9 N' M% I+ U' x4 t% t - builder.WithTranslate();
1 b. I8 A: N j/ T8 ~ - }" B; O5 N. t7 @6 V8 ~6 V- Z8 e
- ! Q! t6 M1 d, {% z5 q" u; ]
- WhisperProcessor processor = builder.Build();
2 K3 p$ e J4 A! ^ - " z: c8 ]* C8 o' L _# R: `/ ]
- Console.WriteLine("请输入wave源文件目录:");( N% g. P4 {; \) Z5 S/ K; m7 f
- string sourceDirectory = Console.ReadLine();
( J) x& Q5 M, {- W - % n( i; y6 b# q' s) a% r
- Console.WriteLine("请输入目标文件目录:");
) z$ b) F, a( [* H7 g1 N. m" t. J - string targetDirectory = Console.ReadLine();7 y2 s7 d, B- j& U! ^
- ( n& A4 K: X' K: a8 W3 U
- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))
$ V7 p! v$ |/ D# r+ D/ v7 c - {! T( x! A7 `/ L7 `" K2 u1 s
- Console.WriteLine("目录不存在,请检查输入的目录路径。");5 N' k0 W2 N2 W5 Z2 O
- return;6 v/ J! q5 w' F e7 r3 a
- }! g" C6 @' c( R5 [) B7 p: q# q+ q! l1 _
2 g3 g7 N |0 m" A& a- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);
/ ^6 ^ ?3 {- X/ I4 i& O - + m9 `5 F0 u2 d
- Console.WriteLine("处理完成!");
; C8 _ L" u" y; v; ]6 Y - 6 R( ?2 a* ~+ l
- }2 Q6 @+ A( }0 d1 d/ ~1 z3 m2 A
- static async Task ProcessFilesAsync(WhisperProcessor processor, . Z' u3 q% O" m+ z& M2 e& N% c
- string sourceDirectory, string targetDirectory)! r* f$ }& ?: l* i+ p$ F* C" m
- {: c" E) U) r+ G
- var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);
6 S! {# R( {/ U
, y8 m7 C: H0 J. g2 |- foreach (var sourceFilePath in files) n3 J6 N4 p5 Q- t! C, m4 s+ [& _) u
- {
; |& o5 d: Y2 M - string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath); D: Y6 x0 G# x( z
- string destinationFilePath = Path.Combine(targetDirectory, relativePath);
* F/ ^ a3 f) c% _2 y, y - destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");
- d1 G: p, v& S" l+ G n5 P7 R - Q- ~# Z( v8 `' `( w' ~) M3 R3 M4 O
- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));
1 x! e0 q* E$ A! [
, B( ^5 G# s* R/ d- q! C: G3 Y- ) c) f/ X/ {6 H+ U" r; `
- if (!File.Exists(destinationFilePath))" U- J5 [" M, U& z2 d- H
- {
. K- B% k1 X5 M" I/ w e# j3 h - Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");
" t: n. w# N1 C! H - " N) L9 i W( f1 f, ^9 |9 W2 S
- using var fileStream = File.OpenRead(sourceFilePath);
' }8 Y8 q8 w) I( N2 p" d9 ? - var segmentIndex = 1;5 q/ L, f/ A6 g- H: B7 e& \
- using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter; i, n! D3 I* `5 N" t- \& y3 a
- var startTime = DateTime.Now; // 记录开始时间
. C- A0 _( R p' a5 t/ V - 0 z$ K- \; n/ Z% X) R# S
- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))
: d6 \ b- u M0 u/ ?& G+ t0 M - {
- j9 L" s- x* v, V0 \ h1 w - Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
6 m# A( j3 ^( ^' @' c& a - Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
) V$ o% E( r- i5 d! w8 V - Console.WriteLine(segment.Text);0 g& z1 \4 I; H6 @1 `
- Console.WriteLine();
0 M0 L7 g) O8 U
1 y* ]- ^, s: _7 D# [- // 将srt内容写入文件
) y1 P* Q& D+ R1 U - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
7 f; @- r& W1 d' ]8 W+ q - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
& a0 k8 _; l6 V9 N. k, f- o6 g - await writer.WriteLineAsync(segment.Text);* P, H6 M5 x. F1 C! u
- await writer.WriteLineAsync();% C4 {: O0 g8 f& n; _" H( E/ U
' ~ j ` P' o5 d( i- writer.Flush(); // 立即保存srt文件
( W% \8 Y1 ]3 J. G' L7 @% M
) Q2 b; q; X- B( }9 K, q& O8 V6 S- segmentIndex++;
4 l0 X" |# j$ G( ?# A3 ^ - }) I% U, Y" i+ K# E# Z9 z$ u
; ^! e5 X5 C( M0 v1 r' S- P- var endTime = DateTime.Now; // 记录结束时间' Q, p- e* Y' V X* B
- var elapsedMinutes = (endTime - startTime).TotalMinutes;
* a9 S" G5 z, t" F. S0 H9 T5 c - Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");
- w% c, `- _9 ^. r4 ? - Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");
% K, I# f9 I3 p - }& y7 X6 f- C9 C! d3 L+ L/ ?
- else {8 W& p6 q ^5 m2 X7 Z4 {
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");
! u9 i, |, X# h) q9 O r, U - }
- H& E# T/ [6 T7 c8 Q/ _" ^; Z- C - }" M% Y7 F. l' U: z
- }, a1 ], {4 R: j3 D) A
- public class Options
9 X+ }* V: Y, v/ @2 e/ c* \ - {9 G. }( G) D" p/ j: u: W
- [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]. L& W% M, f% @- {6 b
- public string Command { get; set; }7 K# v2 W+ ]+ L0 X* Z% p$ H, W
- $ Y0 j0 x. d. M _
- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]
$ r, E: B: t B& f, x1 w - public string FileName { get; set; }
* u% t* ~, f8 c9 N, d2 B
0 s+ n4 B) I) O7 S' p2 D- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]* p7 r9 ]# K9 d2 z, r+ B
- public string Language { get; set; }, e0 O2 X X. d. R& k
2 o# s+ _ V' q- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]; r$ L. o. \) D1 z' X1 \( S8 L4 u# m
- public string ModelName { get; set; }6 v. l% k) q6 b5 g& f
- ! _* _% d1 ]7 B& O
- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]
. O! C% e4 C. H! @* J5 H8 }/ O. D - public GgmlType ModelType { get; set; }1 ~# Z v+ |, \! c) n a
- }
, {$ `6 ]: s- b3 o5 F1 i
复制代码 , R& W [1 u0 _) D% o E
n4 t9 J& e) x' Q1 M+ ~9 T
# S0 U3 N* T1 m
# m6 b/ u y( Q: `, J9 m9 f& M$ v
|