设为首页收藏本站官方微博

建议 【Scummvm汉化 #6】 Voyeur (CD - DOS) 偷窥 音频分析

  [复制链接]
查看: 442|回复: 1
打印 上一主题 下一主题

[建议] 【Scummvm汉化 #6】 Voyeur (CD - DOS) 偷窥 音频分析

跳转到指定楼层
楼主
发表于 2023-9-1 15:34 | 只看该作者 回帖奖励 |正序浏览 |阅读模式

【Scummvm汉化 #6】 Voyeur (CD - DOS) 偷窥 音频分析

本帖最后由 shane007 于 2023-9-2 00:06 编辑
& ?8 k4 R' [, C
2 b+ F$ G. N# ^) _, e该游戏是scummvm支持的少数几款FMV AVG。
; q/ D' W, J/ o, [, d视频采用了一种叫做RL2的格式。' m* y3 _2 e; a1 A$ |0 l4 s+ _& p, j
参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,
  @5 F' a7 O8 h% u0 R然后用whisper语音识别之后就能配上字幕了。
; U: j8 @* {* k此外,rl2格式用potplayer也能直接播放。
: L5 `; @; j3 c: c% E3 {5 ~% G
# ~/ H. |/ z: Q8 n$ g, v( G, C4 R! F文件格式
5 n8 f& H3 b/ c! Q* R0 t: B5 k* ghttps://wiki.multimedia.cx/index.php/RL2
" p( l9 @# }' [6 N8 o
  1. 2 L$ a5 j4 E5 {) ?* @8 N
  2. +  0  dword  Header                -- "FORM"2 g) N% O* p2 V* Q
  3. +  4  dword  BackSize              -- size of the background frame
    1 A; a1 ^1 o: M  k6 s
  4. +  8  dword  Signature             -- "RLV2" or "RLV3"0 ~. _7 r: e+ R
  5. +  C  dword  DataSize              -- size of the data past this point BIG-ENDIAN
    & _5 z5 ^6 ]8 o
  6. + 10  dword  NumFrames             -- number of frames
    9 {- \5 n! g0 v( B  D' @$ b: `
  7. + 14  word   Method                -- encoding method. ignored, zero/ Y* m+ K8 t3 A9 u, i& n
  8. + 16  word   SoundRate             -- sound sample rate in some obscure format. zero means no sound
    3 Z1 [  F1 n7 o( \9 _: n0 N
  9. + 18  word   Rate                  -- sound sample rate in Hz
    . J& }: W8 N' z  C3 ~& D2 n+ r
  10. + 1A  word   Channels              -- number of sound channels
    3 l5 y- u3 r: `& i9 y+ E( M3 |
  11. + 1C  word   DefSoundSize          -- size of the single sound chunk. see notes below+ u% u$ ]4 q0 i9 B* `- w, R
  12. + 1E  word   VideoBase             -- initial drawing offset within 320x200 viewport
    ) N; D, ]  D/ n) e( Z. h
  13. + 20  dword  ClrCount              -- number of used colors
    ' q# H! g2 i) Q- @1 S! i" L
  14. + 24  RGB    Palette[256]          -- 256 RGB triplets. full 8-bit entries3 X+ K  b  z9 R! `( A1 Z% g
  15. -- if Signature == "RLV3" AND BackSize <> 0% t. z7 y' b+ Y( X$ O
  16.   +324 byte  BackFrame[BackSize]   -- encoded background frame. ignore VideoBase during it's decoding
    ! g1 h9 w' t9 |; A) r1 L
  17. --
    , T5 X* d0 i5 F6 z# u. B
  18. +xxx  dword  ChunkSize[NumFrames]  -- complete size of the chunk for each frame (audio+video)/ P) o  R, F" X$ |  t& S: b. V
  19. +yyy  dword  ChunkOffs[NumFrames]  -- offset of the each frame chunk from the start of file- c% y9 D6 Y4 s; j! Q1 G, M
  20. +zzz  dword  SoundSize[NumFrames]  -- size of the audio portion in the frame chunk' v% N$ D' N+ Z7 u, O) Y
  21. -- for each frame --
    ! u4 q+ l3 b& G3 D
  22. +xxx  byte   Audio[SoundSize[frame_nr] & 0xFFFF]  -- raw 8-bit audio
    + N% G% {) H, C3 r3 {
  23. +yyy  byte   Video[...]                           -- compressed video stream
复制代码
4 ]3 _& k$ W: g# ]& r
参考代码(有问题,但可参考)
  1. using System;) G9 Z) _* c7 U% e5 y8 L& ]6 X
  2. using System.IO;
    ) n& V& _0 \2 _1 Q# a- K$ W. J
  3. using System.Text;
    3 N2 F2 R; V  \; u) `% d- C
  4. 6 j* a7 i, \6 d% J* z
  5. public class RL2ToWavConverter
    1 P9 `, d6 x/ E
  6. {
    8 ]6 ]; m% @) s+ G
  7.     public static void ConvertToWav(string inputFile, string outputFile)
    : y4 |2 Q5 |! h5 b( Y4 |
  8.     {
    3 Y  i& K) O( K/ c) H' x
  9.         using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
    9 I$ R8 k5 d0 Y+ ]/ x6 t8 Q; |" @7 V
  10.         using (BinaryReader reader = new BinaryReader(fs))* O4 t. M  m* v6 s* F
  11.         {
    4 j4 r: `8 P& _8 _! d: f0 u
  12.             // 读取头部
    - q" G* D/ x* v5 `! p9 `
  13.             string header = Encoding.ASCII.GetString(reader.ReadBytes(4));0 e- V4 S! R* C3 h3 k1 w: v
  14.             if (header != "FORM")" [4 t7 d5 N: n9 F- s4 T2 @
  15.             {
    / M2 ]* Q3 y, k1 B. S8 ^' O* E. f
  16.                 Console.WriteLine("无效的.rl2文件格式。");9 ^& R: r5 X0 O/ D* y
  17.                 return;
    ( q2 k% v) R' z9 ^: q3 W* C: t
  18.             }: G( b0 O; U* Q+ j
  19. % W" [: j1 J* v$ S8 V, K
  20.             uint backSize = reader.ReadUInt32();
    0 h( \! o" c; @6 O& @/ i) q
  21.             string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));
    * I: {. P6 r( T  O
  22.             uint dataSize = reader.ReadUInt32();
    0 b$ ]& t" @0 p( {+ a. F" }) T
  23.             uint numFrames = reader.ReadUInt32();" s( j8 m( h+ F
  24.             ushort method = reader.ReadUInt16();
      ~3 H. F" Q3 x' Q
  25.             ushort soundRate = reader.ReadUInt16();
    4 C" E5 S9 Z1 C0 `$ V0 C
  26.             ushort rate = reader.ReadUInt16();% m: S' N/ V1 k8 y9 a& u3 A
  27.             ushort channels = reader.ReadUInt16();
    + h" h, y9 J( x$ O
  28.             ushort defSoundSize = reader.ReadUInt16();
    $ e, q7 T- n7 b' k4 e
  29.             ushort videoBase = reader.ReadUInt16();
    & h2 }) j" U$ j  T) k1 W' |
  30.             uint clrCount = reader.ReadUInt32();
    - R7 q  x7 P" }# Y, `
  31.             uint[] chunkSize = new uint[numFrames];
    7 U/ N* s! H) F/ ]. p, S. |1 c
  32.             uint[] chunkOffs = new uint[numFrames];, O& Z, t! f3 V6 Q9 O
  33.             uint[] soundSize = new uint[numFrames];" l5 K1 t! \$ ^. B

  34. 1 ]1 t2 S; r6 |2 V0 j5 ~/ |( J
  35.             if (signature != "RLV2" && signature != "RLV3")
    / o: Y8 B( y" S/ |" V
  36.             {* s8 j0 ?$ @/ w" k6 z# J
  37.                 Console.WriteLine("不支持的签名。");; a7 [9 h9 Z' i; Y. A2 r
  38.                 return;* k" I3 `2 m) ^# D$ A! T, j: R+ w
  39.             }
    $ _* h; ]3 b1 h- |7 ]! E0 e
  40. 8 w5 L7 t! B. y- e+ Q7 A
  41.             // 读取块信息
    ) z' [9 M& R; o+ h; ]. P( ^. \1 q
  42.             for (int i = 0; i < numFrames; i++)5 ?" S& ?) X. N7 A) X
  43.             {0 W! t' T! |& J/ q" E& d
  44.                 chunkSize[i] = reader.ReadUInt32();8 U; r! s' W+ x
  45.                 chunkOffs[i] = reader.ReadUInt32();
    : W& z( d$ d/ C: Z) ?  `
  46.                 soundSize[i] = reader.ReadUInt32();0 @8 H4 \) p$ w) F6 {
  47.             }+ v/ D, z! k/ G' @
  48. 4 j1 J: U- {& y/ c9 n( C
  49.             // 如果存在背景帧,请跳过它
    / S2 z3 `, ~6 i
  50.             if (signature == "RLV3" && backSize != 0)5 a- {1 ^* n2 M1 P% A: t6 w9 g) \+ j
  51.             {% A1 j3 W9 J, T
  52.                 reader.BaseStream.Seek(backSize, SeekOrigin.Current);
    & c6 F% F0 b; _. X( l0 J
  53.             }  k+ V2 u# Q# P1 d/ R/ _3 P

  54. : P, ~& ]  O  X% a$ G% d
  55.             // 创建一个WAV文件并写入音频数据
    3 ~: R1 X* [7 G+ Q3 {8 c8 ]
  56.             using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))  ]: P) h" r  _3 o7 J
  57.             {
    $ w. w% Z1 b2 x4 U1 n
  58.                 // 写入WAV头部
    ; \( t! c' ?. @  h0 [" V
  59.                 wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));4 E) K! {; ?4 S$ B5 Y0 [  k; R! D
  60.                 wavWriter.Write(36 + dataSize); // 总文件大小 - 8: L, Y+ j7 `8 G0 n3 w( ^4 D
  61.                 wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));- Q) j/ N7 ?4 ^! c' D4 ~
  62.                 wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));/ @* }/ t. j& t. C9 t
  63.                 wavWriter.Write(16); // fmt块大小
    , V/ k% d) O  f/ ]
  64.                 wavWriter.Write((ushort)1); // 音频格式(PCM)
    ' p% B# Z6 _9 J$ w2 ]6 Q. Y
  65.                 wavWriter.Write(channels); // 声道数* s/ p+ q$ r8 n: ^7 }& j6 {9 s
  66.                 wavWriter.Write(rate); // 采样率* q7 T" S) N# d- A. q
  67.                 wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数4 ^! H  N1 a6 h! C, Z$ `
  68.                 wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数
    $ Z: [/ R7 T: n+ p. u
  69.                 wavWriter.Write(defSoundSize); // 每个样本的位深度4 f5 l% Y4 V$ z1 D) O, c0 h
  70.                 wavWriter.Write(Encoding.ASCII.GetBytes("data"));  m( V' R/ |- g
  71.                 wavWriter.Write(dataSize); // 数据大小! e- [& |2 q5 U! t; k# }

  72. ; I" ]0 o9 {4 c* L6 C
  73.                 // 从.rl2文件中读取并写入PCM音频数据. {7 y5 q2 u$ k2 c! {
  74.                 for (int i = 0; i < numFrames; i++)! \, N. n$ u) `" W
  75.                 {1 W9 p# q; g' t0 O8 e
  76.                     byte[] audioData = reader.ReadBytes((int)soundSize[i]);
    . X4 Z6 h; w5 g* Y) N/ `& J& m
  77.                     wavWriter.Write(audioData);
    $ X1 A" b* V1 P. `+ e; F  Q! q
  78.                 }  g1 s. [7 j! g
  79.             }
    9 J$ S% C& s3 q7 i2 T6 m; ?' `/ ?
  80.         }
    7 x  Z0 s! @: R9 S" P
  81.     }/ e- \9 R- S/ a) o& [
  82. }
    7 h- I( j+ t" _5 p) U5 h
  83.   P. k5 O7 f( \, I- o
  84. class Program
    1 l, K; _( k; p0 j4 [3 J! k
  85. {
    . v) P- a) r3 E, |. w6 p& B: B
  86.     static void Main(string[] args)
    * D. m1 h) ^% E
  87.     {6 [5 B! u% Y0 X) ?# k4 f: W
  88.         string inputFile = "N1275510.RL2";) q' C. G' {5 a* t. Q. k3 Z% A, I3 R
  89.         string outputFile = "output.wav";- }: b% Y% W5 m; Q
  90.         RL2ToWavConverter.ConvertToWav(inputFile, outputFile);( y# @* S; Q! c7 N
  91.         Console.WriteLine("转换完成。");
    0 x+ L1 y/ D, r5 m  k3 ?
  92.     }# u& J* `5 A1 z; p2 Z
  93. }# ^6 A2 X; {& f
复制代码
" R7 Q+ a& b" r$ u

* |+ j% L% b# F7 v9 a! j, s1 A8 g$ g1 U! ~# R8 x
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 很美好很美好1 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2025-4-4 01:08 | 只看该作者
学习学习一下
回复 支持 反对

使用道具 举报

高级模式
B Color Image Link Quote Code Smilies

本版积分规则

冒险解谜游戏中文网 ChinaAVG

官方微博官方微信号小黑屋 微信玩家群  

(C) ChinaAVG 2004 - 2019 All Right Reserved. Powered by Discuz! X3.2
辽ICP备11008827号 | 桂公网安备 45010702000051号

冒险,与你同在。 冒险解谜游戏中文网ChinaAVG诞生于2004年9月9日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

快速回复 返回顶部 返回列表