Exemple #1
0
        private static ChannelType[] GetChannelTypes(HcaInfo hca)
        {
            int channelsPerTrack = hca.ChannelCount / hca.TrackCount;

            if (hca.StereoBandCount == 0 || channelsPerTrack == 1)
            {
                return(new ChannelType[8]);
            }

            switch (channelsPerTrack)
            {
            case 2: return(new[] { StereoPrimary, StereoSecondary });

            case 3: return(new[] { StereoPrimary, StereoSecondary, Discrete });

            case 4 when hca.ChannelConfig != 0: return(new[] { StereoPrimary, StereoSecondary, Discrete, Discrete });

            case 4 when hca.ChannelConfig == 0: return(new[] { StereoPrimary, StereoSecondary, StereoPrimary, StereoSecondary });

            case 5 when hca.ChannelConfig > 2: return(new[] { StereoPrimary, StereoSecondary, Discrete, Discrete, Discrete });

            case 5 when hca.ChannelConfig <= 2: return(new[] { StereoPrimary, StereoSecondary, Discrete, StereoPrimary, StereoSecondary });

            case 6: return(new[] { StereoPrimary, StereoSecondary, Discrete, Discrete, StereoPrimary, StereoSecondary });

            case 7: return(new[] { StereoPrimary, StereoSecondary, Discrete, Discrete, StereoPrimary, StereoSecondary, Discrete });

            case 8: return(new[] { StereoPrimary, StereoSecondary, Discrete, Discrete, StereoPrimary, StereoSecondary, StereoPrimary, StereoSecondary });

            default: return(new ChannelType[channelsPerTrack]);
            }
        }
 public static void Crypt(HcaInfo hca, byte[][] audio, CriHcaKey key, bool doDecrypt)
 {
     for (int frame = 0; frame < hca.FrameCount; frame++)
     {
         CryptFrame(hca, audio[frame], key, doDecrypt);
     }
 }
Exemple #3
0
 public static void DecryptFrame(HcaInfo hca, byte[] audio, CriHcaKey key)
 {
     for (int b = 0; b < hca.FrameSize; b++)
     {
         audio[b] = key.DecryptionTable[audio[b]];
     }
 }
Exemple #4
0
        /// <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;
        }
        public static void CryptFrame(HcaInfo hca, byte[] audio, CriHcaKey key, bool doDecrypt)
        {
            byte[] substitutionTable = doDecrypt ? key.DecryptionTable : key.EncryptionTable;

            for (int b = 0; b < hca.FrameSize - 2; b++)
            {
                audio[b] = substitutionTable[audio[b]];
            }

            ushort crc = Crc.Compute(audio, hca.FrameSize - 2);

            audio[hca.FrameSize - 2] = (byte)(crc >> 8);
            audio[hca.FrameSize - 1] = (byte)crc;
        }
        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);
        }
Exemple #7
0
        private static void SetChannelConfiguration(HcaInfo hca, int channelConfig = -1)
        {
            int channelsPerTrack = hca.ChannelCount / hca.TrackCount;

            if (channelConfig == -1)
            {
                channelConfig = CriHcaTables.DefaultChannelMapping[channelsPerTrack];
            }

            if (CriHcaTables.ValidChannelMappings[channelsPerTrack - 1][channelConfig] != 1)
            {
                throw new ArgumentOutOfRangeException(nameof(channelConfig), "Channel mapping is not valid.");
            }

            hca.ChannelConfig = channelConfig;
        }
Exemple #8
0
        private static void CalculateLoopInfo(HcaInfo hca, int loopStart, int loopEnd)
        {
            loopStart += hca.InsertedSamples;
            loopEnd   += hca.InsertedSamples;

            hca.LoopStartFrame  = loopStart / SamplesPerFrame;
            hca.PreLoopSamples  = loopStart % SamplesPerFrame;
            hca.LoopEndFrame    = loopEnd / SamplesPerFrame;
            hca.PostLoopSamples = SamplesPerFrame - loopEnd % SamplesPerFrame;

            if (hca.PostLoopSamples == SamplesPerFrame)
            {
                hca.LoopEndFrame--;
                hca.PostLoopSamples = 0;
            }
        }
Exemple #9
0
        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]);
                }
            }
        }
Exemple #10
0
        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);
        }
Exemple #11
0
        private static void CopyPcmToOutput(short[][] pcmIn, short[][] pcmOut, HcaInfo hca, int frame)
        {
            int currentSample    = frame * SamplesPerFrame - hca.InsertedSamples;
            int remainingSamples = Math.Min(hca.SampleCount - currentSample, hca.SampleCount);
            int srcStart         = Helpers.Clamp(0 - currentSample, 0, SamplesPerFrame);
            int destStart        = Math.Max(currentSample, 0);

            int length = Math.Min(SamplesPerFrame - srcStart, remainingSamples);

            if (length <= 0)
            {
                return;
            }

            for (int c = 0; c < pcmOut.Length; c++)
            {
                Array.Copy(pcmIn[c], srcStart, pcmOut[c], destStart, length);
            }
        }
Exemple #12
0
        public CriHcaFrame(HcaInfo hca)
        {
            Hca = hca;
            ChannelType[] channelTypes = GetChannelTypes(hca);
            Channels = new CriHcaChannel[hca.ChannelCount];

            for (int i = 0; i < Channels.Length; i++)
            {
                Channels[i] = new CriHcaChannel
                {
                    Type = channelTypes[i],
                    CodedScaleFactorCount = channelTypes[i] == StereoSecondary
                        ? hca.BaseBandCount
                        : hca.BaseBandCount + hca.StereoBandCount
                };
            }

            AthCurve = hca.UseAthCurve ? ScaleAthCurve(hca.SampleRate) : new byte[SamplesPerSubFrame];
        }
Exemple #13
0
        private static void CalculateHeaderSize(HcaInfo hca)
        {
            const int baseHeaderSize      = 96;
            const int baseHeaderAlignment = 32;
            const int loopFrameAlignment  = 2048;

            hca.HeaderSize = GetNextMultiple(baseHeaderSize + hca.CommentLength, baseHeaderAlignment);
            if (hca.Looping)
            {
                int loopFrameOffset = hca.HeaderSize + hca.FrameSize * hca.LoopStartFrame;
                int paddingBytes    = GetNextMultiple(loopFrameOffset, loopFrameAlignment) - loopFrameOffset;
                int paddingFrames   = paddingBytes / hca.FrameSize;

                hca.InsertedSamples += paddingFrames * SamplesPerFrame;
                hca.LoopStartFrame  += paddingFrames;
                hca.LoopEndFrame    += paddingFrames;
                hca.HeaderSize      += paddingBytes % hca.FrameSize;
            }
        }
Exemple #14
0
        private static void CalculateBandCounts(HcaInfo hca, int bitrate, int cutoffFreq)
        {
            hca.FrameSize = bitrate * 1024 / hca.SampleRate / 8;
            int numGroups  = 0;
            int pcmBitrate = hca.SampleRate * hca.ChannelCount * 16;
            int hfrRatio;    // HFR is used at bitrates below (pcmBitrate / hfrRatio)
            int cutoffRatio; // The cutoff frequency is lowered at bitrates below (pcmBitrate / cutoffRatio)

            if (hca.ChannelCount <= 1 || pcmBitrate / bitrate <= 6)
            {
                hfrRatio    = 6;
                cutoffRatio = 12;
            }
            else
            {
                hfrRatio    = 8;
                cutoffRatio = 16;
            }

            if (bitrate < pcmBitrate / cutoffRatio)
            {
                cutoffFreq = Math.Min(cutoffFreq, cutoffRatio * bitrate / (32 * hca.ChannelCount));
            }

            int totalBandCount = (int)Math.Round(cutoffFreq * 256.0 / hca.SampleRate);

            int hfrStartBand    = (int)Math.Min(totalBandCount, Math.Round((hfrRatio * bitrate * 128.0) / pcmBitrate));
            int stereoStartBand = hfrRatio == 6 ? hfrStartBand : (hfrStartBand + 1) / 2;

            int hfrBandCount  = totalBandCount - hfrStartBand;
            int bandsPerGroup = hfrBandCount.DivideByRoundUp(8);

            if (bandsPerGroup > 0)
            {
                numGroups = hfrBandCount.DivideByRoundUp(bandsPerGroup);
            }

            hca.TotalBandCount   = totalBandCount;
            hca.BaseBandCount    = stereoStartBand;
            hca.StereoBandCount  = hfrStartBand - stereoStartBand;
            hca.HfrGroupCount    = numGroups;
            hca.BandsPerHfrGroup = bandsPerGroup;
        }
Exemple #15
0
        private int CalculateBitrate(HcaInfo hca, CriHcaQuality quality, int bitrate, bool limitBitrate)
        {
            int pcmBitrate = Hca.SampleRate * Hca.ChannelCount * 16;
            int maxBitrate = pcmBitrate / 4;
            int minBitrate = 0;

            int compressionRatio = 6;

            switch (quality)
            {
            case CriHcaQuality.Highest:
                compressionRatio = 4;
                break;

            case CriHcaQuality.High:
                compressionRatio = 6;
                break;

            case CriHcaQuality.Middle:
                compressionRatio = 8;
                break;

            case CriHcaQuality.Low:
                compressionRatio = hca.ChannelCount == 1 ? 10 : 12;
                break;

            case CriHcaQuality.Lowest:
                compressionRatio = hca.ChannelCount == 1 ? 12 : 16;
                break;
            }

            bitrate = bitrate != 0 ? bitrate : pcmBitrate / compressionRatio;

            if (limitBitrate)
            {
                minBitrate = Math.Min(
                    hca.ChannelCount == 1 ? 42666 : 32000 * hca.ChannelCount,
                    pcmBitrate / 6);
            }

            return(Clamp(bitrate, minBitrate, maxBitrate));
        }
Exemple #16
0
        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];
                        }
                    }
                }
            }
        }
Exemple #17
0
        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;
                }
            }
        }