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

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

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

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

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

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

本帖最后由 shane007 于 2023-9-2 00:06 编辑 9 m% Q) I6 @! m( y, w

2 E7 y2 F) O1 P* k# R该游戏是scummvm支持的少数几款FMV AVG。
$ A' o: H6 K) I4 {- a$ O视频采用了一种叫做RL2的格式。
; W5 O( Q3 m8 t3 u1 u: o& a参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,4 I* A; T* g& K3 j* f/ t
然后用whisper语音识别之后就能配上字幕了。* d& O- c: V* S& _8 \: {6 k8 H
此外,rl2格式用potplayer也能直接播放。; q" L. V7 J5 X; T& J

# O# P* K7 ]$ _! \文件格式
8 `9 l& \( r9 f4 A( f' r4 }# b8 Fhttps://wiki.multimedia.cx/index.php/RL2& Q* N, \. D5 N7 }; h

  1. 3 B9 u. y+ O- u( ^' H
  2. +  0  dword  Header                -- "FORM"! Z  x4 h: w8 m6 Y  D
  3. +  4  dword  BackSize              -- size of the background frame) b& d$ i7 w; g9 c$ C6 S
  4. +  8  dword  Signature             -- "RLV2" or "RLV3"
    , H4 Q- Y8 N) m: \! B' q' l& k" I2 g
  5. +  C  dword  DataSize              -- size of the data past this point BIG-ENDIAN: m: k9 f; H& g% j4 G, V) ^
  6. + 10  dword  NumFrames             -- number of frames. p) U$ O1 N: l+ d5 ~8 c
  7. + 14  word   Method                -- encoding method. ignored, zero
    - M9 q9 d; J$ [. ~' O* c
  8. + 16  word   SoundRate             -- sound sample rate in some obscure format. zero means no sound
    # X6 R! w" Z. p, J
  9. + 18  word   Rate                  -- sound sample rate in Hz# t- p6 u5 M1 y4 s5 h# j
  10. + 1A  word   Channels              -- number of sound channels1 e: o9 j' Q/ v# {& A( h
  11. + 1C  word   DefSoundSize          -- size of the single sound chunk. see notes below4 }9 H2 C2 \4 c, |# ^# h
  12. + 1E  word   VideoBase             -- initial drawing offset within 320x200 viewport6 _5 T: Y4 S7 d) Z. J
  13. + 20  dword  ClrCount              -- number of used colors+ `! b* e  g1 t& z1 s, }
  14. + 24  RGB    Palette[256]          -- 256 RGB triplets. full 8-bit entries2 g1 R# w- |0 _* B+ E  k' N
  15. -- if Signature == "RLV3" AND BackSize <> 0$ u7 `' R' U+ p" m
  16.   +324 byte  BackFrame[BackSize]   -- encoded background frame. ignore VideoBase during it's decoding
    6 n9 @! e: i6 Q* K6 ^" p& ]* t
  17. --+ T4 x. m5 M* Q+ a3 P/ \
  18. +xxx  dword  ChunkSize[NumFrames]  -- complete size of the chunk for each frame (audio+video)
    % V) e3 y+ r# q2 ]0 `" C; ?
  19. +yyy  dword  ChunkOffs[NumFrames]  -- offset of the each frame chunk from the start of file' ?, X& s7 S+ |/ D9 g8 ~( `
  20. +zzz  dword  SoundSize[NumFrames]  -- size of the audio portion in the frame chunk: q  Z$ E: q( w3 {% }2 z
  21. -- for each frame --& S) S% t% E/ n9 B. f% [% F
  22. +xxx  byte   Audio[SoundSize[frame_nr] & 0xFFFF]  -- raw 8-bit audio
    4 H& g" b" K  f# {$ i8 ?
  23. +yyy  byte   Video[...]                           -- compressed video stream
复制代码

3 S) c# G2 U7 t: ~' w; p5 F参考代码(有问题,但可参考)
  1. using System;3 G2 n" Y; p1 U' {+ J. L) P
  2. using System.IO;. P2 c9 g# G3 Z" f! @  ?# j/ W
  3. using System.Text;# ]; e6 L+ V# D: L& c9 q9 r
  4. + k2 c# c/ _- I2 J
  5. public class RL2ToWavConverter
    * a1 m; @8 V: j( m, t' H
  6. {& l& ]: b- r# F  |, p
  7.     public static void ConvertToWav(string inputFile, string outputFile)8 X+ v! I# A9 [. ]
  8.     {3 y) E; ^: U) l0 U) w! v7 d
  9.         using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
    , z2 @: [' c! ?- p
  10.         using (BinaryReader reader = new BinaryReader(fs))
    # Z4 r( O  o5 w% C6 t
  11.         {# {& S: f. O& i. ]
  12.             // 读取头部
    4 ^) W/ Q, q& W2 }- O
  13.             string header = Encoding.ASCII.GetString(reader.ReadBytes(4));
    ' J/ L: |! N( |! M$ M
  14.             if (header != "FORM")1 J9 T; [" F) P
  15.             {
    & M- |: S+ I1 A, y
  16.                 Console.WriteLine("无效的.rl2文件格式。");
    0 H5 g: h' ^, i9 P
  17.                 return;
    5 K0 c# {% A! w$ \5 |
  18.             }6 j" I" i/ }6 j; u7 l3 ~

  19. , U5 c, I' j! g! s. c9 M
  20.             uint backSize = reader.ReadUInt32();9 g; Z6 @/ k( n/ @
  21.             string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));- W7 u! g. z$ x3 s9 ?
  22.             uint dataSize = reader.ReadUInt32();
    4 V9 x0 J; h. c  V" T) |& `
  23.             uint numFrames = reader.ReadUInt32();9 `2 }6 a4 b$ X
  24.             ushort method = reader.ReadUInt16();
    3 Z' T! ~0 o8 T/ x! H
  25.             ushort soundRate = reader.ReadUInt16();* l, @8 |7 [' w$ @4 Y7 x/ O
  26.             ushort rate = reader.ReadUInt16();+ M2 C) J( _- l
  27.             ushort channels = reader.ReadUInt16();
    9 v9 Y5 Q0 j- y1 q) X6 m3 O
  28.             ushort defSoundSize = reader.ReadUInt16();& |0 g& n- q6 z  |5 p( @9 O7 [
  29.             ushort videoBase = reader.ReadUInt16();
    ) q: K; g0 Y) F# K  b( M
  30.             uint clrCount = reader.ReadUInt32();: X4 ~$ T  ^; Q
  31.             uint[] chunkSize = new uint[numFrames];
    - v& C1 L4 t0 X2 I0 f% G
  32.             uint[] chunkOffs = new uint[numFrames];
    ) z2 B- K* j2 Y$ @- r3 T
  33.             uint[] soundSize = new uint[numFrames];3 I6 C7 o# Y: d# a" K6 Q
  34. % O: q1 _' D- w' v& k  t
  35.             if (signature != "RLV2" && signature != "RLV3")
    - h' Q: W6 h# F# m# `
  36.             {
    5 w; T4 K" e" U& Z
  37.                 Console.WriteLine("不支持的签名。");8 `2 O* k, v  e
  38.                 return;; h, \  X4 E) {. }3 o" g
  39.             }
    7 K+ {; h: u7 L/ J

  40. 8 V( C& ~: z# e8 A6 X& _
  41.             // 读取块信息$ F+ e. T5 l( w- F7 h0 }! d/ ]
  42.             for (int i = 0; i < numFrames; i++)
    2 a3 j1 c7 t9 B0 x0 A7 S
  43.             {
    : @/ u6 K$ \4 `( I
  44.                 chunkSize[i] = reader.ReadUInt32();9 f4 L) j, E' x# V- ?
  45.                 chunkOffs[i] = reader.ReadUInt32();; h3 U, v* ^& w. s
  46.                 soundSize[i] = reader.ReadUInt32();
    % X+ Q$ Z) I8 ^. c: t, c
  47.             }
    5 a0 W9 H/ ]1 m) x2 y& [8 }- s( H- b
  48. + Y) Y/ ]- H+ K0 R, O& f: O
  49.             // 如果存在背景帧,请跳过它
    9 D2 @5 A8 ^2 ?2 L  }  M) M: Y
  50.             if (signature == "RLV3" && backSize != 0)0 B0 O  e9 r& _/ W. {
  51.             {% O; p, G, b9 C9 y/ E+ P
  52.                 reader.BaseStream.Seek(backSize, SeekOrigin.Current);9 s; Y  A: y( F4 B3 o, A( ?. e
  53.             }
    + S, ?2 Q, `3 o' n0 i4 z

  54. - D# M3 g6 X" I8 X
  55.             // 创建一个WAV文件并写入音频数据- l% H, o! H5 z/ E9 o4 K) O
  56.             using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))
    : i' h! u5 U3 A# A  R& I( ~
  57.             {
    ! k3 P  s/ i% }3 i. ~; h, z% D
  58.                 // 写入WAV头部) |4 R0 l- X4 Z' k1 [. H; o
  59.                 wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));
    % z4 J8 x, j, y. x) S/ L2 i1 B
  60.                 wavWriter.Write(36 + dataSize); // 总文件大小 - 80 P& g; X0 H- T+ ~; S
  61.                 wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));
    8 |0 q, l* I2 v5 k/ s0 D6 Z( c" k& z
  62.                 wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));
    1 P0 L! G: u- v* W* |3 B
  63.                 wavWriter.Write(16); // fmt块大小
    # ^$ E: w" a4 a) M9 r0 B1 _
  64.                 wavWriter.Write((ushort)1); // 音频格式(PCM)
    8 O3 A. H( x& m+ |7 b! ~& ^- B! B
  65.                 wavWriter.Write(channels); // 声道数
    8 ~! A# q+ v6 g) E$ P" x: ?
  66.                 wavWriter.Write(rate); // 采样率6 c! l1 t% z% k7 x" j
  67.                 wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数
    3 H! n' P' d3 ~! ~" R# y! u
  68.                 wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数( i; H2 \/ f7 U
  69.                 wavWriter.Write(defSoundSize); // 每个样本的位深度
    ) X' ]! ^: d+ S4 M9 b
  70.                 wavWriter.Write(Encoding.ASCII.GetBytes("data"));7 q5 S8 |& |7 Y+ L, Q- o; x: c! d4 |
  71.                 wavWriter.Write(dataSize); // 数据大小
    0 L: `5 z5 T6 {9 y1 Y) d0 f

  72. : c$ }8 h1 u4 ]- [" `9 N" g
  73.                 // 从.rl2文件中读取并写入PCM音频数据( T. b: R6 p% D1 c9 O: [
  74.                 for (int i = 0; i < numFrames; i++)4 k( W/ Q7 y/ W- c# i8 h1 Z
  75.                 {1 S- `- }% s8 n4 b8 s3 T# v
  76.                     byte[] audioData = reader.ReadBytes((int)soundSize[i]);- d  S. A" ]8 ~' U) k! B
  77.                     wavWriter.Write(audioData);% U$ B3 x8 }& l9 P
  78.                 }
    8 f5 B! Y" F( O2 ?' P5 P. m7 u
  79.             }
    7 k- Z& C1 [6 t
  80.         }. V" d, _# ?) k0 Z. T/ t
  81.     }3 y8 j9 P; u. f. f% C6 M
  82. }* K. a2 G9 e$ V# {5 R
  83. + h0 b# o7 P+ m
  84. class Program3 O8 |9 l$ y. h8 {/ a& d
  85. {
    ) T) T) J# X2 }1 l6 Y8 N
  86.     static void Main(string[] args)
    . Q+ ]' C0 T5 n  o8 b
  87.     {& V" F2 U5 Q$ y
  88.         string inputFile = "N1275510.RL2";
    , G; L# T( _# q0 l6 M5 r- D
  89.         string outputFile = "output.wav";
    ; l! U" ]; }/ s
  90.         RL2ToWavConverter.ConvertToWav(inputFile, outputFile);' V4 j. O5 E; h
  91.         Console.WriteLine("转换完成。");
    ' {- ~8 F  K7 s0 U
  92.     }
    5 ~) \2 S9 ~2 N
  93. }
    + I- n) Q4 {' N/ o- ^- e! ?% \
复制代码
7 [8 b5 e* l( X

: X" n& o9 ~9 _7 p
: J( `8 }, |- K
分享到:  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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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