Files
UmaViewer/Assets/Scripts/Audio/ClHcaSharp/HcaDecoder.cs
T
1605296300@qq.com 7607c8b4d2 add acb&awb support
2022-09-07 01:22:44 +08:00

732 lines
26 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using static ClHcaSharp.Constants;
using static ClHcaSharp.Header;
using static ClHcaSharp.Tables;
namespace ClHcaSharp
{
public class HcaDecoder
{
private readonly HcaContext hca;
public HcaDecoder(Stream hcaStream, ulong key)
{
hca = new HcaContext();
InitializeTables();
DecodeHeader(hca, hcaStream);
SetKey(key);
}
public HcaInfo GetInfo()
{
HcaInfo info = new HcaInfo()
{
Version = hca.Version,
HeaderSize = hca.HeaderSize,
SamplingRate = hca.SampleRate,
ChannelCount = hca.ChannelCount,
BlockSize = hca.FrameSize,
BlockCount = hca.FrameCount,
EncoderDelay = hca.EncoderDelay,
EncoderPadding = hca.EncoderPadding,
LoopEnabled = hca.LoopFlag,
LoopStartBlock = hca.LoopStartFrame,
LoopEndBlock = hca.LoopEndFrame,
LoopStartDelay = hca.LoopStartDelay,
LoopEndPadding = hca.LoopEndPadding,
SamplesPerBlock = SamplesPerFrame,
Comment = hca.Comment,
EncryptionEnabled = hca.CiphType == 56
};
return info;
}
public void SetKey(ulong key)
{
hca.KeyCode = key;
hca.CipherTable = Cipher.Init(hca.CiphType, hca.KeyCode);
}
public int TestBlock(byte[] data)
{
const int frameSamples = Subframes * SamplesPerSubframe;
const float scale = 32768.0f;
int status;
int clips = 0;
int blanks = 0;
int[] channelBlanks = new int[MaxChannels];
byte[] buffer = data;
{
bool isEmpty = true;
for (int i = 0x02; i < data.Length - 0x02; i++)
{
if (buffer[i] != 0)
{
isEmpty = false;
break;
}
}
if (isEmpty) return 0;
}
status = DecodeBlockUnpack(data);
if (status < 0) return -1;
{
int bitsMax = hca.FrameSize * 8;
int byteStart;
if (status + 14 > bitsMax)
return -1;
//throw new Exception("BitReader error.");
byteStart = (status / 8) + (status % 8 > 0 ? 0x01 : 0);
for (int i = byteStart; i < hca.FrameSize - 0x02; i++)
{
if (buffer[i] != 0)
return -1;
}
}
DecodeBlockTransform();
for (int ch = 0; ch < hca.ChannelCount; ch++)
{
for (int sf = 0; sf < Subframes; sf++)
{
for (int s = 0; s < SamplesPerSubframe; s++)
{
float fSample = hca.Channels[ch].Wave[sf][s];
if (fSample > 1.0f || fSample < -1.0f)
clips++;
else
{
int pSample = (int)(fSample * scale);
if (pSample == 0 || pSample == -1)
{
blanks++;
channelBlanks[ch]++;
}
}
}
}
}
if (clips == 1)
clips++;
if (clips > 1)
return clips;
if (blanks == hca.ChannelCount * frameSamples)
return 0;
if (hca.ChannelCount >= 2)
{
if (channelBlanks[0] == frameSamples && channelBlanks[1] != frameSamples)
return 3;
}
return 1;
}
public void ReadSamples16(short[][] samples)
{
for (int subframe = 0; subframe < Subframes; subframe++)
{
for (int sample = 0; sample < SamplesPerSubframe; sample++)
{
for (int channel = 0; channel < hca.ChannelCount; channel++)
{
float f = hca.Channels[channel].Wave[subframe][sample];
int sInt = (int)(32768 * f);
if (sInt > 32767)
sInt = 32767;
else if (sInt < -32767)
sInt = -32767;
samples[channel][(SamplesPerSubframe * subframe) + sample] = (short)sInt;
}
}
}
}
public void ReadSamples16(short[] samples)
{
int sampleIndex = 0;
for (int subframe = 0; subframe < Subframes; subframe++)
{
for (int sample = 0; sample < SamplesPerSubframe; sample++)
{
for (int channel = 0; channel < hca.ChannelCount; channel++)
{
float f = hca.Channels[channel].Wave[subframe][sample];
int sInt = (int)(32768 * f);
if (sInt > 32767)
sInt = 32767;
else if (sInt < -32767)
sInt = -32767;
samples[sampleIndex++] = (short)sInt;
}
}
}
}
public float[] ReadSamples16()
{
List<float> data = new List<float>();
for (int subframe = 0; subframe < Subframes; subframe++)
{
for (int sample = 0; sample < SamplesPerSubframe; sample++)
{
for (int channel = 0; channel < hca.ChannelCount; channel++)
{
float f = hca.Channels[channel].Wave[subframe][sample];
data.Add(f);
}
}
}
return data.ToArray();
}
public void DecodeReset()
{
if (hca != null)
{
hca.Random = DefaultRandom;
for (int i = 0; i < hca.ChannelCount; i++)
{
Channel channel = hca.Channels[i];
Array.Clear(channel.ImdctPrevious, 0, channel.ImdctPrevious.Length);
}
}
}
public int DecodeBlock(byte[] data)
{
int result = DecodeBlockUnpack(data);
if (result < 0) return result;
DecodeBlockTransform();
return result;
}
private int DecodeBlockUnpack(byte[] data)
{
if (data.Length < hca.FrameSize)
throw new ArgumentException("Data is less than expected frame size.");
BitReader bitReader = new BitReader(data);
ushort sync = (ushort)bitReader.Read(16);
if (sync != 0xFFFF) throw new InvalidDataException("Sync error.");
if (Crc.Crc16Checksum(data) > 0) throw new InvalidDataException("Checksum error.");
Cipher.Decrypt(hca.CipherTable, data);
int frameAcceptableNoiseLevel = bitReader.Read(9);
int frameEvaluationBoundary = bitReader.Read(7);
int packedNoiseLevel = (frameAcceptableNoiseLevel << 8) - frameEvaluationBoundary;
for (int channel = 0; channel < hca.ChannelCount; channel++)
{
UnpackScaleFactors(hca.Channels[channel], bitReader, hca.HfrGroupCount, hca.Version);
UnpackIntensity(hca.Channels[channel], bitReader, hca.HfrGroupCount, hca.Version);
CalculateResolution(hca.Channels[channel], packedNoiseLevel, hca.AthCurve, hca.MinResolution, hca.MaxResolution);
CalculateGain(hca.Channels[channel]);
}
for (int subframe = 0; subframe < Subframes; subframe++)
{
for (int channel = 0; channel < hca.ChannelCount; channel++)
{
DequantizeCoefficients(hca.Channels[channel], bitReader, subframe);
}
}
return bitReader.Bit;
}
private void DecodeBlockTransform()
{
for (int subframe = 0; subframe < Subframes; subframe++)
{
for (int channel = 0; channel < hca.ChannelCount; channel++)
{
int random = hca.Random;
ReconstructNoise(hca.Channels[channel], hca.MinResolution, hca.MsStereo, ref random, subframe);
hca.Random = random;
ReconstructHighFrequency(hca.Channels[channel], hca.HfrGroupCount, hca.BandsPerHfrGroup,
hca.StereoBandCount, hca.BaseBandCount, hca.TotalBandCount, hca.Version, subframe);
}
if (hca.StereoBandCount > 0)
{
for (int ch = 0; ch < hca.ChannelCount - 1; ch++)
{
ApplyIntensityStereo(hca.Channels, ch * 2, subframe, hca.BaseBandCount, hca.TotalBandCount);
ApplyMsStereo(hca.Channels, ch * 2, hca.MsStereo, hca.BaseBandCount, hca.TotalBandCount, subframe);
}
}
for (int channel = 0; channel < hca.ChannelCount; channel++)
{
ImdctTransform(hca.Channels[channel], subframe);
}
}
}
private static void UnpackScaleFactors(Channel channel, BitReader bitReader, int hfrGroupCount, int version)
{
int csCount = channel.CodedCount;
int extraCount;
byte deltaBits = (byte)bitReader.Read(3);
if (channel.Type == ChannelType.StereoSecondary || hfrGroupCount <= 0 || version <= Version200)
extraCount = 0;
else
{
extraCount = hfrGroupCount;
csCount += extraCount;
if (csCount > SamplesPerSubframe)
return;
//throw new InvalidDataException("Invalid scale count.");
}
if (deltaBits >= 6)
{
for (int i = 0; i < csCount; i++)
{
channel.ScaleFactors[i] = (byte)bitReader.Read(6);
}
}
else if (deltaBits > 0)
{
byte expectedDelta = (byte)((1 << deltaBits) - 1);
byte value = (byte)bitReader.Read(6);
channel.ScaleFactors[0] = value;
for (int i = 1; i < csCount; i++)
{
byte delta = (byte)bitReader.Read(deltaBits);
if (delta == expectedDelta)
value = (byte)bitReader.Read(6);
else
{
int scaleFactorTest = value + (delta - (expectedDelta >> 1));
if (scaleFactorTest < 0 || scaleFactorTest >= 64)
return;
//throw new InvalidDataException("Invalid scale factor.");
value = (byte)(value - (expectedDelta >> 1) + delta);
value = (byte)(value & 0x3F);
}
channel.ScaleFactors[i] = value;
}
}
else
{
for (int i = 0; i < SamplesPerSubframe; i++)
{
channel.ScaleFactors[i] = 0;
}
}
for (int i = 0; i < extraCount; i++)
{
channel.ScaleFactors[SamplesPerSubframe - 1 - i] = channel.ScaleFactors[csCount - i];
}
}
private static void UnpackIntensity(Channel channel, BitReader bitReader, int hfrGroupCount, int version)
{
if (channel.Type == ChannelType.StereoSecondary)
{
if (version <= Version200)
{
byte value = (byte)bitReader.Peek(4);
channel.Intensity[0] = value;
if (value < 15)
{
bitReader.Skip(4);
for (int i = 1; i < Subframes; i++)
{
channel.Intensity[i] = (byte)bitReader.Read(4);
}
}
}
else
{
byte value = (byte)bitReader.Peek(4);
if (value < 15)
{
bitReader.Skip(4);
byte deltaBits = (byte)bitReader.Read(2);
channel.Intensity[0] = value;
if (deltaBits == 3)
{
for (int i = 1; i < Subframes; i++)
{
channel.Intensity[i] = (byte)bitReader.Read(4);
}
}
else
{
byte bMax = (byte)((2 << deltaBits) - 1);
byte bits = (byte)(deltaBits + 1);
for (int i = 1; i < Subframes; i++)
{
byte delta = (byte)bitReader.Read(bits);
if (delta == bMax)
value = (byte)bitReader.Read(4);
else
{
value = (byte)(value - (bMax >> 1) + delta);
if (value > 15)
return;
//throw new InvalidDataException("Intensity value out of range.");
}
channel.Intensity[i] = value;
}
}
}
else
{
bitReader.Skip(4);
for (int i = 0; i < Subframes; i++)
{
channel.Intensity[i] = 7;
}
}
}
}
else
{
if (version <= Version200)
{
byte[] hfrScales = channel.ScaleFactors;
int hfrScalesOffset = 128 - hfrGroupCount;
for (int i = 0; i < hfrGroupCount; i++)
{
hfrScales[hfrScalesOffset + i] = (byte)bitReader.Read(6);
}
}
}
}
private static void CalculateResolution(Channel channel, int packedNoiseLevel, byte[] athCurve, int minResolution, int maxResolution)
{
int crCount = channel.CodedCount;
int noiseCount = 0;
int validCount = 0;
for (int i = 0; i < crCount; i++)
{
byte newResolution = 0;
byte scaleFactor = channel.ScaleFactors[i];
if (scaleFactor > 0)
{
int noiseLevel = athCurve[i] + ((packedNoiseLevel + i) >> 8);
int curvePosition = noiseLevel + 1 - ((5 * scaleFactor) >> 1);
if (curvePosition < 0)
newResolution = 15;
else if (curvePosition <= 65)
newResolution = InvertTable[curvePosition];
else
newResolution = 0;
if (newResolution > maxResolution)
newResolution = (byte)maxResolution;
else if (newResolution < minResolution)
newResolution = (byte)minResolution;
if (newResolution < 1)
{
channel.Noises[noiseCount] = (byte)i;
noiseCount++;
}
else
{
channel.Noises[SamplesPerSubframe - 1 - validCount] = (byte)i;
validCount++;
}
}
channel.Resolution[i] = newResolution;
}
channel.NoiseCount = noiseCount;
channel.ValidCount = validCount;
Array.Clear(channel.Resolution, crCount, SamplesPerSubframe - crCount);
}
private static void CalculateGain(Channel channel)
{
int cgCount = channel.CodedCount;
for (int i = 0; i < cgCount; i++)
{
float scaleFactorScale = DequantizerScalingTable[channel.ScaleFactors[i]];
float resolutionScale = DequantizerRangeTable[channel.Resolution[i]];
channel.Gain[i] = scaleFactorScale * resolutionScale;
}
}
private static void DequantizeCoefficients(Channel channel, BitReader bitReader, int subframe)
{
int ccCount = channel.CodedCount;
for (int i = 0; i < ccCount; i++)
{
float qc;
byte resolution = channel.Resolution[i];
byte bits = MaxBitTable[resolution];
int code = bitReader.Read(bits);
if (resolution > 7)
{
int signedCode = (1 - ((code & 1) << 1)) * (code >> 1);
if (signedCode == 0)
bitReader.Skip(-1);
qc = signedCode;
}
else
{
int index = (resolution << 4) + code;
int skip = ReadBitTable[index] - bits;
bitReader.Skip(skip);
qc = ReadValueTable[index];
}
channel.Spectra[subframe][i] = channel.Gain[i] * qc;
}
Array.Clear(channel.Spectra[subframe], ccCount, SamplesPerSubframe - ccCount);
}
private static void ReconstructNoise(Channel channel, int minResolution, int msStereo, ref int random, int subframe)
{
if (minResolution > 0) return;
if (channel.ValidCount <= 0 || channel.NoiseCount <= 0) return;
if (msStereo != 0 && channel.Type == ChannelType.StereoPrimary) return;
for (int i = 0; i < channel.NoiseCount; i++)
{
random = 0x343FD * random + 0x269EC3;
int randomIndex = SamplesPerSubframe - channel.ValidCount + (((random & 0x7FFF) * channel.ValidCount) >> 15);
int noiseIndex = channel.Noises[i];
int validIndex = channel.Noises[randomIndex];
int sfNoise = channel.ScaleFactors[noiseIndex];
int sfValid = channel.ScaleFactors[validIndex];
int scIndex = (sfNoise - sfValid + 62) & ~((sfNoise - sfValid + 62) >> 31);
channel.Spectra[subframe][noiseIndex] = ScaleConversionTable[scIndex] * channel.Spectra[subframe][validIndex];
}
}
private static void ReconstructHighFrequency(Channel channel, int hfrGroupCount, int bandsPerHfrGroup,
int stereoBandCount, int baseBandCount, int totalBandCount, int version, int subframe)
{
if (bandsPerHfrGroup == 0) return;
if (channel.Type == ChannelType.StereoSecondary) return;
int groupLimit;
int startBand = stereoBandCount + baseBandCount;
int highBand = startBand;
int lowBand = startBand - 1;
int hfrScalesOffset = 128 - hfrGroupCount;
byte[] hfrScales = channel.ScaleFactors;
if (version <= Version200)
groupLimit = hfrGroupCount;
else
{
groupLimit = hfrGroupCount >= 0 ? hfrGroupCount : hfrGroupCount + 1;
groupLimit >>= 1;
}
for (int group = 0; group < hfrGroupCount; group++)
{
int lowBandSub = group < groupLimit ? 1 : 0;
for (int i = 0; i < bandsPerHfrGroup; i++)
{
if (highBand >= totalBandCount || lowBand < 0) break;
int scIndex = hfrScales[hfrScalesOffset + group];
scIndex &= ~(scIndex >> 31);
channel.Spectra[subframe][highBand] = ScaleConversionTable[scIndex] * channel.Spectra[subframe][lowBand];
highBand++;
lowBand -= lowBandSub;
}
}
channel.Spectra[subframe][highBand - 1] = 0.0f;
}
private static void ApplyIntensityStereo(Channel[] channelPair, int channelOffset, int subframe, int baseBandCount, int totalBandCount)
{
if (channelPair[channelOffset + 0].Type != ChannelType.StereoPrimary) return;
float ratioL = IntensityRatioTable[channelPair[channelOffset + 1].Intensity[subframe]];
float ratioR = 2.0f - ratioL;
float[] spectraL = channelPair[channelOffset + 0].Spectra[subframe];
float[] spectraR = channelPair[channelOffset + 1].Spectra[subframe];
for (int band = baseBandCount; band < totalBandCount; band++)
{
float coefL = spectraL[band] * ratioL;
float coefR = spectraR[band] * ratioR;
spectraL[band] = coefL;
spectraR[band] = coefR;
}
}
private static void ApplyMsStereo(Channel[] channelPair, int channelOffset, int msStereo, int baseBandCount, int totalBandCount, int subframe)
{
if (msStereo != 0) return;
if (channelPair[channelOffset + 0].Type != ChannelType.StereoPrimary) return;
float ratio = MsStereoRatio;
float[] spectraL = channelPair[channelOffset + 0].Spectra[subframe];
float[] spectraR = channelPair[channelOffset + 1].Spectra[subframe];
for (int band = baseBandCount; band < totalBandCount; band++)
{
float coefL = (spectraL[band] + spectraR[band]) * ratio;
float coefR = (spectraL[band] - spectraR[band]) * ratio;
spectraL[band] = coefL;
spectraR[band] = coefR;
}
}
private static void ImdctTransform(Channel channel, int subframe)
{
const int size = SamplesPerSubframe;
const int half = SamplesPerSubframe / 2;
const int mdctBits = MdctBits;
int count1 = 1;
int count2 = half;
float[] temp1 = channel.Spectra[subframe];
float[] temp2 = channel.Temp;
int temp1Index = 0;
for (int i = 0; i < mdctBits; i++)
{
float[] swap;
float[] d = temp2;
int d1Index = 0;
int d2Index = count2;
for (int j = 0; j < count1; j++)
{
for (int k = 0; k < count2; k++)
{
float a = temp1[temp1Index++];
float b = temp1[temp1Index++];
d[d1Index++] = a + b;
d[d2Index++] = a - b;
}
d1Index += count2;
d2Index += count2;
}
temp1Index -= SamplesPerSubframe;
swap = temp1;
temp1 = temp2;
temp2 = swap;
count1 <<= 1;
count2 >>= 1;
}
count1 = half;
count2 = 1;
temp1 = channel.Temp;
temp2 = channel.Spectra[subframe];
for (int i = 0; i < mdctBits; i++)
{
int sinTableIndex = 0;
int cosTableIndex = 0;
float[] swap;
float[] d = temp2;
float[] s = temp1;
int d1Index = 0;
int d2Index = count2 * 2 - 1;
int s1Index = 0;
int s2Index = count2;
for (int j = 0; j < count1; j++)
{
for (int k = 0; k < count2; k++)
{
float a = s[s1Index++];
float b = s[s2Index++];
float sin = SinTable[i][sinTableIndex++];
float cos = CosTable[i][cosTableIndex++];
d[d1Index++] = a * sin - b * cos;
d[d2Index--] = a * cos + b * sin;
}
s1Index += count2;
s2Index += count2;
d1Index += count2;
d2Index += count2 * 3;
}
swap = temp1;
temp1 = temp2;
temp2 = swap;
count1 >>= 1;
count2 <<= 1;
}
float[] dct = channel.Spectra[subframe];
float[] prev = channel.ImdctPrevious;
for (int i = 0; i < half; i++)
{
channel.Wave[subframe][i] = ImdctWindow[i] * dct[i + half] + prev[i];
channel.Wave[subframe][i + half] = ImdctWindow[i + half] * dct[size - 1 - i] - prev[i + half];
channel.ImdctPrevious[i] = ImdctWindow[size - 1 - i] * dct[half - i - 1];
channel.ImdctPrevious[i + half] = ImdctWindow[half - i - 1] * dct[i];
}
}
}
}