冒险解谜游戏中文网 ChinaAVG
标题:
【汉化工具系列 #2】指定wave格式转换为srt格式字幕(CPU版本)
[打印本页]
作者:
shane007
时间:
2023-9-4 11:03
标题:
【汉化工具系列 #2】指定wave格式转换为srt格式字幕(CPU版本)
本帖最后由 shane007 于 2023-9-4 12:57 编辑
1 z: \) ~$ y7 b( [+ e% p9 \9 r
* c/ ^. o# D/ o n; Y7 {
本程序是在Whisper.net.Demo基础上修改的,可以批量识别wav文件,
! W* W- K. {% B$ R
使用时需要輸入语言,源文件路径和目标文件路径
! I/ B4 ]9 b; |; v q6 [7 g
最后是输出srt文件
3 _& D% R9 _5 N$ V
& U; [8 L; h4 Y! B0 D) J% v
代码如下
; G# }: _2 ^( N2 ^
以下这句用多线程可以增速,否则很慢
# b$ l. i8 l" y' ^
以下这份代码,还只能使用到CPU,速度依旧比较慢,使用CPU的方法另行研究。
! k, ^; Y1 |/ {
; G0 ~+ G0 x9 R: y( n0 g! R" C
; Z( k. s- q4 _# \( G
var builder = factory.CreateBuilder()
6 L3 h, n$ l$ k; Y- L/ P) m( A! ]
.WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
复制代码
) f" i( f1 o4 w2 T' c0 P
; w% `! I' T7 L! C* |
// Licensed under the MIT license: https://opensource.org/licenses/MIT
1 y: b" V; h+ g
. `- x) E j& h: e C
using System;
5 u% ~1 P( p% i
using System.Diagnostics;
2 |5 N2 ?$ b0 T( m! t( R
using System.IO;
h5 i) Q1 F3 y
using System.Threading;
5 b t4 i) {6 a( S) u$ c! {9 E
using System.Threading.Tasks;
8 K2 V6 a' W4 C; O
using CommandLine;
) y7 D; ?% \' q% ~3 ^& v) x
using Whisper.net;
8 _# ]* \6 ^7 r
using Whisper.net.Ggml;
4 d# E6 n/ l6 I: T
using Whisper.net.Wave;
5 [( @9 J/ F2 R) e
2 \& x" Z5 V% q$ J) ~) e5 N
await Parser.Default.ParseArguments<Options>(args)
9 Q( Q& T9 ?6 X" D l1 I y; k
.WithParsedAsync(Demo);
# `! _8 ?# p( v$ h8 c
- }, A' T2 U- W1 q/ ?% b+ ~; a
async Task Demo(Options opt)
3 J, Z8 \ B5 m1 u
4 U2 O, F2 i8 q Y& N3 |3 z( V
{
2 g+ Z% {4 r* I ~
if (!File.Exists(opt.ModelName))
9 Q" R$ Z; J, b) r: z8 ~: w' V
{
3 {/ l% d3 f) x3 Q2 L' w/ n
Console.WriteLine([ DISCUZ_CODE_1 ]quot;Downloading Model {opt.ModelName}");
, ~ l; K# O8 d% W1 }! m
using var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType);
* W: T, {2 R# j
using var fileWriter = File.OpenWrite(opt.ModelName);
' |8 g+ y3 ^. G/ M8 G
await modelStream.CopyToAsync(fileWriter);
+ j- _1 F" K9 Z& ]! Q
}
8 z$ U& t/ _% j
8 d8 V# R1 J- _# P C
switch (opt.Command)
) i* H8 h' U/ q+ K9 Q( `3 F0 h
{
9 @$ Y/ b) p Z. \4 _
case "lang-detect":
. O: ], q2 t- c; M+ R" ~3 U
LanguageIdentification(opt);
* `: Z( _9 C( X- P9 E
break;
9 `- _! |: [ `
case "transcribe":
4 o" p3 S' F; |/ }
case "translate":
8 B: }9 h% Q: n
await FullDetection(opt);
2 Z* v/ ?9 Z1 C- }! {
break;
2 [( X$ e9 Z0 z% V+ G
default:
* F' I1 N5 \' \1 E9 s& W! a3 b
Console.WriteLine("Unknown command");
6 A% I. i5 s1 `. p& {$ k
break;
5 K/ d! G( s; M* h( t4 b/ v a- t
}
/ W; E/ s! ~* B8 \( M
}
: u! k7 ?4 i$ ~
' p: u( N) @ \+ D% }
void LanguageIdentification(Options opt)
4 Z4 x3 W1 X8 x- S
{
; H; A" e8 [5 _5 R9 A0 D
var bufferedModel = File.ReadAllBytes(opt.ModelName);
9 y, T# g/ @" v3 [% T# R2 `* A+ ?
3 j& }2 p0 w ~+ ]
// Same factory can be used by multiple task to create processors.
) k+ s3 J3 K1 \# y4 ~: F, O: _
using var factory = WhisperFactory.FromBuffer(bufferedModel);
/ U2 v6 ^+ g7 ^7 f% M/ B) E
& P+ b' `' P5 u" _- r# y% I- [
/* var builder = factory.CreateBuilder()
5 P/ @, F$ a$ O. g+ k1 \4 g; }1 H1 a
.WithLanguage(opt.Language);*/
6 x- B1 ?# s( s4 E9 U1 D
var builder = factory.CreateBuilder()
9 c1 H9 s" P+ D. h) P
.WithLanguage("english");
+ K; S H& f1 i, m" x) I
using var processor = builder.Build();
- `# q8 d! J4 v2 q
; z4 E5 L ?- V& A6 {0 s" G& t
using var fileStream = File.OpenRead(opt.FileName);
+ C7 e: @' p% D$ k1 t
+ A9 ~% Z7 V' F E& L
var wave = new WaveParser(fileStream);
6 a8 g; A6 m* O; T0 I% R. {
' s# S+ P+ [- Z& ~8 Y7 B( c) H
var samples = wave.GetAvgSamples();
' O7 U4 |( s+ h5 h8 N
2 P" f4 r6 Q* ?- u0 P9 I
var language = processor.DetectLanguage(samples, speedUp: true);
1 n n: |8 Z* z8 }: u+ Y+ j
Console.WriteLine("Language is " + language);
Z( {' K6 P4 ^/ H) t1 }9 |4 y
}
) x# _9 _+ p- N: M J5 m: {
0 o7 P1 s) y/ s8 I+ W5 }+ x
async Task FullDetection(Options opt)
/ K T3 M/ e/ J$ j: ?
{
: n$ Z) A7 O, M m3 L0 O& f9 O
// Same factory can be used by multiple task to create processors.
5 r- E; M1 G6 }/ ~3 i7 h: ~
using var factory = WhisperFactory.FromPath(opt.ModelName);
" O9 X& l; N; f7 x1 c
; y7 @" w( G F p2 s
// var builder = factory.CreateBuilder().WithLanguage(opt.Language);
$ F( m8 V, o, I. y% m; T$ l
Console.WriteLine("请输入语言选项(例如:english,chinese, japanese等):");
% L' n- N: U2 p8 t3 M K
string languageOption = Console.ReadLine();
?/ b! `& n4 f+ S$ g$ Z y
var builder = factory.CreateBuilder()
3 D- M# r- B$ f0 P* I
.WithLanguage(languageOption).WithSpeedUp2x().WithThreads(16);
' d; _7 Y' K# |3 } i
! |4 S% r. h8 p
if (opt.Command == "translate")
2 I) b% K# }% I" m& D2 \% H: \
{
% v% _7 G- [3 t4 d t
builder.WithTranslate();
1 w- s2 m; H2 y. Z
}
9 n) e3 f1 t3 m; g {; k/ W9 U6 t
7 g1 D `6 M0 }! {
WhisperProcessor processor = builder.Build();
' }6 r% x% n+ ~$ _' ]; b5 z
* I5 N8 P% \6 a' k% {( y4 S; h
Console.WriteLine("请输入wave源文件目录:");
$ [$ V" `8 o( {, i' l K, K
string sourceDirectory = Console.ReadLine();
: K/ O* q7 d8 D) `7 k0 _+ d g
( A2 C7 b1 b) g+ d# ?6 s
Console.WriteLine("请输入目标文件目录:");
! g. O5 w" x6 a& B
string targetDirectory = Console.ReadLine();
$ G8 v* M* W% l, y% Q% e
; r5 d) `/ K# p
if (!Directory.Exists(sourceDirectory) || !Directory.Exists(targetDirectory))
. P2 p5 z+ d0 m+ c1 t! E P
{
2 J" f; O( _4 z: q
Console.WriteLine("目录不存在,请检查输入的目录路径。");
- r7 X( [" @- a1 `
return;
; r: v N O6 W$ }
}
0 f( Z+ |: o- b& N* s
i8 V6 q8 g7 H2 N" b; E
await ProcessFilesAsync(processor ,sourceDirectory, targetDirectory);
9 M; S6 q" L7 M g m: C6 \
! H1 X. [5 B8 n2 ]5 e {
Console.WriteLine("处理完成!");
* w# f) g) x( r* m: _
* D+ U: p3 Z! s, Q4 t
}
' `7 x- R) x8 E: n
static async Task ProcessFilesAsync(WhisperProcessor processor,
9 T" W2 L& u4 B- @; Z
string sourceDirectory, string targetDirectory)
8 A3 w" l7 ]" E, x, I2 ]
{
! l' t7 E5 c+ @5 t
var files = Directory.GetFiles(sourceDirectory, "*.wav", SearchOption.AllDirectories);
Q9 z# x* y1 C
% |$ U. v' u5 O
foreach (var sourceFilePath in files)
9 N3 m* P* Y& C4 o* y7 P
{
/ y$ @% }' F* U0 S4 T" h
string relativePath = Path.GetRelativePath(sourceDirectory, sourceFilePath);
4 Z4 k- d& r% \5 F4 f
string destinationFilePath = Path.Combine(targetDirectory, relativePath);
8 g/ S7 \3 K4 V: K
destinationFilePath = Path.ChangeExtension(destinationFilePath, ".srt");
& @5 G# e! F3 T+ P, j+ j6 _# G
3 R6 d3 |$ r \: A( w4 K5 _. F
Directory.CreateDirectory(Path.GetDirectoryName(destinationFilePath));
- t$ y; e# O& {- h
2 h% ]$ h3 ~, i8 ~ S
* T* ^ t) a' ?: q4 {! m' L
if (!File.Exists(destinationFilePath))
. T. W. [( H" O" p( z6 R. G8 D7 g
{
8 m5 ?0 i$ Z2 C. w% p* K
Console.WriteLine([ DISCUZ_CODE_1 ]quot;正在处理文件:{sourceFilePath}");
8 S2 I. j5 m, X. Z( |+ ~4 t
7 F6 v1 a |) `9 j* F
using var fileStream = File.OpenRead(sourceFilePath);
: s; p6 c4 J8 @( {% Y
var segmentIndex = 1;
) B- H' R$ w/ l1 L' K
using var writer = new StreamWriter(destinationFilePath); // 创建用于写入srt文件的StreamWriter
o4 J$ W9 P: K( k) q
var startTime = DateTime.Now; // 记录开始时间
& Q" h# w9 g9 ~5 j
1 |4 ~+ C5 L. L
await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))
8 ~2 e4 t0 f( x$ `
{
6 M$ J* q0 _; Z5 i+ e9 R/ l
Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
$ V0 B- `5 m* w, Q
Console.WriteLine([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
3 { T+ f% ~* \! e9 @- k( T
Console.WriteLine(segment.Text);
- e( P5 _( s! c
Console.WriteLine();
* Q. e- R* s! [5 J" F
) Q; G h3 U) `
// 将srt内容写入文件
# e; g8 j8 A2 g" m6 X7 y, {
await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segmentIndex}");
O9 l* J9 n$ z# q0 s! p1 b& @
await writer.WriteLineAsync([ DISCUZ_CODE_1 ]quot;{segment.Start:hh\\:mm\\:ss\\,fff} --> {segment.End:hh\\:mm\\:ss\\,fff}");
. Z8 g9 e' @; g O7 Z2 v
await writer.WriteLineAsync(segment.Text);
6 D" _% n; p r- E5 d3 s' W7 K) Q! v
await writer.WriteLineAsync();
3 U" ?1 L- r6 a: k3 u) n
! x5 u" s4 F6 k6 @$ O
writer.Flush(); // 立即保存srt文件
1 h6 Q! L. b2 M/ q
" C& Z; r) |( y1 Z- A- j& e9 |
segmentIndex++;
/ G$ [# G2 b6 Y& w( Q
}
- ~4 L* H8 d/ I9 L7 X. g
]) O4 b Z: t7 G R
var endTime = DateTime.Now; // 记录结束时间
, h2 ?8 B: d/ K- a# Y6 I
var elapsedMinutes = (endTime - startTime).TotalMinutes;
+ @/ h k1 D9 M0 Y% g
Console.WriteLine([ DISCUZ_CODE_1 ]quot;已生成srt文件:{destinationFilePath}");
; P+ K) z; a+ T* y* l$ v- k3 x
Console.WriteLine([ DISCUZ_CODE_1 ]quot;生成耗费时间:{elapsedMinutes} 分钟");
, ?) _4 y- h9 M5 D2 V: u6 x
}
/ P9 Y# @* b$ h1 c7 D# J5 M
else {
/ Z8 U7 Z9 K' ^. L+ K/ Y. O! X
Console.WriteLine([ DISCUZ_CODE_1 ]quot;srt文件已经存在:{destinationFilePath}");
, t. U& \5 W. \- p
}
w/ t& u0 e! f# F* ^! i
}
* J. G' h% \4 J4 _4 p4 t* s
}
, A& u$ P8 \% I5 V! Q
public class Options
+ J. F* S% S/ _1 z- P6 v; Y
{
, Q) V8 h: l* v8 _, S
[Option('t', "command", Required = false, HelpText = "Command to run (lang-detect, transcribe or translate)", Default = "transcribe")]
+ \; Z+ Y4 R. `1 {( {, k
public string Command { get; set; }
( R7 D1 W( s9 Q* q: c1 z
, g; R" s f- `/ u: L' I
[Option('f', "file", Required = false, HelpText = "File to process", Default = "test.mp3")]
8 _ M4 Y0 j# P# b' w
public string FileName { get; set; }
! M. e$ d) v* A: r, }% p/ d
& T: g( I- i8 F6 P2 j; g
[Option('l', "lang", Required = false, HelpText = "Language", Default = "auto")]
4 S: R: ?6 d6 w, T8 J
public string Language { get; set; }
- h) _5 c, S7 K% E
# k6 G3 H' t3 v' }7 c% S+ K+ P6 \
[Option('m', "modelFile", Required = false, HelpText = "Model to use (filename", Default = "ggml-large.bin")]
8 X- q) N! o: J1 p
public string ModelName { get; set; }
|" V0 F+ e& `$ k9 a$ _
% P2 n: G' N7 R" Y, `0 i
[Option('g', "ggml", Required = false, HelpText = "Ggml Model type to download (if not exists)", Default = GgmlType.Base)]
} I0 ^ W9 [! w1 X+ [6 }
public GgmlType ModelType { get; set; }
9 X9 j: t7 D* b9 A$ C; d
}
" g! G# w% Q6 p6 h0 H; \! n4 P+ C
复制代码
; A4 U9 n8 V/ G' `1 q) ?5 a
j9 J+ V; F% ^4 a8 y9 l5 `
! v( U2 J! ]5 \. X
* s. ^1 K2 ]+ T4 S: w0 C/ u
作者:
星之韶华
时间:
2025-4-4 01:06
学习学习一下
欢迎光临 冒险解谜游戏中文网 ChinaAVG (https://www.chinaavg.com/)
Powered by Discuz! X3.2