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

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

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

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

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

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

本帖最后由 shane007 于 2023-9-2 00:06 编辑 3 a5 y. P/ w4 N
% \  e) d8 ^; V4 a' `6 H$ z" ~7 ~3 g
该游戏是scummvm支持的少数几款FMV AVG。
& e$ T# y" l/ x0 m! ]6 x, P3 v5 S2 w视频采用了一种叫做RL2的格式。/ t' w7 z4 F( W$ r
参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,
% T& L' o! e" R( U7 Z  {然后用whisper语音识别之后就能配上字幕了。4 o" K# k. P$ ~7 s6 \, i/ n. ^
此外,rl2格式用potplayer也能直接播放。
2 Q# o% }( E  L* Y+ d3 ~2 m# U! u. a8 S& k! {
文件格式3 }/ P5 G9 A9 _$ {6 C* @% S
https://wiki.multimedia.cx/index.php/RL2; |1 F2 O. o/ L+ f; \
  1. 4 w- d/ r( l- |4 o* L
  2. +  0  dword  Header                -- "FORM"+ A( p  h  ]% ?6 J! }( ]
  3. +  4  dword  BackSize              -- size of the background frame
    9 h8 [2 r- O1 h/ @9 M
  4. +  8  dword  Signature             -- "RLV2" or "RLV3"
    : t( o/ h) Z( x. |: s  \8 ^
  5. +  C  dword  DataSize              -- size of the data past this point BIG-ENDIAN  y2 W1 l8 [# G$ I% A. L# ^
  6. + 10  dword  NumFrames             -- number of frames
    & Q) }, k2 h  g+ y* p
  7. + 14  word   Method                -- encoding method. ignored, zero1 x' R  S+ S& C1 [1 h
  8. + 16  word   SoundRate             -- sound sample rate in some obscure format. zero means no sound
    $ H& H" u  f) ~& w. w, X" k
  9. + 18  word   Rate                  -- sound sample rate in Hz
    * U3 S& Y, a, C- q! o, i* m
  10. + 1A  word   Channels              -- number of sound channels* d, Q$ _$ l1 e) F
  11. + 1C  word   DefSoundSize          -- size of the single sound chunk. see notes below+ }0 ^. X" X8 m
  12. + 1E  word   VideoBase             -- initial drawing offset within 320x200 viewport
    / O! L6 v& Y( S
  13. + 20  dword  ClrCount              -- number of used colors
    1 `- H7 ^! _! D  Z  d$ T
  14. + 24  RGB    Palette[256]          -- 256 RGB triplets. full 8-bit entries
    9 N* N; V  Q/ G; c( w0 W
  15. -- if Signature == "RLV3" AND BackSize <> 0- M+ D' Q# O( ]+ e  r) }* P  T
  16.   +324 byte  BackFrame[BackSize]   -- encoded background frame. ignore VideoBase during it's decoding2 V& [" O' o, _3 Y  ?
  17. --
    5 z2 l' u  G5 Y" b
  18. +xxx  dword  ChunkSize[NumFrames]  -- complete size of the chunk for each frame (audio+video)
    . O, Z# Q! s) U+ e: B0 i: K
  19. +yyy  dword  ChunkOffs[NumFrames]  -- offset of the each frame chunk from the start of file8 {0 j" r$ H0 r6 w; s
  20. +zzz  dword  SoundSize[NumFrames]  -- size of the audio portion in the frame chunk- b% D" W, x5 R; d
  21. -- for each frame --$ ]/ X  m( ?5 s6 _1 X4 J
  22. +xxx  byte   Audio[SoundSize[frame_nr] & 0xFFFF]  -- raw 8-bit audio
    8 @% o3 y5 A$ h7 o8 H8 h8 ?& n
  23. +yyy  byte   Video[...]                           -- compressed video stream
复制代码
/ Q+ S. y+ y' D2 X
参考代码(有问题,但可参考)
  1. using System;! B4 x  {/ L* n' g
  2. using System.IO;) _& J1 j, ~7 V1 D6 G! u6 `4 @
  3. using System.Text;
    * h; t. y. C6 ]

  4. % N+ ?* [5 u% n8 }9 a& s7 C0 H9 P
  5. public class RL2ToWavConverter
    8 a# l# L8 Y9 [- D
  6. {& ^+ M& |7 `6 y/ i: A# Z8 }
  7.     public static void ConvertToWav(string inputFile, string outputFile)
    5 {* V3 `2 J  L, @+ G5 f  @
  8.     {
    9 F8 y2 h# `+ H- M: ?7 [( |; r$ Z
  9.         using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))" U5 V2 R5 p% `: B! h
  10.         using (BinaryReader reader = new BinaryReader(fs))
    7 l2 @0 S2 T4 O, t
  11.         {
    4 w6 c4 w! _' b( c
  12.             // 读取头部
    * j. p. i% e- [
  13.             string header = Encoding.ASCII.GetString(reader.ReadBytes(4));1 h2 Q$ P' n' M5 Q, m8 i4 I! n
  14.             if (header != "FORM")3 c. `  D& U$ q6 k6 _+ Y
  15.             {
    3 d, U% u. a3 o" l
  16.                 Console.WriteLine("无效的.rl2文件格式。");
    + n3 Q: X+ v+ Y  y
  17.                 return;
    6 O  w' ^+ s' @: N9 u7 J
  18.             }
    7 h  h# _6 o/ J
  19. ! \: j1 E! b; a3 v
  20.             uint backSize = reader.ReadUInt32();5 Q4 V8 [) n* I) i+ I# q0 w
  21.             string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));
    - P8 v2 J% u" m- T- N& f' e
  22.             uint dataSize = reader.ReadUInt32();, I, Z  g9 @0 d
  23.             uint numFrames = reader.ReadUInt32();8 G8 r" \9 L/ i) f
  24.             ushort method = reader.ReadUInt16();
    2 M& w. A: m( B9 H
  25.             ushort soundRate = reader.ReadUInt16();: m4 s+ R( s5 B. e# W9 Y
  26.             ushort rate = reader.ReadUInt16();
    * o0 V. _* m! C' F& q
  27.             ushort channels = reader.ReadUInt16();- w: E5 F9 I/ t  Y/ O! p+ ^0 O# s
  28.             ushort defSoundSize = reader.ReadUInt16();
    # A# [/ i& n" V/ C; A- ^3 _* M
  29.             ushort videoBase = reader.ReadUInt16();5 ?; M* X, V9 T- B% f
  30.             uint clrCount = reader.ReadUInt32();
    3 ]) u7 {& t: d9 S
  31.             uint[] chunkSize = new uint[numFrames];
    ' n9 m2 T8 H4 N$ `
  32.             uint[] chunkOffs = new uint[numFrames];' b( I  X9 y4 T3 S% W% D
  33.             uint[] soundSize = new uint[numFrames];! U+ z4 b# q+ G6 \

  34. : p, Y8 u7 _. I* p. M+ h
  35.             if (signature != "RLV2" && signature != "RLV3")- s; W# l2 _4 }2 c: ]
  36.             {
    5 r; g6 _4 e. F/ }- U) t
  37.                 Console.WriteLine("不支持的签名。");9 n9 X) K. M# J  q8 o) ?9 d3 w" ^! e7 x
  38.                 return;
    ! [. b- U3 Q. m1 ]9 O2 p1 |
  39.             }
    ( h/ N- \4 f: e6 h! r  a

  40. 5 U; M& t- p' x& i
  41.             // 读取块信息
    ) I. M7 G* A  i2 Z0 L; ~
  42.             for (int i = 0; i < numFrames; i++)8 W" f! S8 Y; k6 G9 F
  43.             {
    3 _: _' `8 }7 f4 p; X
  44.                 chunkSize[i] = reader.ReadUInt32();
    ' p2 j" `' U; K1 l! q; R
  45.                 chunkOffs[i] = reader.ReadUInt32();0 z3 h( @; W+ w
  46.                 soundSize[i] = reader.ReadUInt32();
    ) g0 }! `& v, e
  47.             }0 @4 j6 t- _8 W6 @
  48. * |5 h& b  R$ j9 U
  49.             // 如果存在背景帧,请跳过它, g: m8 S# r* ^+ [5 Z) `( n. t( n
  50.             if (signature == "RLV3" && backSize != 0)
    $ K3 v, h* X: \
  51.             {: R# m; o6 |3 [! }) p5 c- c( O
  52.                 reader.BaseStream.Seek(backSize, SeekOrigin.Current);8 K- Y. H& z# M3 B! F  o1 q
  53.             }
    7 Q' T8 J/ I* m
  54. : l+ {% x+ j  L  K' ]3 n# W
  55.             // 创建一个WAV文件并写入音频数据
    $ Z' O, \. s! }
  56.             using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))
    ( L7 M7 M. |$ I9 D+ A. k$ i# O. c+ A
  57.             {8 ]6 v- h& X! t+ |/ W6 ]9 g( f- ?- m
  58.                 // 写入WAV头部/ k4 W8 L0 I1 U4 P( b' P' _  q
  59.                 wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));
    6 [2 Z" a1 f6 V2 K! n- {; F( K
  60.                 wavWriter.Write(36 + dataSize); // 总文件大小 - 8
    + ]2 g  i- \" C8 b! e7 l
  61.                 wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));+ W6 _9 P* V4 ?5 F' c
  62.                 wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));  F9 \7 H; w  ^/ p5 ?
  63.                 wavWriter.Write(16); // fmt块大小$ i5 x9 a+ x$ H! J# W2 Y5 T. m
  64.                 wavWriter.Write((ushort)1); // 音频格式(PCM)7 E( _; Q8 y" t3 m2 y# ?
  65.                 wavWriter.Write(channels); // 声道数7 b3 u/ o9 M! \( A& ?4 I3 I
  66.                 wavWriter.Write(rate); // 采样率
    0 n: h) j) b0 n7 J/ J
  67.                 wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数5 u  G7 u1 {: |/ j1 X/ m% @0 r
  68.                 wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数
    % @& u4 y8 f. {
  69.                 wavWriter.Write(defSoundSize); // 每个样本的位深度# W5 |" |0 J. G7 _8 Y7 E
  70.                 wavWriter.Write(Encoding.ASCII.GetBytes("data"));
    . e4 O: M5 e: T2 T) Q
  71.                 wavWriter.Write(dataSize); // 数据大小& p3 b1 O, U6 O' Q* {0 i. a' k

  72. 6 b, @, }6 I7 w' g2 I
  73.                 // 从.rl2文件中读取并写入PCM音频数据
    5 q1 Q( }6 U) g, _+ A
  74.                 for (int i = 0; i < numFrames; i++)
    4 D& h/ O8 I+ T- B1 X
  75.                 {
    3 V% m7 ?, L" Y
  76.                     byte[] audioData = reader.ReadBytes((int)soundSize[i]);. E& ~+ G1 _0 }9 P
  77.                     wavWriter.Write(audioData);
    ' e: A# u1 F! }
  78.                 }
    ; i! E# J4 `+ k
  79.             }
    7 F7 ~. c- }- K8 R: L  o  Y
  80.         }
    , Z: I1 V! l% V
  81.     }
    % h3 L1 ?' V6 P# o0 f+ L! t8 x' k
  82. }: y. U2 \2 ~! G3 i3 K% C
  83. / V- y8 p4 A8 |" ~9 l
  84. class Program9 c/ `8 Q) b2 c/ ?5 {" g
  85. {/ X( I, x: j+ d! o5 a6 V" {7 q
  86.     static void Main(string[] args)
    ! h6 e9 e4 d' i, n- ], v* t
  87.     {
    ! D% y2 r5 D- e9 v
  88.         string inputFile = "N1275510.RL2";9 o5 Z* }+ \2 g1 U4 s3 B# C" ]
  89.         string outputFile = "output.wav";1 s3 X6 C1 H/ D6 i0 p
  90.         RL2ToWavConverter.ConvertToWav(inputFile, outputFile);3 y9 m1 r# E9 u8 @2 y9 R
  91.         Console.WriteLine("转换完成。");+ ]/ t$ V( Z) o. S% B. w: i# _6 P
  92.     }
    3 o! E& C5 ~9 _9 m9 @3 \
  93. }
    3 @; t& t3 y: P* f. u" }
复制代码

1 ?' p9 Z9 Y* c8 V9 t1 N, U0 g9 \8 l; B
$ |- C3 D$ b; B9 I' |
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

高级模式
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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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