冒险解谜游戏中文网 ChinaAVG
标题:
【汉化工具系列 #2】指定wave格式转换为srt格式字幕(CPU版本)
[打印本页]
作者:
shane007
时间:
2023-9-4 11:03
标题:
【汉化工具系列 #2】指定wave格式转换为srt格式字幕(CPU版本)
本帖最后由 shane007 于 2023-9-4 12:57 编辑
2 m9 n5 s% B5 x
9 B. J# n% ?' h/ T% R6 z, r B! M8 b
本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,
8 P- B* Q& q7 T. ]3 p
使用时需要輸入语言,源文件路径和目标文件路径
1 ^' A! G$ q: ], s+ ^4 [# p$ K, ?
最后是输出srt文件
& v! I, ], K; c3 p+ |7 F7 d
1 s8 r: V2 T# i0 x' v/ Q; K
代码如下
. z: u9 y, G7 c% j
以下这句用多线程可以增速,否则很慢
* i4 v: S0 Z7 S9 N) Y9 P- h
以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。
1 q" X! ?+ t$ c# V. |/ _
" t) f0 P5 ]) \8 E% |5 j4 f
& I& ^2 l3 P) r( r2 i) O
var builder = factory.CreateBuilder()
: v) R* c" T4 ]0 o
.WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码
% u, g' d& d7 ~ |; F
$ }$ d+ I& b+ N' M
// Licensed under the MIT license: https://opensource.org/licenses/MIT
0 ]6 M8 n p' f' u, c' }
2 E- ~& U7 D( X1 T% d' j9 K0 x# E
using System;
]7 h; m/ E4 b( D5 ~
using System.Diagnostics;
/ ~9 O" i/ D- h Y1 d
using System.IO;
, I# H; G+ ?! S0 t0 J$ N) f
using System.Threading;
8 q3 G& z' K/ D ]7 u
using System.Threading.Tasks;
- ~1 }; J# ^; T I& k+ F- B
using CommandLine;
; w% n% U7 d8 ~# o( Y* e
using Whisper.net;
1 R* s/ L% |/ q, _8 O
using Whisper.net.Ggml;
0 x# Q& M2 |4 k3 }" o6 q
using Whisper.net.Wave;
9 p l! \ ~. _
2 j, d2 E0 \9 ]. q8 ?6 G" m0 `$ a
await Parser.Default.ParseArguments<Options>(args)
5 P+ C# @9 ~* t- w' i
.WithParsedAsync(Demo);
) G% K) u: \) X' ?0 {( O! M
+ z$ Q( s2 e0 Q$ O+ R
async Task Demo(Options opt)
0 K( B6 L; P4 l6 H; }
, O6 f# o3 z9 T* Q
{
* F, g" J+ K% h3 G
if (!File.Exists(opt.ModelName))
m* f+ a& K9 g2 L2 ^3 l# j1 i
{
2 ~. ~" C" N1 V8 X
Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");
0 }9 f: n9 v1 j2 I: |( r8 L
using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);
) Q$ s# z. I* x3 G) z, g
using var fileWriter = File.OpenWrite(opt.ModelName);
$ W, X+ S+ z/ a7 I4 h
await modelStream.CopyToAsync(fileWriter);
) z3 o- k" |: Q% Q9 N2 h" l
}
( G6 f9 d4 u' L$ [
1 m( y, z4 W4 p" r
switch (opt.Command)
6 o' B4 ?. }0 D- b8 T) K7 n
{
: {" ?9 m2 F$ [, K3 x# m( p
case "lang-detect":
4 d7 O. U! \- Q1 \
LanguageIdentification(opt);
$ N* w" c {. h* |5 h; j
break;
4 Y% F- m& N# u Y- T
case "transcribe":
- ^3 O4 T" Y- X* N& v( W- z, }1 n0 n
case "translate":
! p" @& K9 L( s/ A
await FullDetection(opt);
# [4 D, c) z* |1 S- {& _0 v
break;
8 B" t2 l# X% h- A0 W0 V
default:
5 O2 p/ u1 g2 l4 z: J. W; O9 X5 B
Console.WriteLine("Unknown command");
W" T8 P7 A, y& k
break;
9 p1 C( O7 H6 R6 v
}
# r! x& `% D. [+ Z3 G3 ~; @4 _
}
2 T' F$ c% E, y, o0 R+ J
8 ~* v% x! r8 {" Q/ Z
void LanguageIdentification(Options opt)
9 P9 [5 L8 w. X: V' l) x
{
6 [6 n/ c" L2 s+ e& ]1 y# [
var bufferedModel = File.ReadAllBytes(opt.ModelName);
J, P+ y3 `( S) P7 `( d
' ^+ w2 Q& R# e& ?5 n
// Same factory can be used by multiple task to create processors.
+ Q+ d, c6 w$ h" J
using var factory = WhisperFactory.FromBuffer(bufferedModel);
# [! W+ N5 f4 a. I
3 n: d5 q9 a" @4 z5 J" V
/* var builder = factory.CreateBuilder()
8 m# X( m3 r5 G5 o/ t
.WithLanguage(opt.Language);*/
, l$ m |7 [* g
var builder = factory.CreateBuilder()
% ~5 h' A/ X B1 r0 [
.WithLanguage("english");
3 |8 S1 c2 T Y5 s, x1 h: e
using var processor = builder.Build();
/ q* a+ Q* t4 _' b7 I
3 E' k) |% a4 T7 n8 W- K- G
using var fileStream = File.OpenRead(opt.FileName);
0 s$ J& N3 l; o/ N& C
X7 J& c6 y! ?) m) ^0 t
var wave = new WaveParser(fileStream);
$ }/ W2 N5 I4 A! L! J8 S! S, ]
# t, D% T& V/ S8 Y( _2 `
var samples = wave.GetAvgSamples();
. I$ o2 z* o) E- b
6 L$ R/ n* Y6 Z' {
var language = processor.DetectLanguage(samples, speedUp: true);
( W6 A5 m4 d: n5 Z, o3 e/ S
Console.WriteLine("Language is " + language);
: M. y) J: r9 W7 T! e- M6 I% K" G* h
}
* |) M5 Z* o; i" ?
' t3 e0 N; h# l/ ?1 e
async Task FullDetection(Options opt)
: A: ?) \7 A4 G3 N6 R3 X Q
{
; s/ u0 u/ l1 L9 Q" F, ~: v
// Same factory can be used by multiple task to create processors.
0 w6 b$ Y5 j* w# n+ O
using var factory = WhisperFactory.FromPath(opt.ModelName);
" E/ Z9 _6 q* w. A& \
; j0 x: j5 r5 P/ y* q# I. d9 p" Y
// var builder = factory.CreateBuilder().WithLanguage(opt.Language);
4 F# {4 R4 C8 o$ G
Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");
/ y; u8 |: `1 Z/ }
string languageOption = Console.ReadLine();
8 V5 k$ ^" A8 @/ c$ v/ `6 c
var builder = factory.CreateBuilder()
( I: P" n: p8 F& Y) ]6 T
.WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
% Z: P4 G: w+ h/ H' s# I( `* X1 b
/ T8 B( u' Z6 c# B4 d6 k
if (opt.Command == "translate")
, Z1 H: z5 B* r5 ^! h
{
! P# l0 l5 ]- ]0 X! ^$ f
builder.WithTranslate();
; j' X7 r; L4 x4 {8 W' ^
}
" F. E& c r' `' E1 |, ~
( ]& @( t1 I, q! v! T' @
WhisperProcessor processor = builder.Build();
1 W$ v6 C# z! J& | m6 b) e
7 b6 N8 _/ \8 h! G) ^
Console.WriteLine("请输入wave源文件目录:");
3 e5 s' |5 h3 o0 q* V: V1 ?& Y" k/ m
string sourceDirectory = Console.ReadLine();
" v5 [1 E8 N, Y* ?; b
0 [0 N$ l6 I% B# ^% N' H* `
Console.WriteLine("请输入目标文件目录:");
& a8 e5 }- {( y2 x( U& P/ C( b+ q
string targetDirectory = Console.ReadLine();
4 \# Z. i$ P+ H# K5 e
^& W8 x$ g$ g
if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))
) @& e8 g% m0 X( U9 L
{
. Z# V1 U1 m- C b" B
Console.WriteLine("目录不存在,请检查输入的目录路径。");
, U. K: _# Z6 d
return;
4 g& q% W1 d+ z1 Y$ M( L0 p
}
7 K4 {( m7 f8 q* T; r0 b( J- N) v
- w6 A: n0 t; n; @* W! d1 p3 @
await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);
. u8 j0 d9 U( k; ~
1 R# L. d4 }+ H4 e0 t& V$ x
Console.WriteLine("处理完成!");
/ b P7 p$ d' N6 j' M+ T' ^% j
; l" T% i5 ^7 e
}
4 o9 V n: i+ o/ e* }2 g* U
static async Task ProcessFilesAsync(WhisperProcessor processor,
9 [' r3 c6 ?# P2 I! k) }
string sourceDirectory, string targetDirectory)
1 N8 F/ A1 M4 L1 P$ j
{
9 P5 N& b& i) @# ]- I: s7 ~
var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);
+ m0 |3 p3 U5 s2 `# X, l( @
8 U8 d. C5 s: r3 x
foreach (var sourceFilePath in files)
" R2 O# @. |' c5 I4 ~' V) Z7 |
{
6 e: {( u% ^2 `: [7 e) x
string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);
% Q# ^2 O0 _9 Y) ]3 I
string destinationFilePath = Path.Combine(targetDirectory, relativePath);
& j$ u; p; G9 ?+ C/ E% s2 O
destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");
6 w4 \4 O( l+ \- e/ M/ i
" S/ z4 a/ ?1 w& ~
Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));
0 z. I% o2 w" C9 v9 }# i6 x) V
, f* M9 K9 d, |' M% k
* u: W) x9 v9 e7 d( \4 h
if (!File.Exists(destinationFilePath))
- @/ N6 R; I. O
{
% C) L3 U6 B0 b/ M
Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");
3 ]( N4 i: R! }# C) i% f
x9 d- x; ]. u2 F: z5 Q# W
using var fileStream = File.OpenRead(sourceFilePath);
0 z' f: T h- M5 p4 i. J/ K
var segmentIndex = 1;
- g8 \4 T) V0 F- z
using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter
' t2 s' B5 c8 W* }
var startTime = DateTime.Now; // 记录开始时间
' A3 h. }6 D* @: s. A! k
: I- P0 X1 {5 F5 [ U% N O4 x+ A2 p
await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))
& `4 X; A; g9 m, D( B+ g& i
{
$ ~) g, k* |1 W* b2 e( z" C. H
Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
6 S# L+ _- U$ a4 |; I, ]5 {1 w
Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
+ S& E6 ^; S, {0 J4 _
Console.WriteLine(segment.Text);
9 Y+ A/ J! u' g+ M
Console.WriteLine();
% A) J2 {) [) R8 i3 u6 h, B
$ U, X( J- F; T2 c3 S
// 将srt内容写入文件
0 _( D; `( r" e# }7 E$ ?: |/ p
await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
! y& E) @: }6 B9 h0 F1 f
await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
# f1 e$ C T# i8 V
await writer.WriteLineAsync(segment.Text);
: z+ s& y7 M8 c' h7 ^/ ~5 F3 ]
await writer.WriteLineAsync();
' `( B( x9 r( u) p5 u) p
( K5 b# f' f" B5 q3 L, x# a1 H
writer.Flush(); // 立即保存srt文件
' I3 y4 F9 D9 v- ?. ~
$ M& D- x- q0 N3 ]
segmentIndex++;
2 j. s; k ]: T: K2 v5 r4 e7 A/ U
}
1 _+ m- r: r" x3 {6 i! t: Z& b
8 f- j/ M6 f7 L B _! U
var endTime = DateTime.Now; // 记录结束时间
( `' d: R5 C. o. ^7 v: f0 @4 G
var elapsedMinutes = (endTime - startTime).TotalMinutes;
^6 n3 w% _3 w* ?2 W
Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");
& T2 o) K) M7 u# T
Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");
: N( I0 _ D' [$ ^% i" U/ G5 `
}
. M& l1 I+ J" V9 _
else {
$ I7 l! v o; D7 a0 W7 B
Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");
) v/ b1 W& H7 N, t% ]1 Y! N% |4 s( ]
}
- q/ Q+ c* Y6 F+ y
}
( u2 y f( [8 P. q, M
}
. M5 n+ ^% K: z+ K9 E& U, `
public class Options
1 g e' H: o- T* ~1 i; w
{
% x. `4 s- i0 e0 q, c
[Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
y% _( A$ M7 ?! E( d* ]. S) d
public string Command { get; set; }
( J% @; U7 k) ~, N7 A$ G
3 |4 {- r) f# h; ~( N/ G
[Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]
7 w8 u" k: \6 h; M e' N9 M' R
public string FileName { get; set; }
' x+ e3 S1 y6 h. `, R8 d6 n
9 f( [( Y; J1 R8 l; y" F; N$ f
[Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]
& R# ~0 N0 f: `6 d$ c9 }; Z
public string Language { get; set; }
- k5 H- I9 t! R" j
$ b9 q# z W8 H6 N* W
[Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]
6 f1 U2 C( ^: e0 y: Z
public string ModelName { get; set; }
! g, C1 b' i$ [1 W
) I, D0 \4 Y, ?$ i9 O6 t
[Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]
# K, m/ W9 E g9 ^; Q* ]
public GgmlType ModelType { get; set; }
9 h, l% _. F) |- F9 L( A7 ?0 f+ n
}
P8 r* t9 w6 {/ [
复制代码
1 x" a! n8 r2 S* F
. J% P" Z2 S1 b$ |2 l* o7 H
z: ]) Q {6 l; g
4 b% r, X4 {; u
作者:
星之韶华
时间:
2025-4-4 01:06
学习学习一下
欢迎光临 冒险解谜游戏中文网 ChinaAVG (https://chinaavg.com/)
Powered by Discuz! X3.2