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

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

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

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

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

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

本帖最后由 shane007 于 2023-9-2 00:06 编辑
, o5 Y% ~$ K5 q) p7 J, Y0 H) K" O* l
该游戏是scummvm支持的少数几款FMV AVG。
: ?( y; ]$ d& N. |视频采用了一种叫做RL2的格式。, e# j" Q# y6 A1 Y  Y' k
参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,
. ^) X6 b" V# }+ A+ l! k然后用whisper语音识别之后就能配上字幕了。
) J( Y. U4 b1 o, m3 `此外,rl2格式用potplayer也能直接播放。
  E! L; H' p1 Z5 ~( h- _! D' z! ~' Z
文件格式
9 i- z% n2 ]# n; k  c( Ghttps://wiki.multimedia.cx/index.php/RL2
: g: v  h* O; H) h5 u
  1. - V; [' m0 ~; V: @6 S; X
  2. +  0  dword  Header                -- "FORM"! ]5 w2 `: G# |# Q3 i( A
  3. +  4  dword  BackSize              -- size of the background frame! {, M% O8 k# b$ j# a+ A7 x
  4. +  8  dword  Signature             -- "RLV2" or "RLV3"
    & V8 @# `1 J9 [9 @0 m
  5. +  C  dword  DataSize              -- size of the data past this point BIG-ENDIAN- ?( m0 e* f1 v, T/ _: _9 _5 d
  6. + 10  dword  NumFrames             -- number of frames: C+ T) o' v  B
  7. + 14  word   Method                -- encoding method. ignored, zero
    " o6 Q" _& R' `5 k6 K+ Q; N
  8. + 16  word   SoundRate             -- sound sample rate in some obscure format. zero means no sound, w" M% {8 Y3 E4 }% b: x
  9. + 18  word   Rate                  -- sound sample rate in Hz
    . E+ S: Q, e8 L) D9 M  K( n' v
  10. + 1A  word   Channels              -- number of sound channels
    ( B8 C' `$ q$ ?  L
  11. + 1C  word   DefSoundSize          -- size of the single sound chunk. see notes below
    . I' D. H, b% `5 f5 F/ W4 k3 ~
  12. + 1E  word   VideoBase             -- initial drawing offset within 320x200 viewport; K1 }+ @5 x+ N' P. v9 ]# G
  13. + 20  dword  ClrCount              -- number of used colors
    6 n, h9 x4 }' M
  14. + 24  RGB    Palette[256]          -- 256 RGB triplets. full 8-bit entries
    2 w4 v; n. h. P
  15. -- if Signature == "RLV3" AND BackSize <> 00 b  a/ x* y* _' n% [) {
  16.   +324 byte  BackFrame[BackSize]   -- encoded background frame. ignore VideoBase during it's decoding
    & w7 [- j7 @; s/ Y3 y
  17. --
    / B9 L" q. f( s" }
  18. +xxx  dword  ChunkSize[NumFrames]  -- complete size of the chunk for each frame (audio+video); W) Z3 Y8 @: t
  19. +yyy  dword  ChunkOffs[NumFrames]  -- offset of the each frame chunk from the start of file
    , f$ D) A! E5 p7 s* ]
  20. +zzz  dword  SoundSize[NumFrames]  -- size of the audio portion in the frame chunk
    / u- v" x; n. Z4 b9 ?  v( D! U4 X
  21. -- for each frame --
    ! E" |, D8 J1 u' y7 ?5 Z
  22. +xxx  byte   Audio[SoundSize[frame_nr] & 0xFFFF]  -- raw 8-bit audio
    6 _: o, s, J! b, J+ s4 M# s. t
  23. +yyy  byte   Video[...]                           -- compressed video stream
复制代码
; @( P: V  W6 c/ |4 l
参考代码(有问题,但可参考)
  1. using System;& g# b  P! x  t8 N( ^/ r
  2. using System.IO;
    1 l! w9 @8 c* s4 h9 Q
  3. using System.Text;& v4 M0 p8 H& }; T4 r2 y
  4. / J$ [/ ]. z! e" p' r7 ]2 T2 a
  5. public class RL2ToWavConverter. n: g& V2 _6 U
  6. {; I6 Y0 H4 x" [( {7 J
  7.     public static void ConvertToWav(string inputFile, string outputFile)
    9 h8 F4 Z  Z0 @/ @
  8.     {
    " b7 U4 g$ [" u& ]# i2 c. B/ a
  9.         using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
    1 L4 H% Z* t. g7 A0 J  A
  10.         using (BinaryReader reader = new BinaryReader(fs))7 l+ R0 i2 {( c) W7 r% Q9 Z
  11.         {
    - Y! n: |* _8 h1 ]
  12.             // 读取头部
    8 Q0 w7 u. q$ S9 c
  13.             string header = Encoding.ASCII.GetString(reader.ReadBytes(4));
      }! U  y3 c4 j- m; {: w0 @
  14.             if (header != "FORM")$ N5 c) P/ W2 [$ x/ m  ^4 y
  15.             {
    % A9 H9 G) t( r5 Q2 q. s3 L
  16.                 Console.WriteLine("无效的.rl2文件格式。");7 c) _. Q; m0 c0 p9 `
  17.                 return;
    . }& @7 \# `* k1 S
  18.             }: S/ U/ ^- H' H5 `

  19. ) U! K: x/ |  a! e, e- L/ E: z
  20.             uint backSize = reader.ReadUInt32();
    & L4 U: I7 \4 [. o4 R( f
  21.             string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));
    + s0 v8 b' a  x0 u% o
  22.             uint dataSize = reader.ReadUInt32();
      `+ b( y5 d% b* h% O  S& T5 V- N
  23.             uint numFrames = reader.ReadUInt32();& i/ @* L" A: m- K5 |# R. l* H
  24.             ushort method = reader.ReadUInt16();( M/ Z' a8 d; K% t  O9 |3 E
  25.             ushort soundRate = reader.ReadUInt16();/ N- t1 i( @2 a( C1 e
  26.             ushort rate = reader.ReadUInt16();( z% }6 I, K& x& }- Q
  27.             ushort channels = reader.ReadUInt16();
    % V% b4 `4 X, X+ r/ o
  28.             ushort defSoundSize = reader.ReadUInt16();! c# w, m7 y+ {+ u
  29.             ushort videoBase = reader.ReadUInt16();
    1 N  G( k3 Z( f0 e1 ?
  30.             uint clrCount = reader.ReadUInt32();) h. D% ^! u2 w8 _% S. |8 y
  31.             uint[] chunkSize = new uint[numFrames];1 _7 M/ x& H& o: V5 V+ W) {
  32.             uint[] chunkOffs = new uint[numFrames];, A. b0 M) ]9 H9 r* I2 }  G
  33.             uint[] soundSize = new uint[numFrames];
    ! I5 q& A4 \% D# q7 b2 M
  34. 3 g7 l' W4 ?# F3 \4 x' U2 ^, V0 U
  35.             if (signature != "RLV2" && signature != "RLV3")9 u$ j/ z4 a) t# E" e
  36.             {) a' V, I; d( X% }7 j. m: k1 [
  37.                 Console.WriteLine("不支持的签名。");
    ; a0 ]2 D5 E, d+ z+ r3 y
  38.                 return;
    % y) P2 {' m9 {* T' {; x
  39.             }
    " c. {$ g# _/ |

  40. % Q1 f& \$ k( f3 G3 v8 D
  41.             // 读取块信息
    . R) R# v0 v1 H' M
  42.             for (int i = 0; i < numFrames; i++)( H) K! u( O3 i( E
  43.             {
    1 c+ C( s4 l& ?) H
  44.                 chunkSize[i] = reader.ReadUInt32();& N! }8 V9 L6 K/ M8 [7 Z5 V! ]
  45.                 chunkOffs[i] = reader.ReadUInt32();8 ]- P* Q# y3 `& V& v6 n
  46.                 soundSize[i] = reader.ReadUInt32();
    5 r7 `+ G. b! Q
  47.             }
    # D; B& _+ z+ G
  48. 2 S9 J9 m8 `5 C% z* T* u" e
  49.             // 如果存在背景帧,请跳过它
    8 L' V6 d0 h! K; U1 E/ U+ `; K8 e
  50.             if (signature == "RLV3" && backSize != 0)% @" O0 S' _3 q, g- f
  51.             {
    5 _# q1 o1 l/ C$ F# t
  52.                 reader.BaseStream.Seek(backSize, SeekOrigin.Current);+ [5 h1 D# q( R9 Q! K1 V. s9 k
  53.             }
    0 q. A/ i6 O/ x4 h
  54. , p% k: b6 _- ?7 R( `
  55.             // 创建一个WAV文件并写入音频数据
    7 g+ Q& e1 `. B
  56.             using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))
    # u8 r' L6 e2 A8 v+ Z
  57.             {
    : b- m# S# o6 P
  58.                 // 写入WAV头部
    7 a) B1 j' a( k; [
  59.                 wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));+ i% ]( R- |1 ~# V* b: a3 m$ w
  60.                 wavWriter.Write(36 + dataSize); // 总文件大小 - 8
      P' U& I* G- A- ^% m! h
  61.                 wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));+ t. ~8 b1 @% v- C) ]# k. k
  62.                 wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));
    & ~$ `# V1 a0 z/ ^5 K. i
  63.                 wavWriter.Write(16); // fmt块大小/ u% S8 m2 T9 o+ a9 o2 M0 a
  64.                 wavWriter.Write((ushort)1); // 音频格式(PCM)
    1 [: \( Z4 C' P3 c$ h0 h
  65.                 wavWriter.Write(channels); // 声道数$ j8 |- f0 j( a+ V3 P9 Q  \' m+ {
  66.                 wavWriter.Write(rate); // 采样率
    ) D. V8 Y4 w0 t8 Y& v% T
  67.                 wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数2 q1 e5 W) T( V* T) }) q
  68.                 wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数
    0 V0 u6 Z" g- J1 i5 G- A
  69.                 wavWriter.Write(defSoundSize); // 每个样本的位深度
    . o0 e6 e: C; v7 m% Z8 t- Q
  70.                 wavWriter.Write(Encoding.ASCII.GetBytes("data"));* `2 q" t, k8 @7 k& i, |
  71.                 wavWriter.Write(dataSize); // 数据大小3 Z1 @* e/ G8 I

  72. , P7 N2 ^. ?- S+ {0 ~" w( I8 W
  73.                 // 从.rl2文件中读取并写入PCM音频数据- G% E$ G8 z9 T( Z1 Q) k. ]& j0 E
  74.                 for (int i = 0; i < numFrames; i++)
    - y, u4 i* z" P$ T6 i7 r: P6 c
  75.                 {
    & b; O9 H) c. p6 C
  76.                     byte[] audioData = reader.ReadBytes((int)soundSize[i]);
    ; k* k6 J' k9 r  n0 D
  77.                     wavWriter.Write(audioData);3 v  Q9 `" B5 Q1 [6 ~( C# T
  78.                 }+ b- V" g& U7 I% t; {/ b2 e% x
  79.             }
    % g) A# P! ]( Y8 }3 Y  b9 s' s
  80.         }
    - m3 `: m  ^+ m8 g2 f
  81.     }* J" m( s. R/ j
  82. }
    # l1 P& R+ r* U* @8 I& P; ^, {- e
  83. 0 G+ m8 K( Z# W
  84. class Program, P2 B2 h' e3 C$ Y* g* P
  85. {6 @9 K9 x  X( v/ T
  86.     static void Main(string[] args)2 w- i" E' \0 n5 }
  87.     {
    & _: X9 o* x& s6 w" \
  88.         string inputFile = "N1275510.RL2";
    ! |2 }1 E8 T. O, N, f& R
  89.         string outputFile = "output.wav";- |1 z3 b) c; f3 F
  90.         RL2ToWavConverter.ConvertToWav(inputFile, outputFile);! [/ N* {; s% m% {% N
  91.         Console.WriteLine("转换完成。");
    2 f( l1 j( O$ U  f! K
  92.     }
    ! X- s8 u% u# b" U4 y' C5 p" z1 l  `& ^
  93. }4 d6 f1 }9 \; \5 j& e" V
复制代码
/ N  Z2 O0 I1 x( |5 P7 |' P
7 x) P8 \* I; n

. U( J1 `6 n0 }& F0 u/ w* T
分享到:  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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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