mirror of
https://github.com/crskycode/GARbro.git
synced 2024-12-26 12:54:13 +08:00
161 lines
6.4 KiB
C#
161 lines
6.4 KiB
C#
|
//! \file AudioWV1.cs
|
||
|
//! \date 2018 Dec 13
|
||
|
//! \brief Aaru compressed audio format.
|
||
|
//
|
||
|
// Copyright (C) 2018 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;
|
||
|
using System.ComponentModel.Composition;
|
||
|
using System.IO;
|
||
|
using System.Text;
|
||
|
|
||
|
namespace GameRes.Formats.Aaru
|
||
|
{
|
||
|
[Export(typeof(AudioFormat))]
|
||
|
public class Wv1Audio : AudioFormat
|
||
|
{
|
||
|
public override string Tag { get { return "WV1"; } }
|
||
|
public override string Description { get { return "Aaru compressed audio"; } }
|
||
|
public override uint Signature { get { return 0x2E315657; } } // 'WV1.0'
|
||
|
public override bool CanWrite { get { return false; } }
|
||
|
|
||
|
public Wv1Audio ()
|
||
|
{
|
||
|
Extensions = new string[] { "wv1", "wav" };
|
||
|
}
|
||
|
|
||
|
public override SoundInput TryOpen (IBinaryStream file)
|
||
|
{
|
||
|
var header = file.ReadHeader (0x30);
|
||
|
if (!header.AsciiEqual (0, "WV1.0\0"))
|
||
|
return null;
|
||
|
var format = new WaveFormat {
|
||
|
FormatTag = 1,
|
||
|
Channels = header.ToUInt16 (0xA),
|
||
|
SamplesPerSecond = header.ToUInt32 (0xE),
|
||
|
BitsPerSample = 16,
|
||
|
};
|
||
|
format.BlockAlign = (ushort)(format.Channels * format.BitsPerSample / 8);
|
||
|
format.SetBPS();
|
||
|
int sample_count = header.ToInt32 (0x26);
|
||
|
var pcm = new MemoryStream (2 * sample_count);
|
||
|
using (var output = new BinaryWriter (pcm, Encoding.ASCII, true))
|
||
|
{
|
||
|
var l_decoder = new Wv1Decoder();
|
||
|
var r_decoder = l_decoder;
|
||
|
if (format.Channels > 1)
|
||
|
r_decoder = new Wv1Decoder();
|
||
|
int input_sample = 0;
|
||
|
for (int i = 0; i < sample_count; ++i)
|
||
|
{
|
||
|
bool odd_sample = (i & 1) != 0;
|
||
|
short sample;
|
||
|
if (odd_sample)
|
||
|
{
|
||
|
sample = r_decoder.DecodeSample (input_sample >> 4);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
input_sample = file.ReadByte();
|
||
|
if (-1 == input_sample)
|
||
|
break;
|
||
|
sample = l_decoder.DecodeSample (input_sample & 0xF);
|
||
|
}
|
||
|
output.Write (sample);
|
||
|
}
|
||
|
}
|
||
|
file.Dispose();
|
||
|
pcm.Position = 0;
|
||
|
return new RawPcmInput (pcm, format);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal class Wv1Decoder
|
||
|
{
|
||
|
short last_sample = 0;
|
||
|
int last_index = 0;
|
||
|
int[] shift_table = new int[8];
|
||
|
|
||
|
public short DecodeSample (int input)
|
||
|
{
|
||
|
int s0 = SampleTable[last_index];
|
||
|
shift_table[0] = s0 >> 3;
|
||
|
shift_table[1] = shift_table[0] + (s0 >> 2);
|
||
|
shift_table[2] = shift_table[0] + (s0 >> 1);
|
||
|
shift_table[3] = shift_table[1] + (s0 >> 1);
|
||
|
shift_table[4] = shift_table[0] + s0;
|
||
|
shift_table[5] = shift_table[1] + s0;
|
||
|
shift_table[6] = shift_table[2] + s0;
|
||
|
shift_table[7] = shift_table[3] + s0;
|
||
|
int shift_index = input & 7;
|
||
|
if ((input & 8) != 0)
|
||
|
last_sample -= (short)shift_table[shift_index];
|
||
|
else
|
||
|
last_sample += (short)shift_table[shift_index];
|
||
|
switch (shift_index)
|
||
|
{
|
||
|
case 0:
|
||
|
case 1:
|
||
|
case 2:
|
||
|
case 3:
|
||
|
if (last_index > 0)
|
||
|
--last_index;
|
||
|
break;
|
||
|
case 4:
|
||
|
last_index = Math.Min (last_index + 2, 0x7F);
|
||
|
break;
|
||
|
case 5:
|
||
|
last_index = Math.Min (last_index + 4, 0x7F);
|
||
|
break;
|
||
|
case 6:
|
||
|
last_index = Math.Min (last_index + 6, 0x7F);
|
||
|
break;
|
||
|
case 7:
|
||
|
last_index = Math.Min (last_index + 8, 0x7F);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return (short)(2 * last_sample);
|
||
|
}
|
||
|
|
||
|
static readonly short[] SampleTable = {
|
||
|
0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000E, 0x000F,
|
||
|
0x0010, 0x0012, 0x0014, 0x0015, 0x0017, 0x0019, 0x001B, 0x001D,
|
||
|
0x0020, 0x0022, 0x0025, 0x0028, 0x002B, 0x002E, 0x0031, 0x0035,
|
||
|
0x0038, 0x003C, 0x0041, 0x0045, 0x004A, 0x004F, 0x0055, 0x005A,
|
||
|
0x0061, 0x0067, 0x006E, 0x0075, 0x007D, 0x0085, 0x008E, 0x0098,
|
||
|
0x00A2, 0x00AC, 0x00B7, 0x00C3, 0x00D0, 0x00DE, 0x00EC, 0x00FB,
|
||
|
0x010B, 0x011C, 0x012F, 0x0142, 0x0157, 0x016D, 0x0184, 0x019C,
|
||
|
0x01B7, 0x01D3, 0x01F0, 0x0210, 0x0231, 0x0254, 0x027A, 0x02A2,
|
||
|
0x02CD, 0x02FA, 0x032A, 0x035D, 0x0393, 0x03CD, 0x040A, 0x044B,
|
||
|
0x0490, 0x04D9, 0x0527, 0x057A, 0x05D2, 0x062F, 0x0693, 0x06FC,
|
||
|
0x076C, 0x07E3, 0x0862, 0x08E8, 0x0977, 0x0A0E, 0x0AAF, 0x0B5A,
|
||
|
0x0C10, 0x0CD1, 0x0D9E, 0x0E78, 0x0F60, 0x1056, 0x115B, 0x1270,
|
||
|
0x1397, 0x14D1, 0x161D, 0x177F, 0x18F7, 0x1A86, 0x1C2E, 0x1DF0,
|
||
|
0x1FCF, 0x21CB, 0x23E7, 0x2625, 0x2886, 0x2B0E, 0x2DBE, 0x3098,
|
||
|
0x33A1, 0x36D9, 0x3A46, 0x3DE9, 0x41C5, 0x45E0, 0x4A3C, 0x4EDE,
|
||
|
0x53CA, 0x5904, 0x5E92, 0x6478, 0x6ABC, 0x7165, 0x7878, 0x7FFF,
|
||
|
};
|
||
|
}
|
||
|
}
|