Files
OpenRA/OpenRA.Game/FileFormats/WavLoader.cs
RoosterDragon a6cda967c2 Formatted all files.
Automatically formatted all files via VS. This generally corrects indentation, removes trailing whitespace and corrects misplaced tabs or spaces. Manually tweaked a few files where required.
2015-01-06 21:28:50 +00:00

180 lines
4.4 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.IO;
namespace OpenRA.FileFormats
{
public class WavLoader
{
public readonly int FileSize;
public readonly string Format;
public readonly int FmtChunkSize;
public readonly int AudioFormat;
public readonly int Channels;
public readonly int SampleRate;
public readonly int ByteRate;
public readonly int BlockAlign;
public readonly int BitsPerSample;
public readonly int UncompressedSize;
public readonly int DataSize;
public readonly byte[] RawOutput;
public enum WaveType { Pcm = 0x1, ImaAdpcm = 0x11 }
public static WaveType Type { get; private set; }
public WavLoader(Stream s)
{
while (s.Position < s.Length)
{
if ((s.Position & 1) == 1)
s.ReadByte(); // Alignment
var type = s.ReadASCII(4);
switch (type)
{
case "RIFF":
FileSize = s.ReadInt32();
Format = s.ReadASCII(4);
if (Format != "WAVE")
throw new NotSupportedException("Not a canonical WAVE file.");
break;
case "fmt ":
FmtChunkSize = s.ReadInt32();
AudioFormat = s.ReadInt16();
Type = (WaveType)AudioFormat;
if (Type != WaveType.Pcm && Type != WaveType.ImaAdpcm)
throw new NotSupportedException("Compression type is not supported.");
Channels = s.ReadInt16();
SampleRate = s.ReadInt32();
ByteRate = s.ReadInt32();
BlockAlign = s.ReadInt16();
BitsPerSample = s.ReadInt16();
s.ReadBytes(FmtChunkSize - 16);
break;
case "fact":
{
var chunkSize = s.ReadInt32();
UncompressedSize = s.ReadInt32();
s.ReadBytes(chunkSize - 4);
}
break;
case "data":
DataSize = s.ReadInt32();
RawOutput = s.ReadBytes(DataSize);
break;
default:
// Ignore unknown chunks
{
var chunkSize = s.ReadInt32();
s.ReadBytes(chunkSize);
}
break;
}
}
if (Type == WaveType.ImaAdpcm)
{
RawOutput = DecodeImaAdpcmData();
BitsPerSample = 16;
}
}
public static float WaveLength(Stream s)
{
s.Position = 12;
var fmt = s.ReadASCII(4);
if (fmt != "fmt ")
return 0;
s.Position = 22;
var channels = s.ReadInt16();
var sampleRate = s.ReadInt32();
s.Position = 34;
var bitsPerSample = s.ReadInt16();
var length = s.Length * 8;
return length / (channels * sampleRate * bitsPerSample);
}
public byte[] DecodeImaAdpcmData()
{
var s = new MemoryStream(RawOutput);
var numBlocks = DataSize / BlockAlign;
var blockDataSize = BlockAlign - (Channels * 4);
var outputSize = UncompressedSize * Channels * 2;
var outOffset = 0;
var output = new byte[outputSize];
var predictor = new int[Channels];
var index = new int[Channels];
// Decode each block of IMA ADPCM data in RawOutput
for (var block = 0; block < numBlocks; block++)
{
// Each block starts with a initial state per-channel
for (var c = 0; c < Channels; c++)
{
predictor[c] = s.ReadInt16();
index[c] = s.ReadUInt8();
/* unknown/reserved */ s.ReadUInt8();
// Output first sample from input
output[outOffset++] = (byte)predictor[c];
output[outOffset++] = (byte)(predictor[c] >> 8);
if (outOffset >= outputSize)
return output;
}
// Decode and output remaining data in this block
var blockOffset = 0;
while (blockOffset < blockDataSize)
{
for (var c = 0; c < Channels; c++)
{
// Decode 4 bytes (to 16 bytes of output) per channel
var chunk = s.ReadBytes(4);
var decoded = ImaAdpcmLoader.LoadImaAdpcmSound(chunk, ref index[c], ref predictor[c]);
// Interleave output, one sample per channel
var outOffsetChannel = outOffset + (2 * c);
for (var i = 0; i < decoded.Length; i += 2)
{
var outOffsetSample = outOffsetChannel + i;
if (outOffsetSample >= outputSize)
return output;
output[outOffsetSample] = decoded[i];
output[outOffsetSample + 1] = decoded[i + 1];
outOffsetChannel += 2 * (Channels - 1);
}
blockOffset += 4;
}
outOffset += 16 * Channels;
}
}
return output;
}
}
}