本帖最后由 shane007 于 2023-9-4 12:57 编辑 0 |5 n0 X% _2 V' P' {
# _5 O! o$ J( ]' q" P; R+ P
本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,
9 K- z5 f" a' I, [( t; t' M* K 使用时需要輸入语言,源文件路径和目标文件路径, b. F4 N& [+ Q- _* [
最后是输出srt文件
+ a1 x- z8 h, Y& A ]* q7 a4 \2 G' l$ R) |% e
代码如下
8 y/ m; |) _3 h" d以下这句用多线程可以增速,否则很慢" g4 k. p$ X4 B# J* G. C6 D6 g
以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。
1 S. s4 ?* J. _! V$ w9 E6 V* p* e# H5 p& G2 O6 B( w1 ~% r
6 `. N$ R5 P+ H/ s- ?: `& w$ w
- var builder = factory.CreateBuilder()
9 s8 B3 B& d( ~- d/ b- U6 n) Q- M - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码
7 P) z# Z) z. c5 Q2 h q+ R) Y7 [; e9 O" v
- // Licensed under the MIT license: https://opensource.org/licenses/MIT }$ s0 ?" M5 O6 H
- $ @: P' U) y/ W0 l* M s+ U
- using System;
8 _9 }, ?# d- Y( ~1 z - using System.Diagnostics;+ |1 i/ _0 [4 m9 p6 m) |; C
- using System.IO;" e$ p. U/ c7 [7 {. e2 C9 n
- using System.Threading;/ g4 N% j! N: h+ C
- using System.Threading.Tasks;
# l1 V1 E8 |9 `3 Y' [, } - using CommandLine;0 R9 e. j `8 c( |
- using Whisper.net;. V2 i- X7 ^8 u
- using Whisper.net.Ggml;1 ~; A4 P( ]3 V4 a2 K- g
- using Whisper.net.Wave;
" R9 ]8 k$ M% C' t" z - * h7 b% k1 R5 W( p+ E4 c+ @
- await Parser.Default.ParseArguments<Options>(args)
- V+ R$ q% R- z* _5 a - .WithParsedAsync(Demo);
" z3 ?7 h5 q s5 d, P
& c! Q' @) F1 p9 ]- async Task Demo(Options opt). C8 g9 F( G. U' |: q4 z3 l
- % I7 L% } F. H; h- R& W2 z0 D! ~
- {
% u* K1 F5 a) O/ s+ |( { - if (!File.Exists(opt.ModelName)): m% k8 c2 o1 E
- {
" h: h. m3 H# F J4 y4 d6 [: W - Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");
% c" U4 a' t7 N0 d: r - using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);
' d$ m0 J! c6 u i% a - using var fileWriter = File.OpenWrite(opt.ModelName);2 f: `! E9 _# H( ]4 \" [
- await modelStream.CopyToAsync(fileWriter);& N4 Z7 S( V. L1 ~5 V4 F2 o' K0 `
- }
! @" R2 \7 |; l. i: b; J6 C - % t4 ?* U3 f! l" P* R$ E
- switch (opt.Command)
/ \1 [. a7 g8 F y$ ` - {
; S# g: U: K; w3 h' P - case "lang-detect":
( O/ ~! t1 s% W9 U& A - LanguageIdentification(opt);0 ^6 Q1 c: I. j L, H) u( K
- break;% P/ R$ n X) Q2 p2 Q7 ?
- case "transcribe":2 a) A, _- a" P, L& N j; A7 [9 @
- case "translate":
4 A: e+ z8 [/ S8 ~3 y4 c; E1 v - await FullDetection(opt);6 G% S: W0 D" P& L- H& [" x. t7 ]
- break;
3 Z1 r# a: w% V" G& W6 } - default:
+ i( P6 m& s9 V$ @: M7 A5 B7 d% v - Console.WriteLine("Unknown command");
* ]5 G% x7 O0 e: t& C- f - break;& t) f0 I/ s% N% X5 m/ N$ o
- }7 c# e' ?& H L
- }
- b4 V4 f- Z& ~/ |# V - 7 P* [7 J* @, a, g
- void LanguageIdentification(Options opt)% J& y" ^- L- |( ^# N q
- {
: K8 S8 h7 t# C: r" D - var bufferedModel = File.ReadAllBytes(opt.ModelName);4 h/ l7 @ m; H9 _5 C0 X
- # L1 O$ Q3 F- t. a7 Y1 `/ Z6 \
- // Same factory can be used by multiple task to create processors.
: P( Y: c( W* B% o. H" Q& S* ^ - using var factory = WhisperFactory.FromBuffer(bufferedModel);0 d1 p" H% z# F/ c- i' a5 Y
- 7 ]+ y% L ]' n8 n: [
- /* var builder = factory.CreateBuilder()
# g9 k% i; y5 O" A% l- W% x. M( a - .WithLanguage(opt.Language);*/
4 `, p: F7 Q, e5 ~% E - var builder = factory.CreateBuilder()
6 z3 U4 `3 f, g2 n- s8 z - .WithLanguage("english");
/ z# a W* O$ | - using var processor = builder.Build();
+ o; n0 q2 H8 y( |3 P# A. i
2 T9 F0 I+ w9 s7 ^/ x9 U( g- using var fileStream = File.OpenRead(opt.FileName);/ c) s" g# @0 H1 d
- " \( o5 P- n1 x: j& P& X
- var wave = new WaveParser(fileStream); a1 Y- }0 Z1 ?- A$ Y% u2 T# ~; t" b
- + }- w/ S2 v4 p+ \0 l2 X+ d( s
- var samples = wave.GetAvgSamples();# U' w3 `8 a6 E7 p
- 2 g. `) G' ^( Q4 D5 O% `* E' u1 _) c9 ]
- var language = processor.DetectLanguage(samples, speedUp: true);
2 w4 j" ~) A2 P# J$ P - Console.WriteLine("Language is " + language);
, b0 g: H' r1 r+ U1 r - }: _' X9 h% {7 V
# }/ R4 k, \4 o3 S- @- async Task FullDetection(Options opt), d, ^$ v+ B9 @
- {4 q6 }. D5 K4 G) w
- // Same factory can be used by multiple task to create processors.
' P9 F' a% K8 X; B5 Z - using var factory = WhisperFactory.FromPath(opt.ModelName);( A& ~- A/ P) i' ^3 G V
! z# Z! E) o, G- // var builder = factory.CreateBuilder().WithLanguage(opt.Language);
* c: m/ }" h' g$ D4 j; A4 |1 N8 z - Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");
2 j. ~ w9 S* x3 ]5 }+ S Q - string languageOption = Console.ReadLine();
8 O t- H p- _7 `) W0 i, t - var builder = factory.CreateBuilder()
" J: s" ]' J+ ]4 P# b - .WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);) E0 s2 Z6 n7 K
- ) u8 B1 h" C/ W
- if (opt.Command == "translate")
/ O$ U c1 ]$ _2 |$ ~8 \* m - {
- U2 \+ Q/ X& l' ]! g - builder.WithTranslate();
! d4 b5 q# H4 L { - }
% h J$ J% P) W - : q" p4 U) I$ w, ^
- WhisperProcessor processor = builder.Build();+ \; \7 f& L- _, Z
- & w0 b: m, j* h( Q
- Console.WriteLine("请输入wave源文件目录:");
* }$ G* G- x2 f0 w4 f* C* h4 \ - string sourceDirectory = Console.ReadLine();+ q* B) g! Q, H
- 2 O; {+ u) Z3 |+ N& J
- Console.WriteLine("请输入目标文件目录:");
& B @% N: I, S! V* h - string targetDirectory = Console.ReadLine();2 L0 y7 N( R! ]/ j& h. A+ q! U
- 9 K0 ~* p3 H, @" V9 E" Z: @
- if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))
& m2 L" G6 Z4 o! p" I" f - {
J0 a- R2 R+ r; G - Console.WriteLine("目录不存在,请检查输入的目录路径。");
. z' I% v5 y4 [. ?6 V0 d' k - return;
; ~- ?/ H' }( r" R$ F( t X - }
. e) b! [5 }2 t2 c - 6 a4 `- t$ p; Z
- await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);3 [* w# c* i _0 d( ~- H
$ o# h8 B. R: {) }" O- Console.WriteLine("处理完成!");
8 T7 c5 _; c5 o R; b' F# [
0 c* F8 E9 J! h) w1 A; E. m- }* T5 n# r) }; a& _) A
- static async Task ProcessFilesAsync(WhisperProcessor processor, : S, }& H$ B/ L3 B2 J0 a6 V
- string sourceDirectory, string targetDirectory)! i) N' G5 K) P( V* c1 P$ [
- {
3 }8 j; ]! d( t# p2 D - var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);# `" C2 _) @7 @0 ?" g( j
- " W" K4 ?/ c- m A1 E7 g
- foreach (var sourceFilePath in files)
9 _& v2 u2 b) ]8 C - {. w4 y# H0 f0 R$ D m' Q* X
- string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);
$ v- P4 p z9 F% D+ @ - string destinationFilePath = Path.Combine(targetDirectory, relativePath);
" X# I. P8 l# ] - destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");- l$ B `0 a, G8 P$ ?/ O
- " j- C7 u- e- `( |. U; p7 J- y2 H/ C
- Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));( E) r! `& g! ^
6 S; x' M/ g/ i# f
. I! r* p, {# y7 K% K- if (!File.Exists(destinationFilePath))1 e$ i2 ~) p/ H0 y% x; e
- {
6 Z6 R3 N5 V- v0 s* o( Y - Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");
# j- W6 J/ G+ w4 K( O - & ?' }2 V. t5 |3 R1 ~
- using var fileStream = File.OpenRead(sourceFilePath);' f' n# l/ I. b6 P$ ~
- var segmentIndex = 1;% V3 R) n" t7 s" D
- using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter
9 B( E" I: S' l$ S3 G8 o; v - var startTime = DateTime.Now; // 记录开始时间( V, @7 I+ U1 n: b, r* s
- 9 z$ y+ F5 y$ _+ b
- await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))7 \- L) E* T- m+ U8 [9 e3 h
- {
5 z# _: n2 u* l% W* y - Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
W, ?8 ]5 R( g j5 e5 p1 u* \: P) F - Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");1 f* M7 m/ T* @! H& y6 W. C1 ?
- Console.WriteLine(segment.Text);6 E- l+ Q9 b5 \* U2 x9 T w
- Console.WriteLine();+ {$ u4 t8 E% N
. O& y( ]- [0 `' Y, }- G& p- // 将srt内容写入文件
4 P4 f$ \ e9 e' e+ _1 _& C- h - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
1 x4 y9 X* |! v; ]* z0 S- H( |& C. S - await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");( Q) k$ Y: J/ m C7 K3 K: x
- await writer.WriteLineAsync(segment.Text);" w7 s, k3 N, i0 R+ V$ v9 l! j
- await writer.WriteLineAsync();
, a J$ R; ]& R# _0 ^0 F+ i6 x" Z
8 Z. ]2 L" K0 j" v1 H- writer.Flush(); // 立即保存srt文件
( N1 z& \8 N2 t& i6 _- Z. @2 S/ | - 5 c% A& Z# J, D0 N2 [( ]
- segmentIndex++;) |1 e, D+ L6 K" g" e
- }
7 L' W7 }+ @! I- P+ j1 i - - r& l4 i% w; E4 r! O8 E
- var endTime = DateTime.Now; // 记录结束时间) m: a" k) q h8 P: I! p
- var elapsedMinutes = (endTime - startTime).TotalMinutes;
% d% i. V$ R* ]3 O - Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");6 r% \. E1 b& j! @+ ?
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");
% W8 K$ {5 b+ o8 v+ V4 a - }2 T }/ G4 f, d) c& E0 V
- else {+ m u; _9 x2 q9 {' m0 ^
- Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");
9 n* r5 J1 f- I7 \ - }; K4 F8 w* v- r8 V
- }
: a4 A0 P8 }( B - }
1 R2 D4 X1 i. ?; D6 a - public class Options! u: N! r) ^% @9 k( {& t
- {
! s. i/ B( X- r - [Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
# f( L1 F- n( x; ^8 f - public string Command { get; set; } S: m# S8 k& \' D! @
3 R H7 g4 [7 x- N+ f- [Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]# _8 Z- \7 j- _2 j- C7 i+ i
- public string FileName { get; set; }
! K8 U0 S1 N* c/ @* j - : y/ f* |7 L& F1 R& _ f
- [Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]
{+ h0 ?. L% k: u0 h - public string Language { get; set; } T& X; S5 g- _) {3 J c9 t8 \ r
. p7 V7 q @ f) o- [Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]. b' Y* G, l$ ]9 R
- public string ModelName { get; set; }
+ b+ T% f2 O [* l! }& c2 Q6 u' S
5 G* i0 y' n- m- g6 T- [Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]5 y8 y0 g+ k/ L. }1 h5 N
- public GgmlType ModelType { get; set; }
& Q. x# R! ?$ e% b( @. G - }" \* d1 S4 [1 D5 N6 ]
复制代码 ' }6 X' h _0 `5 I9 _
# h( T, L2 m! p) m2 h# c( ]
0 p/ ^/ u6 ^- f4 @" @) N% A
! _1 c( Q1 B$ Y( H5 o
|