1 |
|
using System; |
2 |
|
using System.Collections.Generic; |
3 |
|
using System.Text; |
4 |
+ |
using System.IO; |
5 |
|
|
6 |
|
namespace Oni.Sound |
7 |
|
{ |
8 |
|
internal class SoundData |
9 |
|
{ |
10 |
+ |
public bool IsPCM; |
11 |
|
public bool IsIMA4; |
12 |
|
public int SampleRate; |
13 |
|
public int ChannelCount; |
14 |
|
public byte[] Data; |
15 |
|
|
16 |
+ |
public UInt16 nGameTicks; |
17 |
+ |
|
18 |
+ |
public int SampleCount; // calculated |
19 |
+ |
|
20 |
+ |
//MS WAV variables |
21 |
+ |
public int AverageDataRate; |
22 |
+ |
public int BlockAlignment; |
23 |
+ |
public int BitsPerSample; |
24 |
+ |
public int HeaderSizeADPCM; |
25 |
+ |
|
26 |
+ |
// optional MSADPCM values |
27 |
+ |
public UInt16 SamplesPerBlock; |
28 |
+ |
public UInt16 nCoefPairs; |
29 |
+ |
public Int16[] CoefADPCMa; |
30 |
+ |
public Int16[] CoefADPCMb; |
31 |
+ |
|
32 |
|
public static SoundData Read(InstanceDescriptor sndd, bool do_pc_demo_test) |
33 |
|
{ |
34 |
|
if (sndd.Template.Tag != TemplateTag.SNDD) |
41 |
|
|
42 |
|
using (var reader = sndd.OpenRead()) |
43 |
|
{ |
44 |
+ |
// sample rate |
45 |
+ |
// duration in frames |
46 |
+ |
|
47 |
+ |
// IMA4 or |
48 |
+ |
// MS ADPCM: block size (default 512 for mono, 1024 for stereo, interruptible) |
49 |
+ |
|
50 |
+ |
// sample count (automatic for IMA4 and PC demo) |
51 |
+ |
|
52 |
+ |
|
53 |
+ |
// size in raw / offset in raw |
54 |
|
if (sndd.IsMacFile) |
55 |
|
{ |
56 |
< |
sound.ChannelCount = (reader.ReadInt32() >> 1) + 1; |
56 |
> |
sound.ChannelCount = (reader.ReadInt32() >> 1) + 1; // TODO: interpret the low bit? (uncompressed/compressed?) |
57 |
|
sound.SampleRate = 22050; |
58 |
< |
reader.Skip(4); |
58 |
> |
sound.nGameTicks = (UInt16)reader.ReadInt32(); |
59 |
> |
|
60 |
|
sound.IsIMA4 = true; |
61 |
+ |
sound.IsPCM = false; |
62 |
|
} |
63 |
|
else |
64 |
|
{ |
65 |
< |
reader.Skip(6); // ADPCM format identifiers (change to support PCM?) |
65 |
> |
sound.IsPCM = false; |
66 |
> |
reader.Skip(4); // flags (1=?, 2=?, 4=?, 8=compressed) TODO: Try uncompressed (even for demo?) |
67 |
> |
reader.Skip(2); // Int16; format ID (2= ADPCM, 1=PCM?) TODO: Try uncompressed (even for demo?) |
68 |
|
sound.ChannelCount = reader.ReadInt16(); |
69 |
|
sound.SampleRate = reader.ReadInt32(); |
70 |
< |
reader.Skip(44); |
70 |
> |
sound.AverageDataRate = reader.ReadInt32(); // in B/s (2 or 4 for PCM; 11155, 22311 or 22179 for Vanilla ADPCM) |
71 |
> |
sound.BlockAlignment = reader.ReadInt16(); // 2 or 4 bytes per block for PCM; 512 or 1024 for Vanilla ADPCM |
72 |
> |
sound.BitsPerSample = reader.ReadInt16(); // bits per sample per channel (4 bits per sample for ADPCM, 16 bits for PCM) |
73 |
> |
sound.HeaderSizeADPCM = reader.ReadInt16(); // size of additional (ADPCM) header (zero for PCM); typically 32 bytes |
74 |
> |
|
75 |
> |
sound.SamplesPerBlock = reader.ReadUInt16(); // UInt16; samples per block (can be inferred from block alignment etc) |
76 |
> |
|
77 |
> |
sound.nCoefPairs = reader.ReadUInt16(); // usually 7 |
78 |
> |
sound.CoefADPCMa = new Int16[sound.nCoefPairs]; // usually 256 512 0 192 240 460 392 |
79 |
> |
sound.CoefADPCMb = new Int16[sound.nCoefPairs]; // usually 0 -256 0 64 0 -208 -232 |
80 |
> |
for (int coefPair = 0; coefPair < sound.nCoefPairs; ++coefPair) |
81 |
> |
{ |
82 |
> |
sound.CoefADPCMa[coefPair] = reader.ReadInt16(); |
83 |
> |
sound.CoefADPCMb[coefPair] = reader.ReadInt16(); |
84 |
> |
} |
85 |
> |
|
86 |
> |
sound.nGameTicks = reader.ReadUInt16(); // UInt16; number of game ticks (truncated to lower value) |
87 |
|
sound.IsIMA4 = false; |
88 |
|
} |
89 |
|
|
112 |
|
} |
113 |
|
else |
114 |
|
sound.IsIMA4 = false; |
115 |
< |
if (!(sound.IsIMA4)) |
116 |
< |
Console.WriteLine("PC-demo MS ADPCM detected; use -nodemo flag to treat as IMA4."); |
115 |
> |
if (!(sound.IsIMA4)) // fill in default values of WAV (MS ADCPM) header |
116 |
> |
{ |
117 |
> |
sound.IsPCM = false; |
118 |
> |
Console.WriteLine("PC-demo MS ADPCM detected; use -nodemo flag to treat as Mac IMA4."); |
119 |
> |
sound.AverageDataRate = (sound.ChannelCount == 1) ? 11155 : 22311; |
120 |
> |
sound.BlockAlignment = (sound.ChannelCount == 1) ? 512 : 1024; |
121 |
> |
sound.BitsPerSample = 4; |
122 |
> |
sound.SamplesPerBlock = 1012; |
123 |
> |
sound.HeaderSizeADPCM = 32; |
124 |
> |
sound.nCoefPairs = 7; |
125 |
> |
sound.CoefADPCMa = new Int16[7]; // usually 256 512 0 192 240 460 392 |
126 |
> |
sound.CoefADPCMb = new Int16[7]; // usually 0 -256 0 64 0 -208 -232 |
127 |
> |
sound.CoefADPCMa[0] = 256; sound.CoefADPCMb[0] = 0; |
128 |
> |
sound.CoefADPCMa[1] = 512; sound.CoefADPCMb[1] = -256; |
129 |
> |
sound.CoefADPCMa[2] = 0; sound.CoefADPCMb[2] = 0; |
130 |
> |
sound.CoefADPCMa[3] = 192; sound.CoefADPCMb[3] = 64; |
131 |
> |
sound.CoefADPCMa[4] = 240; sound.CoefADPCMb[4] = 0; |
132 |
> |
sound.CoefADPCMa[5] = 460; sound.CoefADPCMb[5] = -208; |
133 |
> |
sound.CoefADPCMa[6] = 392; sound.CoefADPCMb[6] = -232; |
134 |
> |
} |
135 |
> |
} |
136 |
> |
// validate data and calculate sample count |
137 |
> |
if(sound.IsIMA4) // get the sample count |
138 |
> |
{ |
139 |
> |
int nIMABlocks = sound.Data.Length / 34 / sound.ChannelCount; |
140 |
> |
if(sound.Data.Length - nIMABlocks * 34 * sound.ChannelCount > 0) |
141 |
> |
throw new InvalidDataException("IMA4 data shouldn't have incomplete blocks."); |
142 |
> |
sound.SampleCount = nIMABlocks * 64; |
143 |
> |
} |
144 |
> |
else |
145 |
> |
{ |
146 |
> |
// TODO: validate all the parameters: resolve conflicts if any, or bail out |
147 |
> |
// TODO: handle PCM (the following assumes MS ADPCM) |
148 |
> |
if (sound.IsPCM) |
149 |
> |
{ |
150 |
> |
} |
151 |
> |
else |
152 |
> |
{ |
153 |
> |
int wholeBlocks = sound.Data.Length / sound.BlockAlignment; |
154 |
> |
int leftoverBytes = sound.Data.Length - (wholeBlocks * sound.BlockAlignment); |
155 |
> |
int leftoverSamples = 8 * (leftoverBytes - 7 * sound.ChannelCount) |
156 |
> |
/ sound.BitsPerSample / sound.ChannelCount + 2; // assuming 4 bits per sample |
157 |
> |
sound.SampleCount = wholeBlocks * sound.SamplesPerBlock + leftoverSamples; |
158 |
> |
} |
159 |
|
} |
160 |
+ |
// Console.WriteLine("Sample count:"); |
161 |
+ |
// Console.WriteLine(sound.SampleCount); |
162 |
|
|
163 |
|
return sound; |
164 |
|
} |