冒险解谜游戏中文网 ChinaAVG

标题: 【Scummvm汉化 #6】 Voyeur (CD - DOS) 偷窥 音频分析 [打印本页]

作者: shane007    时间: 2023-9-1 15:34
标题: 【Scummvm汉化 #6】 Voyeur (CD - DOS) 偷窥 音频分析
本帖最后由 shane007 于 2023-9-2 00:06 编辑
3 ^4 |. o* Q0 b5 B" O/ S! n# m$ b3 [, M/ B+ W+ v$ K  s
该游戏是scummvm支持的少数几款FMV AVG。
& o' _; `6 p0 F1 f* _0 V视频采用了一种叫做RL2的格式。
. ?' `) J) Z) x! R参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,. y, \, A; |( b+ ^# H7 t4 I1 ?
然后用whisper语音识别之后就能配上字幕了。
+ Q* f) b0 F- I此外,rl2格式用potplayer也能直接播放。
+ Q: B, I9 E: O% U6 `" Z9 E: h; z3 p% S
文件格式/ x9 n; b* a) O' I( c8 Q+ n
https://wiki.multimedia.cx/index.php/RL25 r% w) N/ T; b
  1. & ~; O3 l" I* T8 k% K/ e
  2. +  0  dword  Header                -- "FORM"4 X1 |+ t7 U: T/ o; }
  3. +  4  dword  BackSize              -- size of the background frame
    5 S4 A6 }. w6 \& m9 O
  4. +  8  dword  Signature             -- "RLV2" or "RLV3"
    ) A$ P/ a8 J* P" G
  5. +  C  dword  DataSize              -- size of the data past this point BIG-ENDIAN
    # i( |! ~9 @5 D
  6. + 10  dword  NumFrames             -- number of frames0 e0 \3 c% ?- J7 b" @/ r
  7. + 14  word   Method                -- encoding method. ignored, zero( U' y7 w8 Z; |/ W/ \* f) D
  8. + 16  word   SoundRate             -- sound sample rate in some obscure format. zero means no sound  w' C' _+ }( c" \4 l; F! e
  9. + 18  word   Rate                  -- sound sample rate in Hz
    , _- T5 m$ ]8 z2 ^; \
  10. + 1A  word   Channels              -- number of sound channels6 s( x' }4 @3 |1 z% |5 I
  11. + 1C  word   DefSoundSize          -- size of the single sound chunk. see notes below7 B% H  e; z+ T$ x3 B. m8 J8 z
  12. + 1E  word   VideoBase             -- initial drawing offset within 320x200 viewport: I2 n% O$ ?# }: b+ [/ }5 m
  13. + 20  dword  ClrCount              -- number of used colors
    4 o8 L8 T: r4 C) i" c3 Y
  14. + 24  RGB    Palette[256]          -- 256 RGB triplets. full 8-bit entries9 r0 V+ v, }3 k, P. b8 R" ?
  15. -- if Signature == "RLV3" AND BackSize <> 0
    . x' ?5 U( H+ u2 T
  16.   +324 byte  BackFrame[BackSize]   -- encoded background frame. ignore VideoBase during it's decoding) |, Q0 w$ d6 U+ u' {' Q- s8 Z2 d
  17. --
    3 ?" q$ V% }' U6 n# B
  18. +xxx  dword  ChunkSize[NumFrames]  -- complete size of the chunk for each frame (audio+video): J1 C1 ?5 ~" l% H) v
  19. +yyy  dword  ChunkOffs[NumFrames]  -- offset of the each frame chunk from the start of file
    5 P8 k7 V4 ]9 F& M; c
  20. +zzz  dword  SoundSize[NumFrames]  -- size of the audio portion in the frame chunk
    % Q4 P6 x* g7 X! a% c4 x$ ^
  21. -- for each frame --6 M* c) a% p* A! d* [' Q" o4 f
  22. +xxx  byte   Audio[SoundSize[frame_nr] & 0xFFFF]  -- raw 8-bit audio
    # ~+ _% z& |0 ]+ e; Q/ M: L  _
  23. +yyy  byte   Video[...]                           -- compressed video stream
复制代码
9 K  h; x9 }( A. N* T: O8 m; \
参考代码(有问题,但可参考)
  1. using System;
    - b1 C6 k% C+ v* p9 Q9 z
  2. using System.IO;
    6 g& P; R4 C8 {( F" @
  3. using System.Text;
    ! g5 g5 k& Y; D2 G- O# y. D$ J$ Z

  4. 6 f/ T5 B) @* t' a
  5. public class RL2ToWavConverter7 h' V( X) _3 I8 @! H* W
  6. {' P; n. T5 E8 s- A; G- h
  7.     public static void ConvertToWav(string inputFile, string outputFile)
    9 {( e( ~7 F+ `# J4 ]) {
  8.     {# o& L6 M% t9 i2 [
  9.         using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))' ]6 J1 T8 [5 f' @/ @7 ?& z, k
  10.         using (BinaryReader reader = new BinaryReader(fs))4 d; D: f/ o( f
  11.         {
    6 _1 v. S: i6 q1 U; ?5 k$ B
  12.             // 读取头部
      p# t4 s7 i* s. Q. W5 V; j
  13.             string header = Encoding.ASCII.GetString(reader.ReadBytes(4));$ V/ k5 t; f; U; I
  14.             if (header != "FORM")- l# @9 v, a! |/ _! J: I% q
  15.             {
    & C3 q( N! ^! R7 i5 _! F: S; b) P
  16.                 Console.WriteLine("无效的.rl2文件格式。");
    1 \4 I! e! V; U2 ]7 d8 s; U4 ?8 k
  17.                 return;5 q& G3 L; ?: T% S8 C
  18.             }
    9 |+ j4 B# H( l8 r% L. z

  19. / j- f; S+ @6 K0 t9 v/ p9 r) G" ^, J& N
  20.             uint backSize = reader.ReadUInt32();5 c8 m6 f6 i) y2 X' p4 ~  l
  21.             string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));" e5 Z- j* G0 L6 `
  22.             uint dataSize = reader.ReadUInt32();
    : i8 Z, V9 V: o- {* ^
  23.             uint numFrames = reader.ReadUInt32();* a! O+ s& c9 S
  24.             ushort method = reader.ReadUInt16();: X6 a+ C* j# J( A( M8 x% ^
  25.             ushort soundRate = reader.ReadUInt16();" U5 ~/ X$ }# W8 i: ~/ a
  26.             ushort rate = reader.ReadUInt16();- P& m, Y- e1 X. _$ J) A) [0 o
  27.             ushort channels = reader.ReadUInt16();0 f( ^( N" N1 W/ i/ o5 @
  28.             ushort defSoundSize = reader.ReadUInt16();- V5 x: B, R9 ~: l* O( C
  29.             ushort videoBase = reader.ReadUInt16();
    ' C% {7 w! F: ]  v& x
  30.             uint clrCount = reader.ReadUInt32();
    5 O0 q9 m! E) H/ P5 m* g% G- S
  31.             uint[] chunkSize = new uint[numFrames];4 f# H' C3 p8 L" A  A3 D
  32.             uint[] chunkOffs = new uint[numFrames];
    $ ~# [  X; Y2 d
  33.             uint[] soundSize = new uint[numFrames];  |% \. b4 f5 @$ ^# Q  _: [
  34. + e- B* A" \2 f
  35.             if (signature != "RLV2" && signature != "RLV3")
    8 O% W" n# R- s/ J! A4 v  R
  36.             {
    : I) Q; s1 K6 F  Z9 H2 g
  37.                 Console.WriteLine("不支持的签名。");6 b# f! _. N$ P1 S2 ?5 G' o
  38.                 return;, I. |# W2 p! B/ Q2 ?
  39.             }6 ?% |9 r# ?6 G
  40. ) S# |% ^! S) `1 o) m
  41.             // 读取块信息
    : T% p0 F5 ~0 C- F1 U  U  G
  42.             for (int i = 0; i < numFrames; i++)# g* q& ]3 y- _, w/ q7 V" |
  43.             {4 o1 n( t# w  `/ i8 T
  44.                 chunkSize[i] = reader.ReadUInt32();
    , O1 Q3 a2 K0 d  t: }- \
  45.                 chunkOffs[i] = reader.ReadUInt32();
    + E7 m; ^/ f2 m
  46.                 soundSize[i] = reader.ReadUInt32();
    % D# l( q8 n5 ~' a/ @& Q' ?
  47.             }
    8 y5 s6 r  F' A+ K! x7 x% ], W

  48. 3 u8 ~. F6 h* J% L- |& X
  49.             // 如果存在背景帧,请跳过它
      y" g  p: n( n2 k7 ?
  50.             if (signature == "RLV3" && backSize != 0)
    + u% O& k! o* \! o' S
  51.             {3 Q" l% M6 [/ D
  52.                 reader.BaseStream.Seek(backSize, SeekOrigin.Current);4 f1 ]+ k3 X- v2 }' B
  53.             }. F' X4 G. c: Y1 l5 C3 Q

  54. * z& ?( H! F( g3 a% F) G  k; Y
  55.             // 创建一个WAV文件并写入音频数据
    % G) X9 t4 m' Z3 E! f  q
  56.             using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))
    5 \& V) s9 i; Q7 ]
  57.             {+ t1 h/ G7 i4 s5 d- T+ c, }# v' V2 D. R
  58.                 // 写入WAV头部
      w/ u- _, v- F1 m" G+ M. [
  59.                 wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));
    ! M8 z& J8 W6 h* J& V7 C! H# f: z
  60.                 wavWriter.Write(36 + dataSize); // 总文件大小 - 8( l+ _' c& ]" a1 ^. x# u
  61.                 wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));
    ( C0 E/ G+ n& {0 c9 B0 E: `- F. ~
  62.                 wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));1 e  \! n/ [! Y1 e
  63.                 wavWriter.Write(16); // fmt块大小5 g! t. b$ ], f7 l
  64.                 wavWriter.Write((ushort)1); // 音频格式(PCM)% x3 b7 p1 _: {/ @+ K# l6 F) @5 |( I' j
  65.                 wavWriter.Write(channels); // 声道数9 F: M7 T- E+ ~5 J; ~
  66.                 wavWriter.Write(rate); // 采样率
    . Q8 `; s5 T; f% i& G
  67.                 wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数) n: n9 A( p, S4 z- R8 Z( t. j
  68.                 wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数
    * \* P+ Y* v+ S8 v. t
  69.                 wavWriter.Write(defSoundSize); // 每个样本的位深度4 R& m1 f$ ]; x1 Q) J1 U1 R. ?
  70.                 wavWriter.Write(Encoding.ASCII.GetBytes("data"));9 N9 V* D, R5 D! u; |
  71.                 wavWriter.Write(dataSize); // 数据大小
    & R7 l! Z1 r9 H2 f/ h( D9 a
  72. 0 r* j3 H, \& u# I, V* I. n
  73.                 // 从.rl2文件中读取并写入PCM音频数据4 u: }0 H  c, {
  74.                 for (int i = 0; i < numFrames; i++)
    7 c) I  m' X6 M
  75.                 {% ^) L; _% \+ k; q; a1 U" R) U7 ?
  76.                     byte[] audioData = reader.ReadBytes((int)soundSize[i]);' ~% K! x0 b  C3 I' i
  77.                     wavWriter.Write(audioData);
    9 D2 }  f! D; P- e! V. |9 h( V
  78.                 }
    $ q9 S6 e4 ^/ g+ y# Y0 S) O' q
  79.             }
    5 G1 ?, Q0 \3 ~- r
  80.         }* P* P! N0 X& W. R
  81.     }
    / d) E  T  b( R; d2 s; T
  82. }' ]0 n4 E. s% F" z0 t

  83. ' ]' m: \6 T6 E
  84. class Program& Q% x6 x1 o# j
  85. {  N, Q6 I$ B" E! t% S4 j
  86.     static void Main(string[] args)
    & c% b; k+ D! Z3 t/ U% h
  87.     {
    4 F$ y, C0 r' h" b
  88.         string inputFile = "N1275510.RL2";) C/ E- [* H# |- w% c
  89.         string outputFile = "output.wav";
    ; @& j$ N* L  m0 [% N& H! w# B
  90.         RL2ToWavConverter.ConvertToWav(inputFile, outputFile);/ h. G3 v& c1 L% I! R- d- H+ l
  91.         Console.WriteLine("转换完成。");4 p8 N# G; `6 y7 P$ {# B$ Z5 G- R
  92.     }
    . K' M. q! Y  S) D
  93. }! F. K# J2 S: u) l) L  k7 f2 a
复制代码

! f6 o# A- y9 f) J0 \* u+ P6 Z/ |, B% \4 H5 A' i4 _; F! d$ _

6 s$ ]: M0 a4 Y/ w8 r$ _




欢迎光临 冒险解谜游戏中文网 ChinaAVG (https://chinaavg.com/) Powered by Discuz! X3.2