冒险解谜游戏中文网 ChinaAVG

标题: 【Scummvm汉化 #6】 Voyeur (CD - DOS) 偷窥 音频分析 [打印本页]

作者: shane007    时间: 2023-9-1 15:34
标题: 【Scummvm汉化 #6】 Voyeur (CD - DOS) 偷窥 音频分析
本帖最后由 shane007 于 2023-9-2 00:06 编辑 0 a. r( z* f( T1 ^/ Z/ Y' {
' W% Y) x0 f/ G8 G
该游戏是scummvm支持的少数几款FMV AVG。) U! n# D  M$ `0 x, \$ v! U  r
视频采用了一种叫做RL2的格式。% v; D4 L! Q, U+ h4 w# v) Z, l
参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,
& b' m( I' R' v" {, ]然后用whisper语音识别之后就能配上字幕了。
( I4 D2 p6 R7 |9 z( A, \此外,rl2格式用potplayer也能直接播放。; S" N6 c) N. M- `

" h8 T" Q0 \4 n8 d: X文件格式
9 F# s# p5 x) M1 V3 V+ ihttps://wiki.multimedia.cx/index.php/RL2$ b* U* x1 K# |$ S4 J

  1. 0 \* n* h1 |' V' O6 S* U2 ^  s5 j
  2. +  0  dword  Header                -- "FORM"
    6 A0 C+ O  e; G+ S0 H0 G" [
  3. +  4  dword  BackSize              -- size of the background frame/ G  S# S0 p8 G+ z% r% S* ^  w& B
  4. +  8  dword  Signature             -- "RLV2" or "RLV3"
    # g) J5 Z; R" i" I- d
  5. +  C  dword  DataSize              -- size of the data past this point BIG-ENDIAN
    8 f, M; R9 m3 U  O$ B, i
  6. + 10  dword  NumFrames             -- number of frames
    * ]' t( y: S, h0 C6 H( y
  7. + 14  word   Method                -- encoding method. ignored, zero' d4 t" @) a4 y- h4 W
  8. + 16  word   SoundRate             -- sound sample rate in some obscure format. zero means no sound
    & \7 p) n- T) `& ]/ k& |4 P
  9. + 18  word   Rate                  -- sound sample rate in Hz2 B  f. F+ l  h
  10. + 1A  word   Channels              -- number of sound channels1 \) x* s5 d1 A) V
  11. + 1C  word   DefSoundSize          -- size of the single sound chunk. see notes below
    ( G8 w; B, |* o& N1 t& ?7 t0 U
  12. + 1E  word   VideoBase             -- initial drawing offset within 320x200 viewport
    : Y: [; s* S6 f/ p
  13. + 20  dword  ClrCount              -- number of used colors
    0 ?9 X  b1 R$ i3 [: R( S) B
  14. + 24  RGB    Palette[256]          -- 256 RGB triplets. full 8-bit entries
    - F) s2 \9 z3 B! P5 E- I4 ~& Z$ I( C5 p
  15. -- if Signature == "RLV3" AND BackSize <> 0
    ! U6 c, H, I( M( X
  16.   +324 byte  BackFrame[BackSize]   -- encoded background frame. ignore VideoBase during it's decoding
    " u, Z6 ^" ^! F0 s) @& R- X$ h
  17. --" }, F: F4 x$ Z% X' n7 P( {7 f
  18. +xxx  dword  ChunkSize[NumFrames]  -- complete size of the chunk for each frame (audio+video)
    * u7 \( X% k3 g1 E4 O' ?
  19. +yyy  dword  ChunkOffs[NumFrames]  -- offset of the each frame chunk from the start of file
    . `8 F  h+ H* p) v" B) t! S
  20. +zzz  dword  SoundSize[NumFrames]  -- size of the audio portion in the frame chunk
    ' @& e" X# L" p0 ~
  21. -- for each frame --
    4 E4 S2 [1 a, ]
  22. +xxx  byte   Audio[SoundSize[frame_nr] & 0xFFFF]  -- raw 8-bit audio5 c2 S- E6 C0 Z% n. ?/ O" Z
  23. +yyy  byte   Video[...]                           -- compressed video stream
复制代码

9 u. v1 W/ w, {% w& v参考代码(有问题,但可参考)
  1. using System;
    8 y3 ?" X, I( S
  2. using System.IO;1 ]4 O# }' s( m- _! p- G( H( @- N
  3. using System.Text;
    1 W5 [  L* l& i
  4. : h2 Q. t6 d1 G' }
  5. public class RL2ToWavConverter
    " N* ?" t. f! n- ], L
  6. {
    9 ]- O; K/ f+ Q# b. `) C9 {
  7.     public static void ConvertToWav(string inputFile, string outputFile)* c7 ^' g# N6 G; f$ ~+ m
  8.     {; W7 |. E% H3 C
  9.         using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))1 {3 Y5 ~& L2 l; N! W) V
  10.         using (BinaryReader reader = new BinaryReader(fs)); l  ~2 X) Z: n7 @
  11.         {( l. n! h& s/ |& x: K. P. v
  12.             // 读取头部
    / x3 S2 p+ @4 `* m) u6 E7 K
  13.             string header = Encoding.ASCII.GetString(reader.ReadBytes(4));' b: c" d2 ?: A7 q5 M; O) B
  14.             if (header != "FORM")
    1 _; S3 ^, M9 G5 u
  15.             {2 N8 R- u) m7 _" I( q4 T
  16.                 Console.WriteLine("无效的.rl2文件格式。");
    9 i! ^) k2 A# P  C
  17.                 return;& j% P8 h! r5 f5 N" J
  18.             }8 E- k1 M. A; M* l% G

  19. ; C2 p- k5 _* A  u
  20.             uint backSize = reader.ReadUInt32();
    $ j8 Y2 H2 x* ]& h3 x) f9 [
  21.             string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));
    # y; k& D/ i1 n0 Q
  22.             uint dataSize = reader.ReadUInt32();
    & I- l5 [  g5 ^6 |8 S$ ?9 F4 o
  23.             uint numFrames = reader.ReadUInt32();
    , Z: o& D" V0 y. ], }
  24.             ushort method = reader.ReadUInt16();0 I9 E9 x3 n% \9 w+ Q
  25.             ushort soundRate = reader.ReadUInt16();' A9 v% s6 }) T+ X9 z
  26.             ushort rate = reader.ReadUInt16();/ ~3 l6 v+ ?+ R! j
  27.             ushort channels = reader.ReadUInt16();
    " h. s( q) @. l1 F' w
  28.             ushort defSoundSize = reader.ReadUInt16();
    # z6 h1 F' W: S2 y, E) C
  29.             ushort videoBase = reader.ReadUInt16();
    1 S- |" Y* Z3 ]
  30.             uint clrCount = reader.ReadUInt32();# s2 E6 G, S* J9 S
  31.             uint[] chunkSize = new uint[numFrames];
    ; u- V. r% J$ ]+ A* @
  32.             uint[] chunkOffs = new uint[numFrames];
    ! x! h- ^; @, l) [! y& `
  33.             uint[] soundSize = new uint[numFrames];
    : O9 U, s6 k. s3 [1 p5 Z3 L

  34. , h) s' r1 W% z# A! s
  35.             if (signature != "RLV2" && signature != "RLV3")
    : Q! X$ I& K0 ^, Z! B) }1 M7 K7 Z
  36.             {: b/ y8 r- y; h( U
  37.                 Console.WriteLine("不支持的签名。");/ K" A  u+ P# m% ^
  38.                 return;3 G# h4 O" y0 p# ]* u
  39.             }
    ) q; U) E/ ?; P# u' @6 o, ^$ D

  40. . H0 i  m+ l0 V% j
  41.             // 读取块信息: t; I: U  s2 R0 s- X! Y! V
  42.             for (int i = 0; i < numFrames; i++)
    : G+ H  P% e5 y- \
  43.             {5 A! r4 @5 ~4 ?2 L% n* B/ o$ i. R. x
  44.                 chunkSize[i] = reader.ReadUInt32();
    ! c7 ]# a5 u, S! S& b7 G
  45.                 chunkOffs[i] = reader.ReadUInt32();& R4 s- a9 k: f& Q7 Y% j% R; ?& S9 E8 y
  46.                 soundSize[i] = reader.ReadUInt32();4 u7 }0 }- g- K: H3 j. s* {
  47.             }
    & ^( t% p* G; l) Q1 H, ~7 w4 Z$ M) c

  48. - I" s" x  m9 J, N$ H8 @% O4 h
  49.             // 如果存在背景帧,请跳过它% B9 u1 `! R& W9 i# N6 P% h
  50.             if (signature == "RLV3" && backSize != 0)
    : t4 q0 @9 v! f1 ^9 w9 D7 S
  51.             {
    ( |% ~4 I4 {3 R2 G3 _
  52.                 reader.BaseStream.Seek(backSize, SeekOrigin.Current);( s: o: D$ `- _; ]0 ]% m1 ^
  53.             }
    4 @. I. q2 s4 I  Q3 A: P# ?

  54. 4 |4 O* Y  U& T8 g/ {
  55.             // 创建一个WAV文件并写入音频数据
    # R9 T7 L( E5 z% t6 l" x
  56.             using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create))); z' p/ o+ ]' b9 I3 Y' H: X
  57.             {% e  [- W" N9 y( D( Z/ Z& G8 B
  58.                 // 写入WAV头部( ]) l' c5 a: n2 N
  59.                 wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));
    % Y6 B# I) h; P2 [
  60.                 wavWriter.Write(36 + dataSize); // 总文件大小 - 84 P- O* b6 T! U
  61.                 wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));% I5 |7 _+ |2 _+ P5 ]( i, X
  62.                 wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));
    6 j$ N. C' y3 v
  63.                 wavWriter.Write(16); // fmt块大小
    0 K; ]) M1 l& J5 W" u+ @) z2 l
  64.                 wavWriter.Write((ushort)1); // 音频格式(PCM)% F0 Y8 s5 d( h& ~* B
  65.                 wavWriter.Write(channels); // 声道数2 ^0 W# S( o+ h% G
  66.                 wavWriter.Write(rate); // 采样率, A. K7 E/ i. u5 P
  67.                 wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数
    / G+ R3 _1 _" J& @6 L
  68.                 wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数
    4 b% d+ P9 U8 o
  69.                 wavWriter.Write(defSoundSize); // 每个样本的位深度+ f0 k2 k8 v: Q2 s0 o. b( x" O
  70.                 wavWriter.Write(Encoding.ASCII.GetBytes("data"));1 ?5 V! @* V/ i7 l: t2 ^
  71.                 wavWriter.Write(dataSize); // 数据大小
    ; H# C, b, }+ f3 F& b3 T

  72. 4 o3 i7 H/ @. `# Y( m
  73.                 // 从.rl2文件中读取并写入PCM音频数据1 y: }! M- [3 Q  q
  74.                 for (int i = 0; i < numFrames; i++)5 k9 ~/ ^4 T0 Z5 q' f5 J3 T
  75.                 {3 k' L5 i" L/ O" U6 p: F/ Z# k
  76.                     byte[] audioData = reader.ReadBytes((int)soundSize[i]);
    % A2 v" y* R" z
  77.                     wavWriter.Write(audioData);( M6 P& T! W* s4 Y
  78.                 }2 E, M/ Z* R! G- ]- J
  79.             }
    7 S7 ?& Y1 l4 g9 H2 j
  80.         }
    ( w3 l- m" b5 `7 Y) G& I
  81.     }& c3 P3 A5 z% V! }. F( _
  82. }' }5 D4 A' O6 {4 ?5 l8 J

  83. 8 j& [! }7 }9 l
  84. class Program! \8 C4 ?/ s1 V  s7 s% r
  85. {9 d- l7 w) P% X, ?- D6 X
  86.     static void Main(string[] args)
    - M, m2 }9 w+ \$ V/ O3 K
  87.     {
    , ]/ i' b0 g/ k( c1 f& n
  88.         string inputFile = "N1275510.RL2";7 M$ u% ]6 C6 i  f
  89.         string outputFile = "output.wav";
    4 a: d4 [2 v- D
  90.         RL2ToWavConverter.ConvertToWav(inputFile, outputFile);6 w9 S' c" A7 a6 ^( R
  91.         Console.WriteLine("转换完成。");
    6 Z# Q9 @5 q7 v7 A; b: E
  92.     }
    ; k7 Q  x; Z" M- f* ~
  93. }
    ' g4 ~$ }6 Z9 x5 r9 b1 N. s
复制代码
4 e. P4 @" T& h, ?

1 u$ ?! h  @' F. x- Z+ H- Q2 r0 e, B' }- x: Y6 P. `  j

作者: 星之韶华    时间: 2025-4-4 01:08
学习学习一下




欢迎光临 冒险解谜游戏中文网 ChinaAVG (https://chinaavg.com/) Powered by Discuz! X3.2