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

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

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

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

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

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

本帖最后由 shane007 于 2023-9-2 00:06 编辑 4 @4 \: M2 n! d3 k# W" N
$ [7 `6 z& W' r1 n9 I3 B3 s9 [
该游戏是scummvm支持的少数几款FMV AVG。# }6 t. D+ T6 x6 I4 o" [" j
视频采用了一种叫做RL2的格式。
# C7 E. ]. S( h: ^9 x参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,
( A- n2 J7 M# W7 w$ Q. Y8 Q6 F1 `) H然后用whisper语音识别之后就能配上字幕了。
: V) H; o( L; o- K此外,rl2格式用potplayer也能直接播放。
- P0 r: Z; O9 m" k2 k, W& n6 p, }" I2 w- l( y
文件格式
4 ^( Q1 E) Z6 C# chttps://wiki.multimedia.cx/index.php/RL2
$ H1 C- @8 _! o& G  G) A5 A

  1. 8 D! c8 G! r" P! C
  2. +  0  dword  Header                -- "FORM"
    0 _) R7 \  h( t, M, D3 m6 q
  3. +  4  dword  BackSize              -- size of the background frame" j$ P3 q/ M- S
  4. +  8  dword  Signature             -- "RLV2" or "RLV3"3 U: U* n4 f  p6 q1 {
  5. +  C  dword  DataSize              -- size of the data past this point BIG-ENDIAN' B0 b; |  Z1 v. w% U  R
  6. + 10  dword  NumFrames             -- number of frames
    2 Z" \% a8 O+ M; T
  7. + 14  word   Method                -- encoding method. ignored, zero4 Y# j+ Y1 `8 U
  8. + 16  word   SoundRate             -- sound sample rate in some obscure format. zero means no sound! k0 k$ l& r1 q3 S+ n
  9. + 18  word   Rate                  -- sound sample rate in Hz# t) T8 g7 J; s
  10. + 1A  word   Channels              -- number of sound channels
    5 F2 ]8 n! q0 D# t. w( ~
  11. + 1C  word   DefSoundSize          -- size of the single sound chunk. see notes below
    + ^4 ?; E7 ~. _' B& S
  12. + 1E  word   VideoBase             -- initial drawing offset within 320x200 viewport
    1 e# E0 t& `0 d' {* o1 M2 f7 P9 `' W
  13. + 20  dword  ClrCount              -- number of used colors" q- ]1 N4 Z  h( R" ?# o+ R' ^
  14. + 24  RGB    Palette[256]          -- 256 RGB triplets. full 8-bit entries
    - l" U" a. X$ v8 E
  15. -- if Signature == "RLV3" AND BackSize <> 05 t" x: ^. a; f1 v9 ?2 Q
  16.   +324 byte  BackFrame[BackSize]   -- encoded background frame. ignore VideoBase during it's decoding
    1 c+ p& l. L, a6 z8 m( M0 c0 M$ Q0 Q3 s9 \
  17. --! N; i$ F( a( z" I+ Z3 _" n
  18. +xxx  dword  ChunkSize[NumFrames]  -- complete size of the chunk for each frame (audio+video)
    8 Q% N7 G+ B7 @
  19. +yyy  dword  ChunkOffs[NumFrames]  -- offset of the each frame chunk from the start of file, }% n4 `) H+ j2 `: E
  20. +zzz  dword  SoundSize[NumFrames]  -- size of the audio portion in the frame chunk) l! I$ p& P3 v
  21. -- for each frame --
    ; j" ?* {% C, a  O7 A
  22. +xxx  byte   Audio[SoundSize[frame_nr] & 0xFFFF]  -- raw 8-bit audio
    - h: C0 w- }8 p& F9 c2 [$ c! \
  23. +yyy  byte   Video[...]                           -- compressed video stream
复制代码

2 M, ?6 }: g( ]/ o1 @! K参考代码(有问题,但可参考)
  1. using System;
    " `; L  U$ y. C" `
  2. using System.IO;% M8 |6 {/ |5 @
  3. using System.Text;
    8 \* S2 V5 B$ _7 A+ f

  4. + f) X* O3 O( g
  5. public class RL2ToWavConverter
    6 G5 E! p, r! B' F" H7 k
  6. {
    " E! i/ \2 ?1 G) h2 M: D9 R
  7.     public static void ConvertToWav(string inputFile, string outputFile)5 H" M, F) Z3 ]! O8 @
  8.     {
    - F& X! w( U1 T8 h) n9 S. o
  9.         using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))/ ~8 [& Z5 V* X9 k9 l$ ?* ^! [1 L
  10.         using (BinaryReader reader = new BinaryReader(fs))) n2 g1 u+ q9 v& t6 \
  11.         {- o  T0 t: W5 i% ]' @
  12.             // 读取头部# o+ b" \! o7 a
  13.             string header = Encoding.ASCII.GetString(reader.ReadBytes(4));4 H: f4 L! t# @$ F, Q) p: r* t
  14.             if (header != "FORM"); @. Y2 C& r/ R
  15.             {7 e9 T8 a; x6 j: b5 a5 e, y6 w6 {$ @3 l7 r
  16.                 Console.WriteLine("无效的.rl2文件格式。");
    1 g$ X$ x( T$ J. D% s! i
  17.                 return;% N  O! X9 W. V) i
  18.             }( M( R1 K: |; c
  19. 8 c4 X0 A' x, q$ Z7 ^
  20.             uint backSize = reader.ReadUInt32();+ K2 ~- k" R8 _5 {0 R
  21.             string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));. v. }& t2 v4 k
  22.             uint dataSize = reader.ReadUInt32();- d7 t& `  F" H: C: ?* \3 U9 ?
  23.             uint numFrames = reader.ReadUInt32();1 i* [* W8 R1 ]: X$ q
  24.             ushort method = reader.ReadUInt16();& j' ]/ _+ |6 G  L$ c
  25.             ushort soundRate = reader.ReadUInt16();
    ; C1 |2 v4 m- V8 y: r
  26.             ushort rate = reader.ReadUInt16();
    1 T: i. G) j! a& M
  27.             ushort channels = reader.ReadUInt16();" M# r# {: E4 ?7 t! [" n0 F. V
  28.             ushort defSoundSize = reader.ReadUInt16();
    + Q% |) e% \% Y
  29.             ushort videoBase = reader.ReadUInt16();& f0 P; N. |4 |- G1 }
  30.             uint clrCount = reader.ReadUInt32();
    $ s6 W& v; @& h. {; k* B
  31.             uint[] chunkSize = new uint[numFrames];* O* G  B6 V' K+ i  V1 p5 b
  32.             uint[] chunkOffs = new uint[numFrames];0 m* Z. g  U. I2 x+ \
  33.             uint[] soundSize = new uint[numFrames];
    # K+ J5 e) r$ t: ]6 C: Q) M1 S
  34. : W6 h6 ^: Z% h3 g
  35.             if (signature != "RLV2" && signature != "RLV3")# H3 @. x0 T4 m8 D
  36.             {- O  `& n; c$ M
  37.                 Console.WriteLine("不支持的签名。");/ {; A) ~1 k, u: }, A
  38.                 return;5 r& ]& Z( r/ T4 W
  39.             }( x' v5 L' }% T$ t( F

  40. ) ^8 J1 [0 C4 ]1 ^
  41.             // 读取块信息
    " g% }2 L3 t8 L2 M( d: ^
  42.             for (int i = 0; i < numFrames; i++)7 V) \7 Q& w$ T
  43.             {( m6 l$ z/ o  h
  44.                 chunkSize[i] = reader.ReadUInt32();7 f- ]& I7 m: m6 s+ Z* b0 Y
  45.                 chunkOffs[i] = reader.ReadUInt32();9 _# l& p1 b7 e  {8 m+ q2 i8 b
  46.                 soundSize[i] = reader.ReadUInt32();% A+ Q6 O/ W7 ~; m5 c
  47.             }, N' p: x. w0 z* h4 h  O

  48. 6 J* C$ P' X0 i2 Y6 {2 a7 j
  49.             // 如果存在背景帧,请跳过它6 q7 V2 o8 Q" `: K4 f0 B) ~. D" x
  50.             if (signature == "RLV3" && backSize != 0)
    ( I" S8 G% b. P* e( F( V  n# c0 ]
  51.             {9 V& {) \: U. ?' J
  52.                 reader.BaseStream.Seek(backSize, SeekOrigin.Current);% z# H8 n8 q! e" d) I* X, Z
  53.             }: u+ P5 e7 ^( h/ b

  54. ; Y3 m0 n) Q# k/ W3 \# j
  55.             // 创建一个WAV文件并写入音频数据! ?8 F" A3 e$ r: V
  56.             using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))
    $ \" M  V2 k, n8 M9 r+ K
  57.             {+ b) v2 _) ~7 x4 V1 Y- h
  58.                 // 写入WAV头部6 C  p: o) K5 B) N# D( q3 \
  59.                 wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));
    " I- z# ]* Q9 e" c
  60.                 wavWriter.Write(36 + dataSize); // 总文件大小 - 8
    % A4 s  L) Q9 v; ~7 w0 p
  61.                 wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));9 H# O: q) f2 G; x
  62.                 wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));
    " r( i6 m0 K* T# x5 a
  63.                 wavWriter.Write(16); // fmt块大小
    , ]1 P- `# @1 H+ c2 g& A. B+ \5 ~
  64.                 wavWriter.Write((ushort)1); // 音频格式(PCM)) ?, r. e; z# u5 U: M  f$ c5 ^
  65.                 wavWriter.Write(channels); // 声道数
    , X. I* h# Z/ b" }! x( z4 I0 l2 E
  66.                 wavWriter.Write(rate); // 采样率- q7 Q/ \5 s( _3 L* s! A5 y8 o
  67.                 wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数
    8 l8 n! _( @3 J# x1 _! v7 H
  68.                 wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数
    & g- P4 Y/ d8 q4 T; ?) N
  69.                 wavWriter.Write(defSoundSize); // 每个样本的位深度( N# a- W1 m0 j) P# q3 l
  70.                 wavWriter.Write(Encoding.ASCII.GetBytes("data"));) r0 u+ ?4 c( f8 l; ~, w/ d
  71.                 wavWriter.Write(dataSize); // 数据大小. ^$ E7 N4 }) {. B$ c4 B! ^) P) e/ n

  72. ' E! f; o1 i; `# C8 `6 a
  73.                 // 从.rl2文件中读取并写入PCM音频数据
    8 I- S/ c8 n; U# f8 x; k! V
  74.                 for (int i = 0; i < numFrames; i++)
    ! [( I- C# }1 |
  75.                 {
    ' Y$ a" ?6 G6 Y& U' w
  76.                     byte[] audioData = reader.ReadBytes((int)soundSize[i]);
    , m0 L* }6 x5 ^
  77.                     wavWriter.Write(audioData);  ^# `! [9 G+ D# p' e
  78.                 }8 {4 g4 o% n5 Z6 q* J, S. t) x
  79.             }
    # J! l  m/ k( p  e% z! Q
  80.         }
    9 X% V. q1 G. w9 r/ I5 `. n
  81.     }
    - G0 k- [" b9 t
  82. }
    ) p; D" d; N( U4 l7 O9 B9 N" O% ^1 T
  83. 2 c! I, @& K* X) C  F" X
  84. class Program
    8 R: a! L& O6 ?
  85. {
    2 H/ \! c+ q5 S5 v3 H
  86.     static void Main(string[] args)
    ! B* T' V( H8 {; ^6 N+ u% y! H7 H
  87.     {  x: f9 h. Y2 ]1 \, r$ z, q1 p* Q. j7 i
  88.         string inputFile = "N1275510.RL2";, L- B; ^5 |6 R% n! f, b
  89.         string outputFile = "output.wav";/ R8 A% D& p7 N$ s; }0 e
  90.         RL2ToWavConverter.ConvertToWav(inputFile, outputFile);
    $ F1 z( j" O" s2 {, s
  91.         Console.WriteLine("转换完成。");
    ; v! M/ p* u" |' d/ C. T% b. a& i
  92.     }: q2 J8 ~& Z9 ^* X% t" L9 A
  93. }! N0 _' {6 |% I& m: ~! h
复制代码
, C9 |6 Q: m9 H7 ~3 X

1 }8 b# `% U* \5 U) H0 l8 Z- q& z3 W. y8 i2 j( a5 b
分享到:  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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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