using System; using System.IO; // This file is part of the ANX.Framework and originally taken from // the AC.AL OpenAL library, released under the MIT License. // For details see: http://acal.codeplex.com/license namespace WaveUtils { /// /// http://wiki.multimedia.cx/index.php?title=Microsoft_ADPCM /// /// http://dslinux.gits.kiev.ua/trunk/lib/audiofile/src/libaudiofile/modules/msadpcm.c /// /// http://netghost.narod.ru/gff/vendspec/micriff/ms_riff.txt /// internal static class MsAdpcm { #region Decoding Tables private static readonly int[] AdaptationTable = { 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230 }; private static readonly int[] AdaptCoeff1 = { 256, 512, 0, 192, 240, 460, 392 }; private static readonly int[] AdaptCoeff2 = { 0, -256, 0, 64, 0, -208, -232 }; #endregion #region StateObject (helper class) private class StateObject { /// /// Indices in the aCoef array to define the predictor /// used to encode this block. /// public byte predicator; /// /// Initial Delta value to use. /// public short delta; /// /// The second sample value of the block. When decoding this will be /// used as the previous sample to start decoding with. /// public short sample1; /// /// The first sample value of the block. When decoding this will be /// used as the previous' previous sample to start decoding with. /// public short sample2; } #endregion #region ConvertToPcm public static void ConvertToPcm(WaveInfo info) { BinaryReader reader = new BinaryReader(new MemoryStream(info.Data)); MemoryStream result = new MemoryStream(); BinaryWriter writer = new BinaryWriter(result); StateObject first = new StateObject(); StateObject second = info.Channels > 1 ? new StateObject() : first; StateObject[] states = { first, second }; int numSamples = (info.ExtSamplesPerBlock - 2) * info.Channels; int numberOfBlocks = info.Data.Length / info.BlockAlign; for (int blockIndex = 0; blockIndex < numberOfBlocks; blockIndex++) { for (int index = 0; index < info.Channels; index++) { states[index].predicator = reader.ReadByte(); } for (int index = 0; index < info.Channels; index++) { states[index].delta = reader.ReadInt16(); } for (int index = 0; index < info.Channels; index++) { states[index].sample1 = reader.ReadInt16(); } for (int index = 0; index < info.Channels; index++) { states[index].sample2 = reader.ReadInt16(); // Write first samples directly from preamble writer.Write(states[index].sample2); } // Write first samples directly from preamble for (int index = 0; index < info.Channels; index++) { writer.Write(states[index].sample1); } // We decode the samples two at a time for (int index = 0; index < numSamples; index += 2) { byte code = reader.ReadByte(); DecodeSample(first, code >> 4, writer); DecodeSample(second, code & 0x0f, writer); } } reader.Close(); writer.Close(); info.Data = result.ToArray(); result.Dispose(); } #endregion #region DecodeSample private static void DecodeSample(StateObject state, int code, BinaryWriter writer) { int linearSample = ((state.sample1 * AdaptCoeff1[state.predicator]) + (state.sample2 * AdaptCoeff2[state.predicator])) / 256; linearSample = linearSample + (state.delta * ((code & 0x08) == 0x08 ? (code - 0x10) : code)); state.sample2 = state.sample1; // clamp predictor within signed 16-bit range state.sample1 = (short)Math.Min(short.MaxValue, Math.Max(short.MinValue, linearSample)); state.delta = (short)Math.Max( (state.delta * AdaptationTable[code]) / 256, 16); writer.Write(state.sample1); } #endregion } }