GARbro-mirror/ArcFormats/Ffa/AudioWA2.cs

158 lines
5.5 KiB
C#
Raw Normal View History

2015-11-12 02:38:38 +08:00
//! \file AudioWA2.cs
//! \date Wed Nov 11 21:14:30 2015
//! \brief FFA System compressed PCM format.
//
// Copyright (C) 2015 by morkt
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
using System.ComponentModel.Composition;
using System.IO;
using GameRes.Utility;
namespace GameRes.Formats.Ffa
{
[Export(typeof(AudioFormat))]
public class Wa2Audio : AudioFormat
{
public override string Tag { get { return "WA2"; } }
public override string Description { get { return "FFA System PCM audio format"; } }
public override uint Signature { get { return 0x4D435041; } } // 'APCM'
public override SoundInput TryOpen (Stream file)
{
return new Wa2Input (file);
}
}
public class Wa2Input : SoundInput
{
public override string SourceFormat { get { return "raw"; } }
public override int SourceBitrate
{
get { return (int)Format.AverageBytesPerSecond * 8; }
}
public Wa2Input (Stream file) : base (null)
{
var header = new byte[0x2C];
if (header.Length != file.Read (header, 0, header.Length))
throw new EndOfStreamException();
if (!Binary.AsciiEqual (header, 8, "WAVEfmt "))
throw new InvalidFormatException();
var format = new WaveFormat();
format.FormatTag = LittleEndian.ToUInt16 (header, 0x14);
format.Channels = LittleEndian.ToUInt16 (header, 0x16);
format.SamplesPerSecond = LittleEndian.ToUInt32 (header, 0x18);
format.AverageBytesPerSecond = LittleEndian.ToUInt32 (header, 0x1C);
format.BlockAlign = LittleEndian.ToUInt16 (header, 0x20);
format.BitsPerSample = LittleEndian.ToUInt16 (header, 0x22);
format.ExtraSize = 0;
this.Format = format;
uint pcm_size = LittleEndian.ToUInt32 (header, 0x28);
var pcm = new byte[pcm_size];
Decode (file, pcm);
Source = new MemoryStream (pcm);
this.PcmSize = pcm_size;
file.Dispose();
}
void Decode (Stream input, byte[] output)
{
int sample = 0;
bool nibble_read = false;
short v7 = 0x7F;
int input_byte = 0;
int dst = 0;
while (dst < output.Length)
{
byte nibble;
if (!nibble_read)
{
input_byte = input.ReadByte();
if (-1 == input_byte)
break;
nibble = (byte)(input_byte >> 4);
}
else
{
nibble = (byte)(input_byte & 0xF);
}
nibble_read = !nibble_read;
int v11 = (ushort)v7;
int diff = (short)(v11 * (byte)(2 * (nibble & 7) + 1) >> 3);
if (0 != (nibble & 8))
{
sample -= diff;
if (sample < -32768)
sample = -32768;
}
else
{
sample += diff;
if (sample > 0x7FFF)
sample = 0x7FFF;
}
ushort v13 = (ushort)(v11 * Wa1Reader.SampleTable[nibble] >> 6);
if (v13 > 0x7F)
{
v7 = 0x6000;
if (v13 < 0x6001)
v7 = (short)v13;
}
else
{
v7 = 0x7F;
}
LittleEndian.Pack ((ushort)sample, output, dst);
dst += 2;
}
}
#region IO.Stream methods
public override long Position
{
get { return Source.Position; }
set { Source.Position = value; }
}
public override bool CanSeek { get { return Source.CanSeek; } }
public override long Seek (long offset, SeekOrigin origin)
{
return Source.Seek (offset, origin);
}
public override int Read (byte[] buffer, int offset, int count)
{
return Source.Read (buffer, offset, count);
}
public override int ReadByte ()
{
return Source.ReadByte();
}
#endregion
}
}