冒险解谜游戏中文网 ChinaAVG
标题:
【汉化工具系列 #2】指定wave格式转换为srt格式字幕(CPU版本)
[打印本页]
作者:
shane007
时间:
2023-9-4 11:03
标题:
【汉化工具系列 #2】指定wave格式转换为srt格式字幕(CPU版本)
本帖最后由 shane007 于 2023-9-4 12:57 编辑
; O$ X; q% ]+ p! _+ y5 C
2 J+ f n+ e1 [9 w/ k" j: E8 e
本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,
+ O/ `: l# T2 E7 [' n- E; v6 o
使用时需要輸入语言,源文件路径和目标文件路径
) P0 T# X8 P' N7 b0 A
最后是输出srt文件
6 H0 {+ m4 @2 M8 N4 ], H8 x
" X6 e/ o1 S/ g k3 H7 X: S
代码如下
, D* t5 R8 u5 y
以下这句用多线程可以增速,否则很慢
( `& U5 P: L) d
以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。
+ T9 y9 n. c/ Y# ]- I5 }1 I
; ]6 `6 Y+ z6 D& H8 {: `' U" r
7 C0 `3 E- p R% U
var builder = factory.CreateBuilder()
; P( c/ `0 z. s0 q& S6 w a- A
.WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码
) X8 B7 o Q4 W3 L
9 g( i: Q; o4 w& ]' Z; H5 t. @
// Licensed under the MIT license: https://opensource.org/licenses/MIT
/ I' j- m U7 G, q- H$ I" k; x1 t" D
( u6 l6 A2 m ]5 r M. J9 K
using System;
* A% M! m$ s3 `, _% s2 z: L8 `1 ]
using System.Diagnostics;
! i4 n# O- K& P3 C3 v: j
using System.IO;
7 }, C" t( F: D; D2 y4 }
using System.Threading;
: W5 @/ A/ B' l" P2 T5 w: Y& Z
using System.Threading.Tasks;
- q4 g( a+ d( o/ W
using CommandLine;
: \/ Y9 M4 K; \4 t
using Whisper.net;
# S' k$ @8 E) s; U
using Whisper.net.Ggml;
- U+ b5 \6 ?- ?' L
using Whisper.net.Wave;
4 {3 f2 {8 l8 X1 A* f0 e: z% e5 E
9 }2 b) r) t) a
await Parser.Default.ParseArguments<Options>(args)
+ t2 ?: D+ J8 \+ f! I+ N9 G1 }5 f
.WithParsedAsync(Demo);
! l% E" M( x8 s& d2 N9 A) n
; }% @# n, v3 v
async Task Demo(Options opt)
* @& b6 @* `, h8 o v a# n$ T
. F: Q1 Q/ P% a! j
{
; A3 x* k4 \. @' y1 [2 n' }, x
if (!File.Exists(opt.ModelName))
% M" Z% P5 L' P. ?% c
{
& _1 b) Y1 E0 t: `1 `1 ]" Y8 l
Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");
. Y3 ^( L, o) L" f
using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);
: P3 q4 Z9 @0 C$ [3 u1 }6 ?
using var fileWriter = File.OpenWrite(opt.ModelName);
5 d$ v( \* s8 o3 l6 c/ a
await modelStream.CopyToAsync(fileWriter);
8 Q2 x% g# b* N$ Q. u8 n% e7 ]
}
8 N! q; I, N) V2 ?$ G2 `
" I3 Z) N% \% W$ Y. P/ J
switch (opt.Command)
" o+ f! W6 W9 L: B, r
{
- U2 ?7 L+ _' F% }4 Q. Q3 O
case "lang-detect":
& q9 j! R/ [6 d, t
LanguageIdentification(opt);
+ r/ g" z& `- y+ y# |+ s7 p
break;
$ m* \- ]/ _0 C# X9 f. j! r7 `2 Z
case "transcribe":
" X/ B+ ~9 A% X+ ^/ t* f
case "translate":
! j5 y G$ d7 L* v/ Q
await FullDetection(opt);
/ P' }. w* B. g
break;
+ _0 Q/ o0 c1 u6 C
default:
2 ]3 @1 r) a' O7 r0 u* q) w
Console.WriteLine("Unknown command");
8 V- x" ?8 f* O+ E' Z3 [( ~+ Y
break;
6 n" N' M4 g6 [) P% h2 Z: r6 V
}
g( x8 k1 U/ K6 M, Y2 G3 x
}
1 q1 [! H* A2 k. G- _& k
. Q: x6 q1 L, ]
void LanguageIdentification(Options opt)
6 { {4 w& j2 @$ p8 g1 i$ N) W6 N
{
! @! Z) i. l; R% I$ c
var bufferedModel = File.ReadAllBytes(opt.ModelName);
4 r/ w. y5 ^2 V# L& }9 X
+ a8 d% W7 n3 o7 f$ z( l( y
// Same factory can be used by multiple task to create processors.
. m7 G! ]3 A9 T" h" e+ f
using var factory = WhisperFactory.FromBuffer(bufferedModel);
y' d0 g$ p% ?
3 Z+ p1 Y) d$ W7 C+ ], Z& f3 p
/* var builder = factory.CreateBuilder()
& k; Y$ z1 Z I6 j( _4 _! a
.WithLanguage(opt.Language);*/
- a Y4 x3 d. i# E8 A5 l& h
var builder = factory.CreateBuilder()
4 v. l& {% Z( v4 ?$ y) Q
.WithLanguage("english");
3 E6 l7 ]! C9 e" n/ V5 Y8 m! C: S
using var processor = builder.Build();
6 b& X9 L! b: w
, M& L; F; J& w4 w6 ]
using var fileStream = File.OpenRead(opt.FileName);
: r& f& m( i1 p( g! |% {
7 W8 Y: ^- {4 B5 z0 G
var wave = new WaveParser(fileStream);
- }$ r' C7 a! R# |, Z: z5 L
; Z* J) f( e: o7 Z* E9 K
var samples = wave.GetAvgSamples();
- u' G7 Q: g9 ~$ `% p
& u9 s, H% I$ T9 v; b7 y0 G% b
var language = processor.DetectLanguage(samples, speedUp: true);
, m) z. Q8 ~- X/ |% z1 q8 L
Console.WriteLine("Language is " + language);
1 |0 X2 P9 ?5 x) t. ~- h3 X, T
}
' q7 V6 r% Y+ a) G7 i
' z: e" e3 ?, @7 s. h/ S
async Task FullDetection(Options opt)
( X, y2 S7 @, Z1 T9 I& [; i
{
. \. N+ W) W f1 C
// Same factory can be used by multiple task to create processors.
! C8 e" P5 Y8 u, L) ] }! g0 \
using var factory = WhisperFactory.FromPath(opt.ModelName);
+ p/ r. Y5 K$ Y6 |( \/ [9 P' y2 }* c5 d
X, f! Z& n- o" h `" ]
// var builder = factory.CreateBuilder().WithLanguage(opt.Language);
! g Y, j Z/ @2 [2 r8 L
Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");
/ x& c) X& U6 g- i% \# C
string languageOption = Console.ReadLine();
7 M7 X' ?, T9 J7 F( J3 h, J
var builder = factory.CreateBuilder()
6 R2 x& Z- C; Z2 Z7 P6 y+ E0 [
.WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
6 J. r2 H6 J- Q4 ?5 Y
+ p8 n$ p' r4 S7 B( Z( F
if (opt.Command == "translate")
4 z5 @9 z* `& O: j5 P0 C6 `; c
{
7 _. g0 ?4 u6 F- m/ w5 W! S: i
builder.WithTranslate();
' l) [: |7 s+ }4 l. T
}
( f$ x* Y: o0 B5 h
/ u; C8 A- A0 O
WhisperProcessor processor = builder.Build();
6 A6 h, R- q% Q6 i+ E
: e' c) B& i( f4 [ ?
Console.WriteLine("请输入wave源文件目录:");
& a: {& l; k) Y$ B
string sourceDirectory = Console.ReadLine();
0 p5 ?7 z* D" N6 ~6 e
0 |. \! s3 t( _2 q3 x3 b; I% r
Console.WriteLine("请输入目标文件目录:");
' P' Z0 o) B* {0 b0 c
string targetDirectory = Console.ReadLine();
2 b, b" M# U3 R: U; f) _6 r/ X) c# ]
& ]3 H0 t% \6 e" C j/ b
if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))
' \! h- `- P& _- Z, m; S
{
+ z6 Q( Y# d) Q+ m6 C w5 W
Console.WriteLine("目录不存在,请检查输入的目录路径。");
0 p1 B2 k) h3 Z$ ?' a5 W# s5 `
return;
0 \" L0 N$ I. ?3 W
}
( }8 c& a' P: W) k$ p5 n
1 ~6 Z3 q1 B0 U' B1 A# {. S" \$ N7 a
await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);
2 v) d8 q5 J, b+ j+ e* D' l
& t2 h" z% l& r" {- d
Console.WriteLine("处理完成!");
8 S4 x3 k; G. q7 w6 q
f" [# p4 z# o+ z
}
0 l( [8 {, D& l# \3 b- I
static async Task ProcessFilesAsync(WhisperProcessor processor,
9 w& p0 g* {- T, \4 v; B2 N1 K9 j3 d
string sourceDirectory, string targetDirectory)
6 K6 a" A* `2 S- y8 c
{
- W+ E3 n! I! v
var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);
- ]1 ?' y X' c* t- O
$ E$ U& M3 ]+ O* B4 K) R, Z1 P( N
foreach (var sourceFilePath in files)
2 m! w" E$ h0 |- E6 P$ B
{
. W: x6 C4 m. W/ B" k. N
string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);
/ K L+ j1 ?, \
string destinationFilePath = Path.Combine(targetDirectory, relativePath);
# e7 z" R- x$ n9 Q5 S
destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");
$ k2 x( p$ z) B7 m- O
# T5 ?$ w: ?" H' G- f# b& N5 g" H t
Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));
4 l& V) ^) @# K0 v) {
" o. g# o7 X5 M0 q
# C. B; }5 M- o# g
if (!File.Exists(destinationFilePath))
. ]) c* \5 C; P9 `; b7 q: p
{
3 Y. a# U, q: ?
Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");
. Q* p! z( A5 e. o5 d
3 k8 e8 U7 ]6 t
using var fileStream = File.OpenRead(sourceFilePath);
9 M2 \9 s" {# J( d: z) k
var segmentIndex = 1;
( Z2 E% M+ o2 P4 {4 K @4 e$ M
using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter
# k7 `0 q4 ?: v6 v! i" R" \
var startTime = DateTime.Now; // 记录开始时间
" a/ a- E8 e9 z" E3 o+ ]
4 a7 o) Y, H! \" H
await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))
/ R. I# Y8 b; _, `5 q" Z! V
{
( n( z5 @; V4 k7 L6 |% R1 \$ y
Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
& B; r% ?* g P$ X, t# y
Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
& f L# Y9 N! R- ?
Console.WriteLine(segment.Text);
9 F0 B: e0 E- |+ [" m
Console.WriteLine();
6 ]7 s0 X& h3 _$ \- A7 O/ n }
. ^- M" q b: S q9 k) p9 ~
// 将srt内容写入文件
/ @ D2 |8 O3 U) e1 a. }& Q
await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
( A0 n; o7 G: H/ b* D! n7 Q
await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
7 v" X, f2 f* O/ u
await writer.WriteLineAsync(segment.Text);
0 Y' r# J2 A, W# t1 [9 G$ e
await writer.WriteLineAsync();
: C- S( y6 Z+ g2 |3 u3 T/ L
, s% D. F/ k; [, X0 o
writer.Flush(); // 立即保存srt文件
5 X. b) f5 q$ o3 ?: ~
& P4 [: h# P, b: t
segmentIndex++;
7 W3 D+ O8 y4 L5 v7 r
}
2 W# u' ?& G7 B/ Y
) m' l' d3 k# B1 w
var endTime = DateTime.Now; // 记录结束时间
0 j1 [$ h& G1 c9 b2 L |/ _/ r
var elapsedMinutes = (endTime - startTime).TotalMinutes;
~& d- n( D0 l0 y
Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");
7 N( B- K. w6 C3 a9 z
Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");
1 K- e- ?0 g$ W0 s+ g' Y( f
}
% u* _% q8 \" f7 D
else {
" U7 w8 o+ Z, v, q8 f$ g# t$ [
Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");
5 i% o' b9 y" q) l: q, A/ a+ ]
}
9 W8 x8 D- ^3 J
}
& x J( g$ W& ~1 u n+ ~
}
" ?# S! d) @- s1 v: z% H
public class Options
0 z$ M- O; V) W
{
7 g2 J1 Z7 E: k' W8 l1 u" q
[Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
0 U: T: T$ L, i0 \/ m0 m
public string Command { get; set; }
5 L9 M& q1 F+ W& H! p" ~/ F
, ]& d* J# h m2 u; S
[Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]
" N/ G6 S+ k0 U
public string FileName { get; set; }
; C+ D8 S) j" w: l( Z
8 K, x) z6 P% K0 L
[Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]
c/ ?- o3 p3 M
public string Language { get; set; }
5 ?8 r1 X2 i; i0 h, m9 Z
; |, i" W& ~- ]6 j
[Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]
6 a: I# W4 i' L3 P
public string ModelName { get; set; }
; Z, ?5 O1 G/ D+ _! T7 s
7 ~. @8 R3 e+ Y6 |
[Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]
( _/ J( L! U9 O6 x: v- F9 }8 A
public GgmlType ModelType { get; set; }
# y; Q3 ?0 @( {/ R" \
}
" t6 v" v! a$ z# ?
复制代码
/ R/ B }2 N0 V6 B+ E
6 G) [3 W; ]5 g1 V, n' n
, l' ?% o% I* `
% E% {2 R1 P4 }6 E8 c u9 E Z6 o
欢迎光临 冒险解谜游戏中文网 ChinaAVG (https://chinaavg.com/)
Powered by Discuz! X3.2