source: OniSplit/Sound/WavFile.cs@ 1190

Last change on this file since 1190 was 1156, checked in by geyser, 4 years ago

Fixed importing of "fact" chunk, as well as an unhandled fatal when attempting to transcode IMA4 to MS ADPCM. Also added missing padding to SNDD template and fixed the duration calculation for WAV-to-SNDD.

File size: 4.9 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.IO;
4//using System.Runtime.Remoting.Metadata.W3cXsd2001;
5
6namespace Oni.Sound
7{
8 internal class WavFile
9 {
10 #region Private data
11 private const int fcc_RIFF = 0x46464952;
12 private const int fcc_WAVE = 0x45564157;
13 private const int fcc_fmt = 0x20746d66;
14 private const int fcc_fact = 0x74636166;
15 private const int fcc_data = 0x61746164;
16
17 private WavFormat format;
18 private int channelCount;
19 private int sampleRate;
20 private int averageBytesPerSecond;
21 private int blockAlign;
22 private int bitsPerSample;
23 private int sampleCount;
24 private byte[] extraData;
25 private byte[] soundData;
26 #endregion
27
28 public static WavFile FromFile(string filePath)
29 {
30 using (var reader = new BinaryReader(filePath))
31 {
32 if (reader.ReadInt32() != fcc_RIFF)
33 throw new InvalidDataException("Not a WAV file");
34
35 int size = reader.ReadInt32();
36
37 if (reader.ReadInt32() != fcc_WAVE)
38 throw new InvalidDataException("Not a WAV file");
39
40 var header = new WavFile()
41 {
42 sampleCount = -1
43 };
44
45 for (int chunkType, chunkSize, chunkStart; reader.Position < size; reader.Position = chunkStart + chunkSize)
46 {
47 chunkType = reader.ReadInt32();
48 chunkSize = reader.ReadInt32();
49 chunkStart = reader.Position;
50
51 if (chunkType == fcc_fmt)
52 header.ReadFormatChunk(reader, chunkSize);
53 if (chunkType == fcc_fact)
54 header.ReadFactChunk(reader, chunkSize);
55 if (chunkType == fcc_data)
56 header.ReadDataChunk(reader, chunkSize);
57 }
58 header.TruncatePerFact();
59 return header;
60 }
61 }
62
63 private void ReadFormatChunk(BinaryReader reader, int chunkSize)
64 {
65 format = (WavFormat)reader.ReadInt16();
66 channelCount = reader.ReadInt16();
67 sampleRate = reader.ReadInt32();
68 averageBytesPerSecond = reader.ReadInt32();
69 blockAlign = reader.ReadInt16();
70 bitsPerSample = reader.ReadInt16();
71
72 if (chunkSize > 16)
73 extraData = reader.ReadBytes(reader.ReadInt16());
74 else
75 extraData = new byte[0];
76 }
77
78 private void ReadFactChunk(BinaryReader reader, int chunkSize)
79 {
80 sampleCount = reader.ReadInt32();
81 }
82
83 private void ReadDataChunk(BinaryReader reader, int chunkSize)
84 {
85 soundData = reader.ReadBytes(chunkSize);
86 }
87 private void TruncatePerFact() // TODO: MORE THOROUGH VALIDATION?
88 {
89 if(sampleCount == -1) // not explicitly set (no fact chunk present)
90 {
91 Console.WriteLine("The imported WAV file has no FACT chunk.");
92 }
93 else if (format == WavFormat.Adpcm) // calculate truncated data size
94 {
95 var blockSizeADPCM = blockAlign;
96 var samplesPerBlock = 2 + (blockSizeADPCM - channelCount * 7) * 8 / channelCount / bitsPerSample;
97 int wholeBlocks = sampleCount / samplesPerBlock;
98 if (wholeBlocks * blockAlign > soundData.Length)
99 Console.Error.WriteLine("Sample count exceeds the range of sound data.");
100 int leftoverSamples = sampleCount - wholeBlocks * samplesPerBlock;
101 if (leftoverSamples < 2) // a block always starts with at least two samples?
102 Console.Error.WriteLine("Improper trailing bytes/samples!");
103 if (bitsPerSample != 4) // are MS ADPCM nibbles always 4-bit-sized?
104 Console.Error.WriteLine("Nibble size is expected to be 4 bits!");
105 int leftoverNibbles = (leftoverSamples - 2) * channelCount;
106 int leftoverBytes = 7 * channelCount
107 + (int)Math.Ceiling((leftoverNibbles * bitsPerSample) * 0.125);
108 Array.Resize(ref soundData, wholeBlocks * blockAlign + leftoverBytes);
109 }
110 }
111
112 public WavFormat Format => format;
113 public int ChannelCount => channelCount;
114 public int SampleRate => sampleRate;
115 public int AverageBytesPerSecond => averageBytesPerSecond;
116 public int BlockAlign => blockAlign;
117 public int BitsPerSample => bitsPerSample;
118 public int SampleCount => sampleCount;
119 public byte[] ExtraData => extraData;
120 public byte[] SoundData => soundData;
121 }
122}
Note: See TracBrowser for help on using the repository browser.