Ignore:
Timestamp:
May 3, 2021, 5:26:34 PM (4 years ago)
Author:
geyser
Message:

Uploading WIP code for SoundData, because the previous commit relies on it after all.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • OniSplit/Sound/SoundData.cs

    r1131 r1155  
    22using System.Collections.Generic;
    33using System.Text;
     4using System.IO;
    45
    56namespace Oni.Sound
     
    78    internal class SoundData
    89    {
     10        public bool IsPCM;
    911        public bool IsIMA4;
    1012        public int SampleRate;
    1113        public int ChannelCount;
    1214        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;
    1331
    1432        public static SoundData Read(InstanceDescriptor sndd, bool do_pc_demo_test)
     
    2442            using (var reader = sndd.OpenRead())
    2543            {
     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
    2654                if (sndd.IsMacFile)
    2755                {
    28                     sound.ChannelCount = (reader.ReadInt32() >> 1) + 1;
     56                    sound.ChannelCount = (reader.ReadInt32() >> 1) + 1; // TODO: interpret the low bit? (uncompressed/compressed?)
    2957                    sound.SampleRate = 22050;
    30                     reader.Skip(4);
     58                    sound.nGameTicks = (UInt16)reader.ReadInt32();
     59
    3160                    sound.IsIMA4 = true;
     61                    sound.IsPCM = false;
    3262                }
    3363                else
    3464                {
    35                     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?)
    3668                    sound.ChannelCount = reader.ReadInt16();
    3769                    sound.SampleRate = reader.ReadInt32();
    38                     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)
    3987                    sound.IsIMA4 = false;
    4088                }
     
    65113                else
    66114                    sound.IsIMA4 = false;
    67                 if (!(sound.IsIMA4))
    68                     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                }
    69135            }
     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);
    70162
    71163            return sound;
Note: See TracChangeset for help on using the changeset viewer.