-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1335bfd
commit 8368a4c
Showing
22 changed files
with
3,626 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio 15 | ||
VisualStudioVersion = 15.0.27130.2003 | ||
MinimumVisualStudioVersion = 15.0.26124.0 | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibAtrac9", "LibAtrac9\LibAtrac9.csproj", "{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Debug|ARM = Debug|ARM | ||
Debug|x64 = Debug|x64 | ||
Debug|x86 = Debug|x86 | ||
Release|Any CPU = Release|Any CPU | ||
Release|ARM = Release|ARM | ||
Release|x64 = Release|x64 | ||
Release|x86 = Release|x86 | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|ARM.ActiveCfg = Debug|Any CPU | ||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|ARM.Build.0 = Debug|Any CPU | ||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|x64.ActiveCfg = Debug|Any CPU | ||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|x64.Build.0 = Debug|Any CPU | ||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|x86.ActiveCfg = Debug|Any CPU | ||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|x86.Build.0 = Debug|Any CPU | ||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|Any CPU.Build.0 = Release|Any CPU | ||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|ARM.ActiveCfg = Release|Any CPU | ||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|ARM.Build.0 = Release|Any CPU | ||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|x64.ActiveCfg = Release|Any CPU | ||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|x64.Build.0 = Release|Any CPU | ||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|x86.ActiveCfg = Release|Any CPU | ||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|x86.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {2AFD09F0-D995-47ED-AA86-FFA93824A514} | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
using System.IO; | ||
using LibAtrac9.Utilities; | ||
|
||
namespace LibAtrac9 | ||
{ | ||
/// <summary> | ||
/// Stores the configuration data needed to decode or encode an ATRAC9 stream. | ||
/// </summary> | ||
public class Atrac9Config | ||
{ | ||
/// <summary> | ||
/// The 4-byte ATRAC9 configuration data. | ||
/// </summary> | ||
public byte[] ConfigData { get; } | ||
|
||
/// <summary> | ||
/// A 4-bit value specifying one of 16 sample rates. | ||
/// </summary> | ||
public int SampleRateIndex { get; } | ||
/// <summary> | ||
/// A 3-bit value specifying one of 6 substream channel mappings. | ||
/// </summary> | ||
public int ChannelConfigIndex { get; } | ||
/// <summary> | ||
/// An 11-bit value containing the average size of a single frame. | ||
/// </summary> | ||
public int FrameBytes { get; } | ||
/// <summary> | ||
/// A 2-bit value indicating how many frames are in each superframe. | ||
/// </summary> | ||
public int SuperframeIndex { get; } | ||
|
||
/// <summary> | ||
/// The channel mapping used by the ATRAC9 stream. | ||
/// </summary> | ||
public ChannelConfig ChannelConfig { get; } | ||
/// <summary> | ||
/// The total number of channels in the ATRAC9 stream. | ||
/// </summary> | ||
public int ChannelCount { get; } | ||
/// <summary> | ||
/// The sample rate of the ATRAC9 stream. | ||
/// </summary> | ||
public int SampleRate { get; } | ||
/// <summary> | ||
/// Indicates whether the ATRAC9 stream has a <see cref="SampleRateIndex"/> of 8 or above. | ||
/// </summary> | ||
public bool HighSampleRate { get; } | ||
|
||
/// <summary> | ||
/// The number of frames in each superframe. | ||
/// </summary> | ||
public int FramesPerSuperframe { get; } | ||
/// <summary> | ||
/// The number of samples in one frame as an exponent of 2. | ||
/// <see cref="FrameSamples"/> = 2^<see cref="FrameSamplesPower"/>. | ||
/// </summary> | ||
public int FrameSamplesPower { get; } | ||
/// <summary> | ||
/// The number of samples in one frame. | ||
/// </summary> | ||
public int FrameSamples { get; } | ||
/// <summary> | ||
/// The number of bytes in one superframe. | ||
/// </summary> | ||
public int SuperframeBytes { get; } | ||
/// <summary> | ||
/// The number of samples in one superframe. | ||
/// </summary> | ||
public int SuperframeSamples { get; } | ||
|
||
/// <summary> | ||
/// Reads ATRAC9 configuration data and calculates the stream parameters from it. | ||
/// </summary> | ||
/// <param name="configData">The processed ATRAC9 configuration.</param> | ||
public Atrac9Config(byte[] configData) | ||
{ | ||
if (configData == null || configData.Length != 4) | ||
{ | ||
throw new InvalidDataException("Config data must be 4 bytes long"); | ||
} | ||
|
||
ReadConfigData(configData, out int a, out int b, out int c, out int d); | ||
SampleRateIndex = a; | ||
ChannelConfigIndex = b; | ||
FrameBytes = c; | ||
SuperframeIndex = d; | ||
ConfigData = configData; | ||
|
||
FramesPerSuperframe = 1 << SuperframeIndex; | ||
SuperframeBytes = FrameBytes << SuperframeIndex; | ||
ChannelConfig = Tables.ChannelConfig[ChannelConfigIndex]; | ||
|
||
ChannelCount = ChannelConfig.ChannelCount; | ||
SampleRate = Tables.SampleRates[SampleRateIndex]; | ||
HighSampleRate = SampleRateIndex > 7; | ||
FrameSamplesPower = Tables.SamplingRateIndexToFrameSamplesPower[SampleRateIndex]; | ||
FrameSamples = 1 << FrameSamplesPower; | ||
SuperframeSamples = FrameSamples * FramesPerSuperframe; | ||
} | ||
|
||
private static void ReadConfigData(byte[] configData, out int sampleRateIndex, out int channelConfigIndex, out int frameBytes, out int superframeIndex) | ||
{ | ||
var reader = new BitReader(configData); | ||
|
||
int header = reader.ReadInt(8); | ||
sampleRateIndex = reader.ReadInt(4); | ||
channelConfigIndex = reader.ReadInt(3); | ||
int validationBit = reader.ReadInt(1); | ||
frameBytes = reader.ReadInt(11) + 1; | ||
superframeIndex = reader.ReadInt(2); | ||
|
||
if (header != 0xFE || validationBit != 0) | ||
{ | ||
throw new InvalidDataException("ATRAC9 Config Data is invalid"); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
using System; | ||
using LibAtrac9.Utilities; | ||
|
||
namespace LibAtrac9 | ||
{ | ||
/// <summary> | ||
/// Decodes an ATRAC9 stream into 16-bit PCM. | ||
/// </summary> | ||
public class Atrac9Decoder | ||
{ | ||
/// <summary> | ||
/// The config data for the current ATRAC9 stream. | ||
/// </summary> | ||
public Atrac9Config Config { get; private set; } | ||
|
||
private Frame Frame { get; set; } | ||
private BitReader Reader { get; set; } | ||
private bool _initialized; | ||
|
||
/// <summary> | ||
/// Sets up the decoder to decode an ATRAC9 stream based on the information in <paramref name="configData"/>. | ||
/// </summary> | ||
/// <param name="configData">A 4-byte value containing information about the ATRAC9 stream.</param> | ||
public void Initialize(byte[] configData) | ||
{ | ||
Config = new Atrac9Config(configData); | ||
Frame = new Frame(Config); | ||
Reader = new BitReader(null); | ||
_initialized = true; | ||
} | ||
|
||
/// <summary> | ||
/// Decodes one superframe of ATRAC9 data. | ||
/// </summary> | ||
/// <param name="atrac9Data">The ATRAC9 data to decode. The array must be at least | ||
/// <see cref="Config"/>.<see cref="Atrac9Config.SuperframeBytes"/> bytes long.</param> | ||
/// <param name="pcmOut">A buffer that the decoded PCM data will be placed in. | ||
/// The array must have dimensions of at least [<see cref="Config"/>.<see cref="Atrac9Config.ChannelCount"/>] | ||
/// [<see cref="Config"/>.<see cref="Atrac9Config.SuperframeSamples"/>].</param> | ||
public void Decode(byte[] atrac9Data, short[][] pcmOut) | ||
{ | ||
if (!_initialized) throw new InvalidOperationException("Decoder must be initialized before decoding."); | ||
|
||
ValidateDecodeBuffers(atrac9Data, pcmOut); | ||
Reader.SetBuffer(atrac9Data); | ||
DecodeSuperFrame(pcmOut); | ||
} | ||
|
||
private void ValidateDecodeBuffers(byte[] atrac9Buffer, short[][] pcmBuffer) | ||
{ | ||
if (atrac9Buffer == null) throw new ArgumentNullException(nameof(atrac9Buffer)); | ||
if (pcmBuffer == null) throw new ArgumentNullException(nameof(pcmBuffer)); | ||
|
||
if (atrac9Buffer.Length < Config.SuperframeBytes) | ||
{ | ||
throw new ArgumentException("ATRAC9 buffer is too small"); | ||
} | ||
|
||
if (pcmBuffer.Length < Config.ChannelCount) | ||
{ | ||
throw new ArgumentException("PCM buffer is too small"); | ||
} | ||
|
||
for (int i = 0; i < Config.ChannelCount; i++) | ||
{ | ||
if (pcmBuffer[i]?.Length < Config.SuperframeSamples) | ||
{ | ||
throw new ArgumentException("PCM buffer is too small"); | ||
} | ||
} | ||
} | ||
|
||
private void DecodeSuperFrame(short[][] pcmOut) | ||
{ | ||
for (int i = 0; i < Config.FramesPerSuperframe; i++) | ||
{ | ||
Frame.FrameIndex = i; | ||
DecodeFrame(Reader, Frame); | ||
PcmFloatToShort(pcmOut, i * Config.FrameSamples); | ||
Reader.AlignPosition(8); | ||
} | ||
} | ||
|
||
private void PcmFloatToShort(short[][] pcmOut, int start) | ||
{ | ||
int endSample = start + Config.FrameSamples; | ||
int channelNum = 0; | ||
foreach (Block block in Frame.Blocks) | ||
{ | ||
foreach (Channel channel in block.Channels) | ||
{ | ||
double[] pcmSrc = channel.Pcm; | ||
short[] pcmDest = pcmOut[channelNum++]; | ||
for (int d = 0, s = start; s < endSample; d++, s++) | ||
{ | ||
double sample = pcmSrc[d]; | ||
// Not using Math.Round because it's ~20x slower on 64-bit | ||
int roundedSample = (int)Math.Floor(sample + 0.5); | ||
pcmDest[s] = Helpers.Clamp16(roundedSample); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private static void DecodeFrame(BitReader reader, Frame frame) | ||
{ | ||
Unpack.UnpackFrame(reader, frame); | ||
|
||
foreach (Block block in frame.Blocks) | ||
{ | ||
Quantization.DequantizeSpectra(block); | ||
Stereo.ApplyIntensityStereo(block); | ||
Quantization.ScaleSpectrum(block); | ||
BandExtension.ApplyBandExtension(block); | ||
ImdctBlock(block); | ||
} | ||
} | ||
|
||
private static void ImdctBlock(Block block) | ||
{ | ||
foreach (Channel channel in block.Channels) | ||
{ | ||
channel.Mdct.RunImdct(channel.Spectra, channel.Pcm); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
namespace LibAtrac9 | ||
{ | ||
/// <summary> | ||
/// An Xorshift RNG used by the ATRAC9 codec | ||
/// </summary> | ||
internal class Atrac9Rng | ||
{ | ||
private ushort _stateA; | ||
private ushort _stateB; | ||
private ushort _stateC; | ||
private ushort _stateD; | ||
|
||
public Atrac9Rng(ushort seed) | ||
{ | ||
int startValue = 0x4D93 * (seed ^ (seed >> 14)); | ||
|
||
_stateA = (ushort)(3 - startValue); | ||
_stateB = (ushort)(2 - startValue); | ||
_stateC = (ushort)(1 - startValue); | ||
_stateD = (ushort)(0 - startValue); | ||
} | ||
|
||
public ushort Next() | ||
{ | ||
ushort t = (ushort)(_stateD ^ (_stateD << 5)); | ||
_stateD = _stateC; | ||
_stateC = _stateB; | ||
_stateB = _stateA; | ||
_stateA = (ushort)(t ^ _stateA ^ ((t ^ (_stateA >> 5)) >> 4)); | ||
return _stateA; | ||
} | ||
} | ||
} |
Oops, something went wrong.