private static void ReadSpectralCoefficients(CriHcaFrame frame, BitReader reader) { for (int sf = 0; sf < SubframesPerFrame; sf++) { foreach (CriHcaChannel channel in frame.Channels) { for (int s = 0; s < channel.CodedScaleFactorCount; s++) { int resolution = channel.Resolution[s]; int bits = CriHcaTables.QuantizedSpectrumMaxBits[resolution]; int code = reader.PeekInt(bits); if (resolution < 8) { bits = CriHcaTables.QuantizedSpectrumBits[resolution][code]; channel.QuantizedSpectra[sf][s] = CriHcaTables.QuantizedSpectrumValue[resolution][code]; } else { // Read the sign-magnitude value. The low bit is the sign int quantizedCoefficient = code / 2 * (1 - (code % 2 * 2)); if (quantizedCoefficient == 0) { bits--; } channel.QuantizedSpectra[sf][s] = quantizedCoefficient; } reader.Position += bits; } Array.Clear(channel.Spectra[sf], channel.CodedScaleFactorCount, 0x80 - channel.CodedScaleFactorCount); } } }
private static void CalculateNoiseLevel(CriHcaFrame frame) { int highestBand = frame.Hca.BaseBandCount + frame.Hca.StereoBandCount - 1; int availableBits = frame.Hca.FrameSize * 8; int maxLevel = 255; int minLevel = 0; int level = BinarySearchLevel(frame.Channels, availableBits, minLevel, maxLevel); // If there aren't enough available bits, remove bands until there are. while (level < 0) { highestBand -= 2; if (highestBand < 0) { throw new InvalidDataException("Bitrate is set too low."); } foreach (CriHcaChannel channel in frame.Channels) { channel.ScaleFactors[highestBand + 1] = 0; channel.ScaleFactors[highestBand + 2] = 0; } CalculateFrameHeaderLength(frame); level = BinarySearchLevel(frame.Channels, availableBits, minLevel, maxLevel); } frame.AcceptableNoiseLevel = level; }
private static void ApplyIntensityStereo(CriHcaFrame frame) { if (frame.Hca.StereoBandCount <= 0) { return; } for (int c = 0; c < frame.Channels.Length; c++) { if (frame.Channels[c].Type != ChannelType.StereoPrimary) { continue; } for (int sf = 0; sf < SubframesPerFrame; sf++) { double[] l = frame.Channels[c].Spectra[sf]; double[] r = frame.Channels[c + 1].Spectra[sf]; double ratioL = IntensityRatioTable[frame.Channels[c + 1].Intensity[sf]]; double ratioR = ratioL - 2.0; for (int b = frame.Hca.BaseBandCount; b < frame.Hca.TotalBandCount; b++) { r[b] = l[b] * ratioR; l[b] *= ratioL; } } } }
private static void EncodeIntensityStereo(CriHcaFrame frame) { if (frame.Hca.StereoBandCount <= 0) { return; } for (int c = 0; c < frame.Channels.Length; c++) { if (frame.Channels[c].Type != ChannelType.StereoPrimary) { continue; } for (int sf = 0; sf < SubframesPerFrame; sf++) { double[] l = frame.Channels[c].Spectra[sf]; double[] r = frame.Channels[c + 1].Spectra[sf]; double energyL = 0; double energyR = 0; double energyTotal = 0; for (int b = frame.Hca.BaseBandCount; b < frame.Hca.TotalBandCount; b++) { energyL += Math.Abs(l[b]); energyR += Math.Abs(r[b]); energyTotal += Math.Abs(l[b] + r[b]); } energyTotal *= 2; double energyLR = energyR + energyL; double storedValue = 2 * energyL / energyLR; double energyRatio = energyLR / energyTotal; energyRatio = Clamp(energyRatio, 0.5, Math.Sqrt(2) / 2); int quantized = 1; if (energyR > 0 || energyL > 0) { while (quantized < 13 && CriHcaTables.IntensityRatioBoundsTable[quantized] >= storedValue) { quantized++; } } else { quantized = 0; energyRatio = 1; } frame.Channels[c + 1].Intensity[sf] = quantized; for (int b = frame.Hca.BaseBandCount; b < frame.Hca.TotalBandCount; b++) { l[b] = (l[b] + r[b]) * energyRatio; r[b] = 0; } } } }
private static bool UnpackingWasSuccessful(CriHcaFrame frame, BitReader reader) { // 128 leftover bits after unpacking should be high enough to get rid of false negatives, // and low enough that false positives will be uncommon. return(reader.Remaining >= 16 && reader.Remaining <= 128 || FrameEmpty(frame) || frame.AcceptableNoiseLevel == 0 && reader.Remaining >= 16); }
public static bool UnpackFrame(CriHcaFrame frame, BitReader reader) { if (!UnpackFrameHeader(frame, reader)) { return(false); } ReadSpectralCoefficients(frame, reader); return(UnpackingWasSuccessful(frame, reader)); }
private static void DecodeFrame(byte[] audio, CriHcaFrame frame, short[][] pcmOut) { var reader = new BitReader(audio); UnpackFrame(frame, reader); DequantizeFrame(frame); RestoreMissingBands(frame); RunImdct(frame); PcmFloatToShort(frame, pcmOut); }
private static void RunImdct(CriHcaFrame frame) { for (int sf = 0; sf < SubframesPerFrame; sf++) { foreach (CriHcaChannel channel in frame.Channels) { channel.Mdct.RunImdct(channel.Spectra[sf], channel.PcmFloat[sf]); } } }
/// <summary> /// Initializes this <see cref="CriHcaEncoder"/>. Any preexisting state is reset, and the encoder /// will be ready to accept PCM audio via <see cref="Encode"/>. /// </summary> /// <param name="config">The configuration to be used when creating the HCA file.</param> public void Initialize(CriHcaParameters config) { if (config.ChannelCount > 8) { throw new ArgumentOutOfRangeException(nameof(config.ChannelCount), "HCA channel count must be 8 or below"); } CutoffFrequency = config.SampleRate / 2; Quality = config.Quality; PostSamples = 128; Hca = new HcaInfo { ChannelCount = config.ChannelCount, TrackCount = 1, SampleCount = config.SampleCount, SampleRate = config.SampleRate, MinResolution = 1, MaxResolution = 15, InsertedSamples = SamplesPerSubFrame }; Bitrate = CalculateBitrate(Hca, Quality, config.Bitrate, config.LimitBitrate); CalculateBandCounts(Hca, Bitrate, CutoffFrequency); Hca.CalculateHfrValues(); SetChannelConfiguration(Hca); int inputSampleCount = Hca.SampleCount; if (config.Looping) { Hca.Looping = true; Hca.SampleCount = Math.Min(config.LoopEnd, config.SampleCount); Hca.InsertedSamples += GetNextMultiple(config.LoopStart, SamplesPerFrame) - config.LoopStart; CalculateLoopInfo(Hca, config.LoopStart, config.LoopEnd); inputSampleCount = Math.Min(GetNextMultiple(Hca.SampleCount, SamplesPerSubFrame), config.SampleCount); inputSampleCount += SamplesPerSubFrame * 2; PostSamples = inputSampleCount - Hca.SampleCount; } CalculateHeaderSize(Hca); int totalSamples = inputSampleCount + Hca.InsertedSamples; Hca.FrameCount = totalSamples.DivideByRoundUp(SamplesPerFrame); Hca.AppendedSamples = Hca.FrameCount * SamplesPerFrame - Hca.InsertedSamples - inputSampleCount; Frame = new CriHcaFrame(Hca); Channels = Frame.Channels; PcmBuffer = CreateJaggedArray <short[][]>(Hca.ChannelCount, SamplesPerFrame); PostAudio = CreateJaggedArray <short[][]>(Hca.ChannelCount, PostSamples); HcaOutputBuffer = new Queue <byte[]>(); BufferPreSamples = Hca.InsertedSamples - 128; }
private static void PcmFloatToShort(CriHcaFrame frame, short[][] pcm) { for (int c = 0; c < frame.Channels.Length; c++) { for (int sf = 0; sf < SubframesPerFrame; sf++) { for (int s = 0; s < SamplesPerSubFrame; s++) { int sample = (int)(frame.Channels[c].PcmFloat[sf][s] * (short.MaxValue + 1)); pcm[c][sf * SamplesPerSubFrame + s] = Helpers.Clamp16(sample); } } } }
public static CriHcaKey FindKey(HcaInfo hca, byte[][] audio) { var frame = new CriHcaFrame(hca); var buffer = new byte[hca.FrameSize]; foreach (CriHcaKey key in Keys) { if (TestKey(frame, audio, key, buffer)) { return(key); } } return(null); }
private static void CalculateFrameHeaderLength(CriHcaFrame frame) { foreach (CriHcaChannel channel in frame.Channels) { CalculateOptimalDeltaLength(channel); if (channel.Type == ChannelType.StereoSecondary) { channel.HeaderLengthBits += 32; } else if (frame.Hca.HfrGroupCount > 0) { channel.HeaderLengthBits += 6 * frame.Hca.HfrGroupCount; } } }
private static void CalculateEvaluationBoundary(CriHcaFrame frame) { if (frame.AcceptableNoiseLevel == 0) { frame.EvaluationBoundary = 0; return; } int availableBits = frame.Hca.FrameSize * 8; int maxLevel = 127; int minLevel = 0; int level = BinarySearchBoundary(frame.Channels, availableBits, frame.AcceptableNoiseLevel, minLevel, maxLevel); frame.EvaluationBoundary = level >= 0 ? level : throw new NotImplementedException(); }
private static void CalculateFrameResolutions(CriHcaFrame frame) { foreach (CriHcaChannel channel in frame.Channels) { for (int i = 0; i < frame.EvaluationBoundary; i++) { channel.Resolution[i] = CalculateResolution(channel.ScaleFactors[i], frame.AcceptableNoiseLevel - 1); } for (int i = frame.EvaluationBoundary; i < channel.CodedScaleFactorCount; i++) { channel.Resolution[i] = CalculateResolution(channel.ScaleFactors[i], frame.AcceptableNoiseLevel); } Array.Clear(channel.Resolution, channel.CodedScaleFactorCount, channel.Resolution.Length - channel.CodedScaleFactorCount); } }
private static bool TestKey(CriHcaFrame frame, byte[][] audio, CriHcaKey key, byte[] buffer) { int startFrame = FindFirstNonEmptyFrame(audio); int endFrame = Math.Min(audio.Length, startFrame + FramesToTest); for (int i = startFrame; i < endFrame; i++) { Array.Copy(audio[i], buffer, audio[i].Length); CryptFrame(frame.Hca, buffer, key, true); var reader = new BitReader(buffer); if (!CriHcaPacking.UnpackFrame(frame, reader)) { return(false); } } return(true); }
private static bool FrameEmpty(CriHcaFrame frame) { if (frame.AcceptableNoiseLevel > 0) { return(false); } // If all the scale factors are 0, the frame is empty foreach (CriHcaChannel channel in frame.Channels) { if (channel.ScaleFactorDeltaBits > 0) { return(false); } } return(true); }
private static void CalculateHfrScale(CriHcaFrame frame) { HcaInfo hca = frame.Hca; if (hca.HfrGroupCount == 0) { return; } int hfrStartBand = hca.StereoBandCount + hca.BaseBandCount; int hfrBandCount = Math.Min(hca.HfrBandCount, hca.TotalBandCount - hca.HfrBandCount); foreach (CriHcaChannel channel in frame.Channels) { if (channel.Type == ChannelType.StereoSecondary) { continue; } double[] groupSpectra = channel.HfrGroupAverageSpectra; for (int group = 0, band = 0; group < hca.HfrGroupCount; group++) { double sum = 0.0; int count = 0; for (int i = 0; i < hca.BandsPerHfrGroup && band < hfrBandCount; band++, i++) { for (int subframe = 0; subframe < SubframesPerFrame; subframe++) { sum += Math.Abs(channel.ScaledSpectra[hfrStartBand - band - 1][subframe]); } count += SubframesPerFrame; } double averageSpectra = sum / count; if (averageSpectra > 0.0) { groupSpectra[group] *= Math.Min(1.0 / averageSpectra, Math.Sqrt(2)); } channel.HfrScales[group] = FindScaleFactor(groupSpectra[group]); } } }
private static void DequantizeFrame(CriHcaFrame frame) { foreach (CriHcaChannel channel in frame.Channels) { CalculateGain(channel); } for (int sf = 0; sf < SubframesPerFrame; sf++) { foreach (CriHcaChannel channel in frame.Channels) { for (int s = 0; s < channel.CodedScaleFactorCount; s++) { channel.Spectra[sf][s] = channel.QuantizedSpectra[sf][s] * channel.Gain[s]; } } } }
public static short[][] Decode(HcaInfo hca, byte[][] audio, CriHcaParameters config = null) { config?.Progress?.SetTotal(hca.FrameCount); var pcmOut = Helpers.CreateJaggedArray <short[][]>(hca.ChannelCount, hca.SampleCount); var pcmBuffer = Helpers.CreateJaggedArray <short[][]>(hca.ChannelCount, SamplesPerFrame); var frame = new CriHcaFrame(hca); for (int i = 0; i < hca.FrameCount; i++) { DecodeFrame(audio[i], frame, pcmBuffer); CopyPcmToOutput(pcmBuffer, pcmOut, hca, i); //CopyBuffer(pcmBuffer, pcmOut, hca.InsertedSamples, i); config?.Progress?.ReportAdd(1); } return(pcmOut); }
public static void PackFrame(CriHcaFrame frame, Crc16 crc, byte[] outBuffer) { var writer = new BitWriter(outBuffer); writer.Write(0xffff, 16); writer.Write(frame.AcceptableNoiseLevel, 9); writer.Write(frame.EvaluationBoundary, 7); foreach (CriHcaChannel channel in frame.Channels) { WriteScaleFactors(writer, channel); if (channel.Type == ChannelType.StereoSecondary) { for (int i = 0; i < SubframesPerFrame; i++) { writer.Write(channel.Intensity[i], 4); } } else if (frame.Hca.HfrGroupCount > 0) { for (int i = 0; i < frame.Hca.HfrGroupCount; i++) { writer.Write(channel.HfrScales[i], 6); } } } for (int sf = 0; sf < SubframesPerFrame; sf++) { foreach (CriHcaChannel channel in frame.Channels) { WriteSpectra(writer, channel, sf); } } writer.AlignPosition(8); for (int i = writer.Position / 8; i < frame.Hca.FrameSize - 2; i++) { writer.Buffer[i] = 0; } WriteChecksum(writer, crc, outBuffer); }
private static bool UnpackFrameHeader(CriHcaFrame frame, BitReader reader) { int syncWord = reader.ReadInt(16); if (syncWord != 0xffff) { throw new InvalidDataException("Invalid frame header"); } byte[] athCurve = frame.AthCurve; frame.AcceptableNoiseLevel = reader.ReadInt(9); frame.EvaluationBoundary = reader.ReadInt(7); foreach (CriHcaChannel channel in frame.Channels) { if (!ReadScaleFactors(channel, reader)) { return(false); } for (int i = 0; i < frame.EvaluationBoundary; i++) { channel.Resolution[i] = CalculateResolution(channel.ScaleFactors[i], athCurve[i] + frame.AcceptableNoiseLevel - 1); } for (int i = frame.EvaluationBoundary; i < channel.CodedScaleFactorCount; i++) { channel.Resolution[i] = CalculateResolution(channel.ScaleFactors[i], athCurve[i] + frame.AcceptableNoiseLevel); } if (channel.Type == ChannelType.StereoSecondary) { ReadIntensity(reader, channel.Intensity); } else if (frame.Hca.HfrGroupCount > 0) { ReadHfrScaleFactors(reader, frame.Hca.HfrGroupCount, channel.HfrScales); } } return(true); }
private static void ReconstructHighFrequency(CriHcaFrame frame) { HcaInfo hca = frame.Hca; if (hca.HfrGroupCount == 0) { return; } // The last spectral coefficient should always be 0; int totalBandCount = Math.Min(hca.TotalBandCount, 127); int hfrStartBand = hca.BaseBandCount + hca.StereoBandCount; int hfrBandCount = Math.Min(hca.HfrBandCount, totalBandCount - hca.HfrBandCount); foreach (CriHcaChannel channel in frame.Channels) { if (channel.Type == ChannelType.StereoSecondary) { continue; } for (int group = 0, band = 0; group < hca.HfrGroupCount; group++) { for (int i = 0; i < hca.BandsPerHfrGroup && band < hfrBandCount; band++, i++) { int highBand = hfrStartBand + band; int lowBand = hfrStartBand - band - 1; int index = channel.HfrScales[group] - channel.ScaleFactors[lowBand] + 64; for (int sf = 0; sf < SubframesPerFrame; sf++) { channel.Spectra[sf][highBand] = ScaleConversionTable[index] * channel.Spectra[sf][lowBand]; } } } } }
private static void CalculateHfrGroupAverages(CriHcaFrame frame) { HcaInfo hca = frame.Hca; if (hca.HfrGroupCount == 0) { return; } int hfrStartBand = hca.StereoBandCount + hca.BaseBandCount; foreach (CriHcaChannel channel in frame.Channels) { if (channel.Type == ChannelType.StereoSecondary) { continue; } for (int group = 0, band = hfrStartBand; group < hca.HfrGroupCount; group++) { double sum = 0.0; int count = 0; for (int i = 0; i < hca.BandsPerHfrGroup && band < SamplesPerSubFrame; band++, i++) { for (int subframe = 0; subframe < SubframesPerFrame; subframe++) { sum += Math.Abs(channel.Spectra[subframe][band]); } count += SubframesPerFrame; } channel.HfrGroupAverageSpectra[group] = sum / count; } } }
private static void RestoreMissingBands(CriHcaFrame frame) { ReconstructHighFrequency(frame); ApplyIntensityStereo(frame); }