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

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

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

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

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

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

本帖最后由 shane007 于 2023-9-2 00:06 编辑
$ B' M& a2 c: M. h- V: z. @+ T/ M5 F1 m! _9 X3 w6 }. B4 G
该游戏是scummvm支持的少数几款FMV AVG。
, T* R8 O2 i  s) |* D4 |% o视频采用了一种叫做RL2的格式。
4 X" J4 n4 ~, @参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,
% r  ]  v- D$ n! {然后用whisper语音识别之后就能配上字幕了。3 @& N$ n7 @/ n
此外,rl2格式用potplayer也能直接播放。5 B( y- I! `4 L7 _* o

2 \9 _: x  N) l4 D/ y文件格式
! I! k9 R" E" d) Q5 ?https://wiki.multimedia.cx/index.php/RL2
) j; S: J7 G) X2 A$ I1 z7 {

  1. + r& e- x5 F$ q. B  y  ^, H- D, ?
  2. +  0  dword  Header                -- "FORM"8 d& c5 V7 c# p
  3. +  4  dword  BackSize              -- size of the background frame* `" W4 Q5 d! n. Y! T( a& K
  4. +  8  dword  Signature             -- "RLV2" or "RLV3"
    ) W8 F- v. p$ q  V% ^, [. H
  5. +  C  dword  DataSize              -- size of the data past this point BIG-ENDIAN; L+ G  b& a3 w/ g3 l, U$ f0 a1 `
  6. + 10  dword  NumFrames             -- number of frames
    1 @; L. _- F* C2 C4 ?) R9 ]. V! S
  7. + 14  word   Method                -- encoding method. ignored, zero7 l4 P, T2 i  s# n
  8. + 16  word   SoundRate             -- sound sample rate in some obscure format. zero means no sound0 B3 p. A; p2 F5 R
  9. + 18  word   Rate                  -- sound sample rate in Hz6 g& I" i& N% ~; I6 Z- I
  10. + 1A  word   Channels              -- number of sound channels* m/ S2 _/ M$ j/ i9 K/ a3 v
  11. + 1C  word   DefSoundSize          -- size of the single sound chunk. see notes below" z. c+ I9 P7 y- h  H. Q$ c
  12. + 1E  word   VideoBase             -- initial drawing offset within 320x200 viewport
    , J# m# n' _7 E) z
  13. + 20  dword  ClrCount              -- number of used colors
    3 t1 ~8 D# s- w% u2 `" a  s! V! [! k" ^
  14. + 24  RGB    Palette[256]          -- 256 RGB triplets. full 8-bit entries
    8 `4 X6 U; u+ Y  Z- i8 D. j5 g# T
  15. -- if Signature == "RLV3" AND BackSize <> 0
    # {$ N+ R3 s% j8 ?6 B8 \3 h' Q
  16.   +324 byte  BackFrame[BackSize]   -- encoded background frame. ignore VideoBase during it's decoding9 X. [) d2 q7 z4 {* Q* C+ x# _
  17. --
    0 ]4 v) y$ h6 W1 Y1 {3 d
  18. +xxx  dword  ChunkSize[NumFrames]  -- complete size of the chunk for each frame (audio+video)! L. d' [% N2 n! ~: R# C  _7 J; d
  19. +yyy  dword  ChunkOffs[NumFrames]  -- offset of the each frame chunk from the start of file( R8 n( b  O0 z' d3 F
  20. +zzz  dword  SoundSize[NumFrames]  -- size of the audio portion in the frame chunk
    6 p* g0 r7 f8 {6 @) A6 e
  21. -- for each frame --; I  I1 U" g+ R
  22. +xxx  byte   Audio[SoundSize[frame_nr] & 0xFFFF]  -- raw 8-bit audio
    % v! k3 @  m0 P# K& C! @) F! W- m/ @
  23. +yyy  byte   Video[...]                           -- compressed video stream
复制代码
: c+ m( }1 `5 B6 N8 M" z  Z/ P
参考代码(有问题,但可参考)
  1. using System;. k/ a, ?- F/ v1 M0 J1 v
  2. using System.IO;
    5 N+ ]2 z# M7 G. ~  ^0 A
  3. using System.Text;
    8 Q6 ?4 T" M# C. ^9 z2 L9 {# Z

  4. . f$ z( r, W% w- v/ X" d% w
  5. public class RL2ToWavConverter5 S2 \/ o% o6 m8 u9 H
  6. {
    ( l) G. r. o! N/ K2 c
  7.     public static void ConvertToWav(string inputFile, string outputFile)& M8 p- o) S$ e) n- ^2 Z! ~
  8.     {
    ' Y" g9 Y7 U! n' O# |
  9.         using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))4 \5 Q: v& z( Z4 y
  10.         using (BinaryReader reader = new BinaryReader(fs))
    # P. ]$ s5 H! `' W( @
  11.         {
    1 T# l0 ]. G6 ]" y
  12.             // 读取头部! k! w  U6 ~8 u: \& r* v6 G
  13.             string header = Encoding.ASCII.GetString(reader.ReadBytes(4));) @. k, {- q* \( k" B
  14.             if (header != "FORM")
    ! l# y  K7 j0 `/ B* R
  15.             {
    : k9 z; O# K$ ^6 }0 g# x# b5 P
  16.                 Console.WriteLine("无效的.rl2文件格式。");4 _8 j2 P0 z! a6 T7 u. @$ L! n
  17.                 return;
    ' p, \+ h: c% k/ @0 N" ~1 F( {4 _
  18.             }
    . x5 L0 p0 E$ E- W; I" v9 {) g8 a

  19. ! W$ G  u! K6 [8 l2 n2 L7 J) Y
  20.             uint backSize = reader.ReadUInt32();9 U6 a; S' l  e' {' q" R
  21.             string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));5 e/ m5 v9 k- J4 a$ i
  22.             uint dataSize = reader.ReadUInt32();
    2 E0 r3 P  j2 l# \. T
  23.             uint numFrames = reader.ReadUInt32();
    # `: p& ?/ H7 }( A: ^' z3 N
  24.             ushort method = reader.ReadUInt16();
    - K$ g. j, H* |
  25.             ushort soundRate = reader.ReadUInt16();. C( z7 D7 K( g1 {2 q' F4 W
  26.             ushort rate = reader.ReadUInt16();
    + f6 B/ ?  G0 u5 o6 m8 X# F
  27.             ushort channels = reader.ReadUInt16();
    5 A# v: f& h& z
  28.             ushort defSoundSize = reader.ReadUInt16();/ l. t+ q2 Y- I9 U: i' J& D
  29.             ushort videoBase = reader.ReadUInt16();$ q9 J* a( e# ~2 ^% w! X, c" z
  30.             uint clrCount = reader.ReadUInt32();
    0 w2 d  R7 y5 d! p; Q' l/ t
  31.             uint[] chunkSize = new uint[numFrames];
    7 G; X  g$ t! Y% _6 O- {1 d/ J
  32.             uint[] chunkOffs = new uint[numFrames];
    % N( t! R$ V8 s$ O& J  B) R
  33.             uint[] soundSize = new uint[numFrames];
    ' y# V6 N/ ~; _- c

  34. ( @3 q& M8 ]- R3 P, J) j' Z
  35.             if (signature != "RLV2" && signature != "RLV3")
    0 m+ _- W) r5 \2 ?6 ~- Q
  36.             {$ C& X; l5 U' {5 u! H+ \+ y3 X% t* j
  37.                 Console.WriteLine("不支持的签名。");
      b1 S" }2 A! U+ \4 Y
  38.                 return;0 P& b5 L" M# {3 \# A0 }9 g! `
  39.             }3 ?$ _3 _: Q7 J2 t% F

  40. 4 f0 P" @+ D3 c- M  t
  41.             // 读取块信息
    ' c% \% F3 ]  i, ]8 [$ k
  42.             for (int i = 0; i < numFrames; i++)
    8 ]  ^8 h* ]3 H
  43.             {
    ) a: `3 @" ?6 a; W$ I) ?
  44.                 chunkSize[i] = reader.ReadUInt32();
    4 R  ^7 h) w+ Z4 R; p$ n7 z
  45.                 chunkOffs[i] = reader.ReadUInt32();
    8 |8 M6 l! l$ t$ g+ j# i3 ~1 K& T
  46.                 soundSize[i] = reader.ReadUInt32();
    ; k' e" V' F/ [. X. [/ Z
  47.             }
    2 `- M+ ?, i; v( n
  48. + S1 K2 c) X% R, B. ]
  49.             // 如果存在背景帧,请跳过它- d5 v, b3 k6 P2 h' k3 D# A8 I
  50.             if (signature == "RLV3" && backSize != 0)3 Z% }0 J6 Y6 `3 H- Y* Y. o
  51.             {6 N9 N+ E) c/ o, Q( i" V/ d
  52.                 reader.BaseStream.Seek(backSize, SeekOrigin.Current);7 K6 C, X: O. \, `; r) |1 q
  53.             }3 T4 N1 Y3 p5 F9 Q% K
  54. 7 S- x& I, n* `
  55.             // 创建一个WAV文件并写入音频数据
    % ]4 S: q: v$ }- x) @0 b8 J7 y; y
  56.             using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))
    3 F4 v4 |% q; r" |6 U" T3 r- u4 h
  57.             {5 _% l  W) M) x. L$ f8 A+ n1 v1 L( q
  58.                 // 写入WAV头部
    - f4 |2 M" ]7 w( x5 H# @: c
  59.                 wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));
    9 I' R. ~  O  r5 ?/ i/ o( j
  60.                 wavWriter.Write(36 + dataSize); // 总文件大小 - 8
    ; N0 Q9 T9 t/ p
  61.                 wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));
    0 k2 T, q) o: C- I
  62.                 wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));
    & z2 s- x  x" l+ J6 I
  63.                 wavWriter.Write(16); // fmt块大小
    6 e0 C, i( m; S: O: @( R* s: r( X3 h
  64.                 wavWriter.Write((ushort)1); // 音频格式(PCM)* {* D8 {* R( g, y# y
  65.                 wavWriter.Write(channels); // 声道数  E  }' D1 S# a6 A8 m. u
  66.                 wavWriter.Write(rate); // 采样率! z: |$ W+ f" c* k
  67.                 wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数
    # W6 a) m2 z  G
  68.                 wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数9 k3 _) E( Q8 a) Q% d) x0 c+ q5 p
  69.                 wavWriter.Write(defSoundSize); // 每个样本的位深度4 ]0 |4 g: y! p
  70.                 wavWriter.Write(Encoding.ASCII.GetBytes("data"));, ~: z2 h- M8 j, j' s  Q; A4 Q. ]
  71.                 wavWriter.Write(dataSize); // 数据大小2 [/ b$ a) P# ]

  72. # ?. u" b  K5 b; X( |3 p, A7 Q
  73.                 // 从.rl2文件中读取并写入PCM音频数据
    & N+ x, M. K9 Q" }- S
  74.                 for (int i = 0; i < numFrames; i++)$ b5 g' `+ l  Z: Z
  75.                 {
    2 r3 t) ~6 k- t+ T3 @
  76.                     byte[] audioData = reader.ReadBytes((int)soundSize[i]);$ y9 x, \1 M6 e' D% M
  77.                     wavWriter.Write(audioData);
    4 E% X, s- Q9 C! g  j2 a# g. f( m
  78.                 }$ a* W9 q; `- Y2 Z3 Y* j5 w7 R
  79.             }0 M3 i; {: z- T( @" F  |
  80.         }9 T. r( V; O/ e0 V3 g. z
  81.     }+ r' U; M) H% y+ i$ A
  82. }2 R( o. j% v8 P. n

  83. : ~2 t' @+ r& |0 c1 ]; a$ Z6 b
  84. class Program
    8 u# r8 y' Y" B, `! r) _
  85. {% G3 x" Z' k. }1 a+ U
  86.     static void Main(string[] args)
    - _1 I' a* j0 }/ e. T6 b/ c) H
  87.     {
    0 w- l1 ~9 a) |$ J; Y
  88.         string inputFile = "N1275510.RL2";
    0 @* ]) x' C4 t
  89.         string outputFile = "output.wav";
    ( m0 l* a0 P5 m5 A7 F) J( |
  90.         RL2ToWavConverter.ConvertToWav(inputFile, outputFile);
    * ~& N4 e* S! ]. ]* y2 ?: c% l
  91.         Console.WriteLine("转换完成。");
    4 g( w& Q7 U7 U. [3 a
  92.     }) r$ [, q9 d2 Y; n0 v& U
  93. }6 ]) R* U1 Q# g' m  ]# i' b. u: g
复制代码
9 b' O$ L8 w% U7 f/ h

" _+ d5 _$ D" }# c1 D) a9 ~
* H; b9 W: m0 u
分享到:  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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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