(MvReader): renamed to MvDecoder and moved common methods to separate base class.

This commit is contained in:
morkt 2016-12-03 19:15:35 +04:00
parent 1af4cd8396
commit 0075eb9b63

View File

@ -28,7 +28,7 @@ using System.ComponentModel.Composition;
using System.IO; using System.IO;
using GameRes.Utility; using GameRes.Utility;
namespace GameRes.Formats.Pvns namespace GameRes.Formats.Purple
{ {
[Export(typeof(AudioFormat))] [Export(typeof(AudioFormat))]
public class MvAudio : AudioFormat public class MvAudio : AudioFormat
@ -39,7 +39,7 @@ namespace GameRes.Formats.Pvns
public override SoundInput TryOpen (IBinaryStream file) public override SoundInput TryOpen (IBinaryStream file)
{ {
using (var reader = new MvReader (file)) using (var reader = new MvDecoder (file))
{ {
reader.Unpack(); reader.Unpack();
var input = new MemoryStream (reader.Data); var input = new MemoryStream (reader.Data);
@ -50,36 +50,98 @@ namespace GameRes.Formats.Pvns
} }
} }
internal sealed class MvReader : IDisposable internal class MvDecoderBase : IDisposable
{ {
IBinaryStream m_input; private IBinaryStream m_input;
byte[] m_output; private int m_bits;
WaveFormat m_format; private int m_bits_count = 0;
int m_channel_size;
int m_samples; protected WaveFormat m_format;
protected byte[] m_output;
protected int m_channel_size;
public byte[] Data { get { return m_output; } } public byte[] Data { get { return m_output; } }
public WaveFormat Format { get { return m_format; } } public WaveFormat Format { get { return m_format; } }
public MvReader (IBinaryStream input) protected MvDecoderBase (IBinaryStream input)
{ {
var header = new byte[0x12]; m_input = input;
input.Read (header, 0, header.Length); }
m_channel_size = LittleEndian.ToInt32 (header, 4);
internal void SetPosition (long pos)
{
m_input.Position = pos;
m_bits_count = 0;
}
internal int GetBits (int count)
{
int v = 0;
while (count --> 0)
{
if (0 == m_bits_count)
{
m_bits = m_input.ReadInt32();
m_bits_count = 32;
}
v = (v << 1) | (m_bits & 1);
m_bits >>= 1;
--m_bits_count;
}
return v;
}
internal int GetCount ()
{
int n = 0;
while (GetBits (1) > 0)
++n;
return n;
}
internal static short Clamp (int sample)
{
if (sample > 0x7FFF)
return 0x7FFF;
else if (sample < -0x7FFF)
return -0x7FFF;
else
return (short)sample;
}
#region IDisposable Members
public void Dispose ()
{
Dispose (true);
}
protected virtual void Dispose (bool disposing)
{
}
#endregion
}
internal sealed class MvDecoder : MvDecoderBase
{
int m_samples;
public MvDecoder (IBinaryStream input) : base (input)
{
var header = input.ReadHeader (0x12);
m_channel_size = header.ToInt32 (4);
m_format.FormatTag = 1; m_format.FormatTag = 1;
m_format.BitsPerSample = 16; m_format.BitsPerSample = 16;
m_format.Channels = header[0xC]; m_format.Channels = header[0xC];
m_format.SamplesPerSecond = LittleEndian.ToUInt16 (header, 0xA); m_format.SamplesPerSecond = header.ToUInt16 (0xA);
m_format.BlockAlign = (ushort)(m_format.Channels*m_format.BitsPerSample/8); m_format.BlockAlign = (ushort)(m_format.Channels*m_format.BitsPerSample/8);
m_format.AverageBytesPerSecond = m_format.SamplesPerSecond*m_format.BlockAlign; m_format.AverageBytesPerSecond = m_format.BlockAlign * m_format.SamplesPerSecond;
m_input = input; m_output = new byte[m_format.BlockAlign * m_channel_size];
m_output = new byte[2 * m_format.Channels * m_channel_size]; m_samples = header.ToInt32 (0xE);
m_samples = LittleEndian.ToInt32 (header, 0xE);
} }
public void Unpack () public void Unpack ()
{ {
m_input.Position = 0x12; SetPosition (0x12);
var pre_sample1 = new int[0x400]; var pre_sample1 = new int[0x400];
var pre_sample2 = new int[0x400]; var pre_sample2 = new int[0x400];
var pre_sample3 = new int[0x140 * m_format.Channels]; var pre_sample3 = new int[0x140 * m_format.Channels];
@ -95,11 +157,7 @@ namespace GameRes.Formats.Pvns
pre_sample1[j] = 0; pre_sample1[j] = 0;
for (int j = 0; j < count; ++j) for (int j = 0; j < count; ++j)
{ {
int bit_count = 0; int bit_count = GetCount();
while (0 != GetBits (1))
{
++bit_count;
}
if (bit_count != 0) if (bit_count != 0)
{ {
int coef = GetBits (bit_count); int coef = GetBits (bit_count);
@ -147,45 +205,15 @@ namespace GameRes.Formats.Pvns
} }
for (int j = 0; j < 0x140; ++j) for (int j = 0; j < 0x140; ++j)
{ {
int sample = pre_sample3[j] >> 1; short sample = Clamp (pre_sample3[j] >> 1);
if (sample > 0x7FFF) LittleEndian.Pack (sample, m_output, dst);
sample = 0x7FFF;
else if (sample < -0x7FFF)
sample = -0x7FFF;
LittleEndian.Pack ((short)sample, m_output, dst);
dst += 2; // ??? shouldn't channel interleaving be taken into account? dst += 2; // ??? shouldn't channel interleaving be taken into account?
} }
} }
} }
} }
int m_bits; static MvDecoder ()
int m_bits_count = 0;
int GetBits (int count)
{
int v = 0;
while (count --> 0)
{
if (0 == m_bits_count)
{
m_bits = m_input.ReadInt32();
m_bits_count = 32;
}
v = (v << 1) | (m_bits & 1);
m_bits >>= 1;
--m_bits_count;
}
return v;
}
#region IDisposable Members
public void Dispose ()
{
}
#endregion
static MvReader ()
{ {
var table = new uint[0x400]; var table = new uint[0x400];
Array.Copy (SampleTable, 0, table, 0x192, SampleTable.Length); Array.Copy (SampleTable, 0, table, 0x192, SampleTable.Length);
@ -233,7 +261,7 @@ namespace GameRes.Formats.Pvns
0x07D2F200, 0x9A0BEA00, 0x8C1F4A00, 0x22092C00, 0xD5107C00, 0x8A20AC00, 0xDDBFC000, 0x07CE7800, 0x07D2F200, 0x9A0BEA00, 0x8C1F4A00, 0x22092C00, 0xD5107C00, 0x8A20AC00, 0xDDBFC000, 0x07CE7800,
}; };
static readonly short[] Coef1Table = { internal static readonly short[] Coef1Table = {
724, -724, -724, 724, 724, -724, -724, 724, 724, -724, -724, 724, 724, -724, -724, 724, 724, -724, -724, 724, 724, -724, -724, 724, 724, -724, -724, 724, 724, -724, -724, 724,
724, -724, -724, 724, 724, -724, -724, 724, 724, -724, -724, 724, 724, -724, -724, 724, 724, -724, -724, 724, 724, -724, -724, 724, 724, -724, -724, 724, 724, -724, -724, 724,
687, -822, -526, 925, 344, -993, -150, 1022, -50, -1012, 248, 964, -437, -878, 609, 758, 687, -822, -526, 925, 344, -993, -150, 1022, -50, -1012, 248, 964, -437, -878, 609, 758,