--- OniSplit/Sound/WavFile.cs 2020/01/22 14:08:57 1114 +++ OniSplit/Sound/WavFile.cs 2021/05/08 01:44:24 1156 @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +//using System.Runtime.Remoting.Metadata.W3cXsd2001; namespace Oni.Sound { @@ -9,7 +10,8 @@ namespace Oni.Sound #region Private data private const int fcc_RIFF = 0x46464952; private const int fcc_WAVE = 0x45564157; - private const int fcc_fmt = 0x20746d66; + private const int fcc_fmt = 0x20746d66; + private const int fcc_fact = 0x74636166; private const int fcc_data = 0x61746164; private WavFormat format; @@ -18,6 +20,7 @@ namespace Oni.Sound private int averageBytesPerSecond; private int blockAlign; private int bitsPerSample; + private int sampleCount; private byte[] extraData; private byte[] soundData; #endregion @@ -34,7 +37,10 @@ namespace Oni.Sound if (reader.ReadInt32() != fcc_WAVE) throw new InvalidDataException("Not a WAV file"); - var header = new WavFile(); + var header = new WavFile() + { + sampleCount = -1 + }; for (int chunkType, chunkSize, chunkStart; reader.Position < size; reader.Position = chunkStart + chunkSize) { @@ -44,10 +50,12 @@ namespace Oni.Sound if (chunkType == fcc_fmt) header.ReadFormatChunk(reader, chunkSize); - else if (chunkType == fcc_data) + if (chunkType == fcc_fact) + header.ReadFactChunk(reader, chunkSize); + if (chunkType == fcc_data) header.ReadDataChunk(reader, chunkSize); } - + header.TruncatePerFact(); return header; } } @@ -67,10 +75,39 @@ namespace Oni.Sound extraData = new byte[0]; } + private void ReadFactChunk(BinaryReader reader, int chunkSize) + { + sampleCount = reader.ReadInt32(); + } + private void ReadDataChunk(BinaryReader reader, int chunkSize) { soundData = reader.ReadBytes(chunkSize); } + private void TruncatePerFact() // TODO: MORE THOROUGH VALIDATION? + { + if(sampleCount == -1) // not explicitly set (no fact chunk present) + { + Console.WriteLine("The imported WAV file has no FACT chunk."); + } + else if (format == WavFormat.Adpcm) // calculate truncated data size + { + var blockSizeADPCM = blockAlign; + var samplesPerBlock = 2 + (blockSizeADPCM - channelCount * 7) * 8 / channelCount / bitsPerSample; + int wholeBlocks = sampleCount / samplesPerBlock; + if (wholeBlocks * blockAlign > soundData.Length) + Console.Error.WriteLine("Sample count exceeds the range of sound data."); + int leftoverSamples = sampleCount - wholeBlocks * samplesPerBlock; + if (leftoverSamples < 2) // a block always starts with at least two samples? + Console.Error.WriteLine("Improper trailing bytes/samples!"); + if (bitsPerSample != 4) // are MS ADPCM nibbles always 4-bit-sized? + Console.Error.WriteLine("Nibble size is expected to be 4 bits!"); + int leftoverNibbles = (leftoverSamples - 2) * channelCount; + int leftoverBytes = 7 * channelCount + + (int)Math.Ceiling((leftoverNibbles * bitsPerSample) * 0.125); + Array.Resize(ref soundData, wholeBlocks * blockAlign + leftoverBytes); + } + } public WavFormat Format => format; public int ChannelCount => channelCount; @@ -78,6 +115,7 @@ namespace Oni.Sound public int AverageBytesPerSecond => averageBytesPerSecond; public int BlockAlign => blockAlign; public int BitsPerSample => bitsPerSample; + public int SampleCount => sampleCount; public byte[] ExtraData => extraData; public byte[] SoundData => soundData; }