| 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 |  | } |