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

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

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

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

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

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

本帖最后由 shane007 于 2023-9-2 00:06 编辑
  S+ U% r. q0 P, E* U
% x% n2 L! l5 z9 Y! v该游戏是scummvm支持的少数几款FMV AVG。
4 B+ H/ T5 ?+ [$ i8 K视频采用了一种叫做RL2的格式。  `0 N6 S  z2 B8 `
参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,
( \- }3 [* h/ X+ l然后用whisper语音识别之后就能配上字幕了。
6 t  ]. l0 x8 k0 b+ {( @* |0 Y% d此外,rl2格式用potplayer也能直接播放。) m2 Z" p* @0 p7 {+ b
6 f& Y  q( |3 t) T. J' s
文件格式5 Y4 G( U1 o& _2 X
https://wiki.multimedia.cx/index.php/RL2' N; z/ s% O( L/ e$ _. l

  1. # ~* O- }% Q. y0 v) u5 }
  2. +  0  dword  Header                -- "FORM"
    5 {! D; ^+ v6 k
  3. +  4  dword  BackSize              -- size of the background frame
    + c/ Y8 m3 p  X6 C- T
  4. +  8  dword  Signature             -- "RLV2" or "RLV3"
    % T9 B, R  I( C% g" ^  |: S6 c; u* n
  5. +  C  dword  DataSize              -- size of the data past this point BIG-ENDIAN$ ]. {! j6 J+ X: Z  g8 P- U
  6. + 10  dword  NumFrames             -- number of frames! p3 Y( S/ d7 |+ m
  7. + 14  word   Method                -- encoding method. ignored, zero
    , F0 i4 [/ L3 W. H: g
  8. + 16  word   SoundRate             -- sound sample rate in some obscure format. zero means no sound
    4 T- M! O# z5 P  s: K, G- p
  9. + 18  word   Rate                  -- sound sample rate in Hz
    6 {5 G) J1 y/ y, d
  10. + 1A  word   Channels              -- number of sound channels
    7 ^- ?3 \/ L+ `6 h/ x% y7 k
  11. + 1C  word   DefSoundSize          -- size of the single sound chunk. see notes below2 n, E% F' U8 z0 k9 R+ M' g8 J
  12. + 1E  word   VideoBase             -- initial drawing offset within 320x200 viewport1 v4 q0 Z4 H; x. \! z
  13. + 20  dword  ClrCount              -- number of used colors6 R" L9 L  e7 Y' ?: n
  14. + 24  RGB    Palette[256]          -- 256 RGB triplets. full 8-bit entries
    ; H4 ^8 w$ Q, e* j+ Z
  15. -- if Signature == "RLV3" AND BackSize <> 0
    - Z  b  c; u$ z0 N
  16.   +324 byte  BackFrame[BackSize]   -- encoded background frame. ignore VideoBase during it's decoding
    ! H$ L5 Z, V6 ]% J- r
  17. --. W/ }9 d$ f1 q9 j/ V
  18. +xxx  dword  ChunkSize[NumFrames]  -- complete size of the chunk for each frame (audio+video)# V# ?) }* y$ P, N7 y
  19. +yyy  dword  ChunkOffs[NumFrames]  -- offset of the each frame chunk from the start of file( W! \$ g1 \# [0 O  p. r
  20. +zzz  dword  SoundSize[NumFrames]  -- size of the audio portion in the frame chunk" x9 `  @3 R: g  s0 h* o
  21. -- for each frame --6 x' R; Y4 a9 ~: {0 W  A( {
  22. +xxx  byte   Audio[SoundSize[frame_nr] & 0xFFFF]  -- raw 8-bit audio
    ) z) G& M) C# i4 _5 w8 x; }6 P
  23. +yyy  byte   Video[...]                           -- compressed video stream
复制代码
0 p# }# t  K; b3 z2 U% ^, {3 |
参考代码(有问题,但可参考)
  1. using System;6 W$ k3 a2 E, p4 A) \( z; ^  W$ @" H  V
  2. using System.IO;6 [2 \/ g1 `* g; }! w. f" R3 {  u
  3. using System.Text;
    # P! {0 H4 e# C% A' e

  4. " X% i4 A, {" G0 S9 ?+ E
  5. public class RL2ToWavConverter
    , Q* h" y$ d, a& J
  6. {
    , b/ Z8 L! t% L
  7.     public static void ConvertToWav(string inputFile, string outputFile)
      L" V: w5 c& Q! `5 _1 i: l
  8.     {7 }# R5 }& d/ j0 ]; z5 d
  9.         using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read)): B) a; ~3 v9 G$ f- H
  10.         using (BinaryReader reader = new BinaryReader(fs))
    ' J! v* ~2 u. b0 L: \
  11.         {
    9 C: ?5 O( a& m* b- L$ C
  12.             // 读取头部7 P/ C, o/ B% r% K
  13.             string header = Encoding.ASCII.GetString(reader.ReadBytes(4));
    7 r. c: ~5 T+ e8 n$ b. j/ G
  14.             if (header != "FORM")3 E* X: Q' U) b9 j5 {
  15.             {
    8 g: k- a( o- j6 T
  16.                 Console.WriteLine("无效的.rl2文件格式。");
    3 ^" H, K) f: k3 u5 f6 ^/ T
  17.                 return;( m5 w+ A, I- X4 n( Z, y; f+ V/ [9 M
  18.             }  {/ \: s1 p7 x% E4 q  y

  19. . j2 s* Q5 o# ?) j; q
  20.             uint backSize = reader.ReadUInt32();; i( ~4 {& p( R. t% P. p2 c: Q+ F3 e
  21.             string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));& d5 b3 P2 [- t/ W% |) m6 [
  22.             uint dataSize = reader.ReadUInt32();
    3 \# i. `9 @* b; q
  23.             uint numFrames = reader.ReadUInt32();
    4 E+ h6 n: ~8 A  ^: i" U! \
  24.             ushort method = reader.ReadUInt16();
    9 x4 T0 A; g/ t% A8 j7 F- F1 [5 B7 Q
  25.             ushort soundRate = reader.ReadUInt16();1 C+ z/ T' p7 `3 C
  26.             ushort rate = reader.ReadUInt16();
    " n: ~/ ^7 {7 ~& o  O
  27.             ushort channels = reader.ReadUInt16();
    ' [$ o! N0 a* o, g# K
  28.             ushort defSoundSize = reader.ReadUInt16();) j: Q. J) x' u- M, Z
  29.             ushort videoBase = reader.ReadUInt16();# ~7 U& h6 o1 E& A0 W% U( |
  30.             uint clrCount = reader.ReadUInt32();
    4 t  \) K& B8 S) J5 y$ d
  31.             uint[] chunkSize = new uint[numFrames];
    0 R1 `* M0 ?3 o" w$ D# Y
  32.             uint[] chunkOffs = new uint[numFrames];* L* p2 G* R; @: H% S+ G, t: m' q
  33.             uint[] soundSize = new uint[numFrames];; g- Y( }; _& Z

  34. * |7 O8 K" O$ \+ o+ f
  35.             if (signature != "RLV2" && signature != "RLV3")
    * C6 K% n8 }% [* C4 j4 ^
  36.             {8 j- c1 M7 U1 ]) V, H# G0 X
  37.                 Console.WriteLine("不支持的签名。");
    4 y) R1 f5 m; |0 v4 W4 W2 u$ u0 W
  38.                 return;: l( L! [5 O3 O9 `* X
  39.             }* D1 J  e, R  i
  40. 0 U# l6 j% v! ~
  41.             // 读取块信息
    6 o+ R& }0 J* Q3 n: \% m+ h1 r
  42.             for (int i = 0; i < numFrames; i++)" Z5 L- o8 {2 ?0 a' c  f. o
  43.             {
    ! @$ z1 r! L1 A# {0 p
  44.                 chunkSize[i] = reader.ReadUInt32();+ f! o. `- a& h# W
  45.                 chunkOffs[i] = reader.ReadUInt32();
    1 t$ m' C- D9 |! J& Q3 J
  46.                 soundSize[i] = reader.ReadUInt32();
    9 j$ u- @$ `% V3 o" ?. v
  47.             }
    ' I7 o3 C2 H: [4 _% L: X
  48. 8 z( p8 j: g: N! N  a
  49.             // 如果存在背景帧,请跳过它5 }+ |+ j6 ~2 s9 H0 P! p) u
  50.             if (signature == "RLV3" && backSize != 0)
    1 m7 Y4 K0 ]& i* `: t1 P* w2 H3 A
  51.             {
    / D: N8 a$ b; s2 W9 D4 C
  52.                 reader.BaseStream.Seek(backSize, SeekOrigin.Current);
    ' Z) q) m5 a! h+ l- t; A. W
  53.             }
    . R; y1 T  ]8 V
  54. / e9 S9 ], O3 S) e/ s
  55.             // 创建一个WAV文件并写入音频数据
    : k- U0 l, Q" D; G' I8 F# l, _
  56.             using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))3 R4 o6 O. u" j4 B
  57.             {2 w8 }% G' p! ?6 l- b) f
  58.                 // 写入WAV头部) R% E- u1 C, g  t
  59.                 wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));- F/ _3 Z0 p( X' Q" X( C
  60.                 wavWriter.Write(36 + dataSize); // 总文件大小 - 8
    : e( m, [& O7 b. P1 B2 i. Q
  61.                 wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));
    . l( i: E" G4 C0 e
  62.                 wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));1 B1 |: W/ l! e7 [
  63.                 wavWriter.Write(16); // fmt块大小
    3 j: K9 y. L, j. n4 V" y
  64.                 wavWriter.Write((ushort)1); // 音频格式(PCM)$ d" S. `3 R( M% d
  65.                 wavWriter.Write(channels); // 声道数; o+ |) V- Y' `4 Q, \% ^7 [4 R5 \* p
  66.                 wavWriter.Write(rate); // 采样率+ b( ]5 P: O# ?& z& F6 g& |8 F
  67.                 wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数
    4 X1 ^/ `" T: e2 d2 d9 k
  68.                 wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数0 m2 ~0 ~% r- Q# s9 b
  69.                 wavWriter.Write(defSoundSize); // 每个样本的位深度2 J- k# m3 v+ Q" g/ f5 O* j) ]
  70.                 wavWriter.Write(Encoding.ASCII.GetBytes("data"));2 d6 t- _& d9 A# O
  71.                 wavWriter.Write(dataSize); // 数据大小5 q# M0 ~) `8 L" h
  72. % ]1 C) Y: I1 e1 r9 f2 e& @
  73.                 // 从.rl2文件中读取并写入PCM音频数据7 p* N) x1 e2 `) ~+ g# x
  74.                 for (int i = 0; i < numFrames; i++)! z: H1 [1 x- `2 I) @' |% e$ D
  75.                 {. o! b( o3 v% g6 p
  76.                     byte[] audioData = reader.ReadBytes((int)soundSize[i]);  E$ u: C) _: a8 ~$ D
  77.                     wavWriter.Write(audioData);
    5 v! `7 f. e( r0 P) p! c
  78.                 }% k9 S6 Z4 I- y
  79.             }
    ; T, L0 z/ c" \& I
  80.         }
    , F9 X7 H! p8 \( o- b' c" h
  81.     }
    4 A) `- [( v1 g4 e& a
  82. }
    8 @5 C3 F" t$ k7 C6 {4 ?- ?
  83. ; P1 m3 Z, y/ i; s2 \' q4 @
  84. class Program; _3 C. R7 T& n' T8 C
  85. {
    ! `. Y" ?0 Q1 }: t# k5 c& h9 m
  86.     static void Main(string[] args). H1 _9 d" E/ _$ J4 N! w
  87.     {
    7 S% Z5 q  a9 s( `
  88.         string inputFile = "N1275510.RL2";
    / N: d5 q" ~. U4 q( W& [
  89.         string outputFile = "output.wav";
    6 Y, K  M' K9 L" _/ \' ^3 v) A/ O! Q
  90.         RL2ToWavConverter.ConvertToWav(inputFile, outputFile);
    9 x6 H1 j) V# @
  91.         Console.WriteLine("转换完成。");! M: R1 X2 W* @/ B
  92.     }8 e8 t, S# D1 h
  93. }
    ( M5 I! _1 o# ~9 U
复制代码

! Q; z' O# y8 J/ j/ S( F; x* w
$ }/ q5 T6 t9 ]/ e) s, y, l
) F( E& h+ K1 F6 W5 [
分享到:  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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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